Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: introduce common supertype ProblemEmitter #94

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
private val methodHandlesInitializer = MethodHandlesInitializer(this)
private val methodHandleTransformer = MethodHandleTransformer(this)
private val lookupHelper = LookupHelper(this)
private val methodTypeHelper = MethodTypeHelper(this)

fun doTraversal() {
ssaConstruction.traverse(::onRead, ::onWrite)
Expand All @@ -40,7 +41,7 @@
private fun onRead(instruction: ReadVariableInstruction, index: Int, block: Block) {
if (isUnrelated(instruction.variable)) return
val value = ssaConstruction.readVariable(instruction.variable, block)
if (value is Holder) {

Check notice on line 44 in src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Cascade 'if' can be replaced with 'when'

Cascade 'if' should be replaced with 'when'
typeData[controlFlow.getElement(index)] = value.value
} else if (value is Phi) {
val type = value.blockToValue.values
Expand Down Expand Up @@ -106,59 +107,59 @@
"methodType" -> {
if (arguments.isEmpty()) return noMatch()
if (arguments.size == 2 && arguments[1].type == methodTypeType(expression)) {
MethodTypeHelper.methodType(
methodTypeHelper.methodType(
arguments[0],
arguments[1].mhType(block) ?: notConstant()
)
} else {
MethodTypeHelper.methodType(arguments)
methodTypeHelper.methodType(arguments)
}
}

"unwrap" -> MethodTypeHelper.unwrap(qualifier?.mhType(block) ?: return noMatch())
"wrap" -> MethodTypeHelper.wrap(expression, qualifier?.mhType(block) ?: return noMatch())
"unwrap" -> methodTypeHelper.unwrap(qualifier?.mhType(block) ?: return noMatch())
"wrap" -> methodTypeHelper.wrap(expression, qualifier?.mhType(block) ?: return noMatch())
"dropParameterTypes" -> {
val mhType = qualifier?.mhType(block) ?: return noMatch()
if (arguments.size != 2) return noMatch()
val (start, end) = arguments
MethodTypeHelper.dropParameterTypes(mhType, start, end)
methodTypeHelper.dropParameterTypes(mhType, start, end)
}

"insertParameterTypes" -> {
val mhType = qualifier?.mhType(block) ?: notConstant()
if (arguments.isEmpty()) return noMatch()
MethodTypeHelper.insertParameterTypes(mhType, arguments[0], arguments.drop(1))
methodTypeHelper.insertParameterTypes(mhType, arguments[0], arguments.drop(1))
}

"changeParameterType" -> {
val mhType = qualifier?.mhType(block) ?: notConstant()
if (arguments.size != 2) return noMatch()
val (num, type) = arguments
MethodTypeHelper.changeParameterType(mhType, num, type)
methodTypeHelper.changeParameterType(mhType, num, type)
}

"changeReturnType" -> {
val mhType = qualifier?.mhType(block) ?: notConstant()
if (arguments.size != 1) return noMatch()
val type = arguments[0]
MethodTypeHelper.changeReturnType(mhType, type)
methodTypeHelper.changeReturnType(mhType, type)
}

"appendParameterTypes" ->
MethodTypeHelper.appendParameterTypes(qualifier?.mhType(block) ?: return noMatch(), arguments)
methodTypeHelper.appendParameterTypes(qualifier?.mhType(block) ?: return noMatch(), arguments)

"erase" ->
MethodTypeHelper.erase(qualifier?.mhType(block) ?: return noMatch(), expression)
methodTypeHelper.erase(qualifier?.mhType(block) ?: return noMatch(), expression)

"generic" ->
MethodTypeHelper.generic(qualifier?.mhType(block) ?: return noMatch(), objectType(expression))
methodTypeHelper.generic(qualifier?.mhType(block) ?: return noMatch(), objectType(expression))

"genericMethodType" -> {
val size = arguments.size
if (size != 1 && arguments.size != 2) return noMatch()
val finalArray =
if (size == 1) false else arguments[1].getConstantOfType<Boolean>() ?: return notConstant()
MethodTypeHelper.genericMethodType(arguments[0], finalArray, objectType(expression))
methodTypeHelper.genericMethodType(arguments[0], finalArray, objectType(expression))
}

"fromMethodDescriptorString" -> notConstant() // not supported
Expand Down Expand Up @@ -383,7 +384,7 @@

"filterReturnValue" -> {
if (arguments.size != 1) return noMatch()
methodHandlesMerger.filterReturnValue(arguments[0], arguments[1], block)

Check warning on line 387 in src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Constant conditions

Index is always out of bounds
}

"foldArguments" -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package de.sirywell.handlehints.inspection

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiTypes
import de.sirywell.handlehints.MethodHandleBundle.message
import de.sirywell.handlehints.TypeData
import de.sirywell.handlehints.type.MethodHandleType
import de.sirywell.handlehints.type.TopSignature
import de.sirywell.handlehints.type.Type
import org.jetbrains.annotations.Nls

abstract class ProblemEmitter(private val typeData: TypeData) {

protected fun emitProblem(element: PsiElement, message: @Nls String): MethodHandleType {
typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return MethodHandleType(TopSignature)
}

protected fun emitMustNotBeVoid(typeExpr: PsiExpression) {
emitProblem(
typeExpr,
message("problem.merging.general.typeMustNotBe", PsiTypes.voidType().presentableText)
)
}

protected fun emitMustBeReferenceType(refc: PsiExpression, referenceClass: Type) {
emitProblem(
refc, message(
"problem.merging.general.referenceTypeExpectedReturn",
referenceClass,
)
)
}

protected fun emitMustBeArrayType(refc: PsiExpression, referenceClass: Type) {
emitProblem(
refc, message(
"problem.merging.general.arrayTypeExpected",
referenceClass,
)
)
}

protected fun emitIncompatibleReturnTypes(
element: PsiElement,
first: Type,
second: Type
): MethodHandleType {
return emitProblem(
element, message(
"problem.merging.general.incompatibleReturnType",
first,
second
)
)
}

protected fun emitOutOfBounds(
size: Int?,
targetExpr: PsiExpression,
pos: Int,
exclusive: Boolean
) = if (size != null) {
if (exclusive) {
emitProblem(targetExpr, message("problem.general.position.invalidIndexKnownBoundsExcl", pos, size))
} else {
emitProblem(targetExpr, message("problem.general.position.invalidIndexKnownBoundsIncl", pos, size))
}
} else {
emitProblem(targetExpr, message("problem.general.position.invalidIndex", pos))
}
}
34 changes: 6 additions & 28 deletions src/main/kotlin/de/sirywell/handlehints/mhtype/LookupHelper.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package de.sirywell.handlehints.mhtype

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.*
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiTypes
import com.intellij.psi.util.PsiTypesUtil
import de.sirywell.handlehints.*
import de.sirywell.handlehints.dfa.SsaAnalyzer
import de.sirywell.handlehints.dfa.SsaConstruction
import de.sirywell.handlehints.inspection.ProblemEmitter
import de.sirywell.handlehints.type.*
import de.sirywell.handlehints.type.TopType
import org.jetbrains.annotations.Nls

class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) {
class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.typeData) {

// MethodHandle factory methods

Expand Down Expand Up @@ -104,29 +105,6 @@ class LookupHelper(private val ssaAnalyzer: SsaAnalyzer) {
return MethodHandleType(mhType.signature.withParameterTypes(pt).withVarargs(mhType.signature.varargs))
}

private fun emitProblem(element: PsiElement, message: @Nls String): MethodHandleType {
ssaAnalyzer.typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return MethodHandleType(TopSignature)
}

private fun emitMustNotBeVoid(typeExpr: PsiExpression) {
emitProblem(
typeExpr,
MethodHandleBundle.message("problem.merging.general.typeMustNotBe", PsiTypes.voidType().presentableText)
)
}

private fun emitMustBeReferenceType(refc: PsiExpression, referenceClass: Type) {
emitProblem(
refc, MethodHandleBundle.message(
"problem.merging.general.referenceTypeExpectedReturn",
referenceClass,
)
)
}

private fun PsiExpression.asReferenceType(): Type {
val referenceClass = this.asType()
if (referenceClass.isPrimitive()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package de.sirywell.handlehints.mhtype

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import de.sirywell.handlehints.MethodHandleBundle.message
import de.sirywell.handlehints.TriState
import de.sirywell.handlehints.dfa.SsaAnalyzer
import de.sirywell.handlehints.dfa.SsaConstruction
import de.sirywell.handlehints.inspection.ProblemEmitter
import de.sirywell.handlehints.type.*
import org.jetbrains.annotations.Nls

class MethodHandleTransformer(private val ssaAnalyzer: SsaAnalyzer) {
class MethodHandleTransformer(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.typeData) {

// fun asCollector()

Expand All @@ -19,7 +17,7 @@
// fun asSpreader()

// TODO this is not exactly right...
fun asType(type: MethodHandleType, newType: MethodHandleType): MethodHandleType = TODO()

Check warning on line 20 in src/main/kotlin/de/sirywell/handlehints/mhtype/MethodHandleTransformer.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "asType" is never used

// fun asVarargsCollector()

Expand Down Expand Up @@ -64,10 +62,4 @@

// fun withVarargs()

private fun emitProblem(element: PsiElement, message: @Nls String): MethodHandleType {
ssaAnalyzer.typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return MethodHandleType(TopSignature)
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package de.sirywell.handlehints.mhtype

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.*
import de.sirywell.handlehints.*
import de.sirywell.handlehints.MethodHandleBundle.message
import de.sirywell.handlehints.dfa.SsaAnalyzer
import de.sirywell.handlehints.dfa.SsaConstruction
import de.sirywell.handlehints.inspection.ProblemEmitter
import de.sirywell.handlehints.type.*
import de.sirywell.handlehints.type.TopType
import org.jetbrains.annotations.Nls

private const val VAR_HANDLE_FQN = "java.lang.invoke.VarHandle"

/**
* Contains methods from [java.lang.invoke.MethodHandles] that create
* new [java.lang.invoke.MethodHandle]s.
*/
class MethodHandlesInitializer(private val ssaAnalyzer: SsaAnalyzer) {
class MethodHandlesInitializer(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.typeData) {

private val topType = MethodHandleType(TopSignature)

Expand Down Expand Up @@ -112,23 +110,6 @@ class MethodHandlesInitializer(private val ssaAnalyzer: SsaAnalyzer) {
return referenceClass
}

private fun emitProblem(element: PsiElement, message: @Nls String): MethodHandleType {
ssaAnalyzer.typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return MethodHandleType(TopSignature)
}


private fun emitMustBeArrayType(refc: PsiExpression, referenceClass: Type) {
emitProblem(
refc, message(
"problem.merging.general.arrayTypeExpected",
referenceClass,
)
)
}

private fun typesAreCompatible(
left: Type,
right: Type,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package de.sirywell.handlehints.mhtype

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiTypes
import de.sirywell.handlehints.*
import de.sirywell.handlehints.MethodHandleBundle.message
import de.sirywell.handlehints.dfa.SsaAnalyzer
import de.sirywell.handlehints.dfa.SsaConstruction
import de.sirywell.handlehints.inspection.ProblemEmitter
import de.sirywell.handlehints.type.*
import de.sirywell.handlehints.type.TopType
import org.jetbrains.annotations.Nls

/**
* Contains methods to merge multiple [MethodHandleType]s into a new one,
* provided by [java.lang.invoke.MethodHandles]
*/
class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) {
class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.typeData) {

private val bottomType = MethodHandleType(BotSignature)
private val topType = MethodHandleType(TopSignature)
Expand All @@ -39,7 +36,7 @@
emitProblem(handlerExpr, message("problem.merging.catchException.missingException", exType))
}
val returnType = if (target.signature.returnType != handler.signature.returnType) {
incompatibleReturnTypes(targetExpr, target.signature.returnType, handler.signature.returnType)
emitIncompatibleReturnTypes(targetExpr, target.signature.returnType, handler.signature.returnType)
TopType
} else {
target.signature.returnType
Expand Down Expand Up @@ -173,7 +170,7 @@
val start = ssaAnalyzer.mhType(startExpr, block) ?: bottomType
val end = ssaAnalyzer.mhType(endExpr, block) ?: bottomType
if (start.signature.returnType.join(end.signature.returnType) == TopType) {
return incompatibleReturnTypes(startExpr, start.signature.returnType, end.signature.returnType)
return emitIncompatibleReturnTypes(startExpr, start.signature.returnType, end.signature.returnType)
}
if (start.signature !is CompleteSignature || end.signature !is CompleteSignature) {
return bottomType // TODO not correct
Expand Down Expand Up @@ -377,10 +374,10 @@
return MethodHandleType(target.signature.withParameterTypes(new))
}

fun iteratedLoop(iterator: MethodHandleType, init: MethodHandleType, body: MethodHandleType): MethodHandleType =

Check warning on line 377 in src/main/kotlin/de/sirywell/handlehints/mhtype/MethodHandlesMerger.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "iteratedLoop" is never used
TODO()

fun loop(clauses: List<List<MethodHandleType>>): MethodHandleType =

Check warning on line 380 in src/main/kotlin/de/sirywell/handlehints/mhtype/MethodHandlesMerger.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "loop" is never used
TODO() // not sure if we can do anything about this

fun permuteArguments(
Expand All @@ -392,7 +389,7 @@
val target = ssaAnalyzer.mhType(targetExpr, block) ?: bottomType
val newType = ssaAnalyzer.mhType(newTypeExpr, block) ?: bottomType
if (target.signature.returnType.join(newType.signature.returnType) == TopType) {
return incompatibleReturnTypes(targetExpr, target.signature.returnType, newType.signature.returnType)
return emitIncompatibleReturnTypes(targetExpr, target.signature.returnType, newType.signature.returnType)
}
val outParams = target.signature.parameterList
val inParams = newType.signature.parameterList
Expand Down Expand Up @@ -498,50 +495,14 @@
return MethodHandleType(targetSignature)
}

fun whileLoop(

Check warning on line 498 in src/main/kotlin/de/sirywell/handlehints/mhtype/MethodHandlesMerger.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unused symbol

Function "whileLoop" is never used
initExpr: PsiExpression,
bodyExpr: PsiExpression,
predExpr: PsiExpression,
block: SsaConstruction.Block
) = doWhileLoop(initExpr, bodyExpr, predExpr, block) // same type behavior

private fun incompatibleReturnTypes(
element: PsiElement,
first: Type,
second: Type
): MethodHandleType {
return emitProblem(
element, message(
"problem.merging.general.incompatibleReturnType",
first,
second
)
)
}

private fun emitProblem(element: PsiElement, message: @Nls String): MethodHandleType {
ssaAnalyzer.typeData.reportProblem(element) {
it.registerProblem(element, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
}
return topType
}

private fun emitOutOfBounds(
size: Int?,
targetExpr: PsiExpression,
pos: Int,
exclusive: Boolean
) = if (size != null) {
if (exclusive) {
emitProblem(targetExpr, message("problem.general.position.invalidIndexKnownBoundsExcl", pos, size))
} else {
emitProblem(targetExpr, message("problem.general.position.invalidIndexKnownBoundsIncl", pos, size))
}
} else {
emitProblem(targetExpr, message("problem.general.position.invalidIndex", pos))
}

fun PsiExpression.nonNegativeInt(): Int? {

Check notice on line 505 in src/main/kotlin/de/sirywell/handlehints/mhtype/MethodHandlesMerger.kt

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Class member can have 'private' visibility

Function 'nonNegativeInt' could be private
return this.getConstantOfType<Int>()?.let {
if (it < 0) {
emitProblem(this, message("problem.general.position.invalidIndexNegative", it))
Expand Down
Loading
Loading