Skip to content

Commit

Permalink
refactor: introduce common supertype ProblemEmitter (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell committed Jun 17, 2024
1 parent 5f40947 commit bafda4a
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 116 deletions.
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 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
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 Down Expand Up @@ -106,59 +107,59 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData)
"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
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 Down Expand Up @@ -64,10 +62,4 @@ class MethodHandleTransformer(private val ssaAnalyzer: SsaAnalyzer) {

// 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 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) {
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 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) {
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 @@ -392,7 +389,7 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) {
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 @@ -505,42 +502,6 @@ class MethodHandlesMerger(private val ssaAnalyzer: SsaAnalyzer) {
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) {
Expand Down
Loading

0 comments on commit bafda4a

Please sign in to comment.