IDEA-221863 SOE in type inlay hint: better preventing endless recursion

GitOrigin-RevId: 49a0a0675e28d6ee6b5262645575cb964851929d
This commit is contained in:
Konstantin.Nisht
2019-10-09 14:08:11 +00:00
committed by intellij-monorepo-bot
parent fbd7bc8a46
commit 95cf4188c9
5 changed files with 17 additions and 48 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.intentions.style.inference
import com.intellij.openapi.util.RecursionManager
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiSubstitutor
import com.intellij.psi.PsiType
@@ -8,8 +9,6 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.parentOfType
import org.jetbrains.plugins.groovy.intentions.style.inference.MethodParameterAugmenter.Companion.VisitState.NOT_VISITED
import org.jetbrains.plugins.groovy.intentions.style.inference.MethodParameterAugmenter.Companion.VisitState.VISITED_MANY
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
@@ -21,9 +20,6 @@ class MethodParameterAugmenter : TypeAugmenter() {
const val GROOVY_COLLECT_METHOD_CALLS_FOR_INFERENCE = "groovy.collect.method.calls.for.inference"
private val methodRegistry: ThreadLocal<MutableMap<GrMethod, VisitState>> =
ThreadLocal.withInitial { mutableMapOf<GrMethod, VisitState>() }
internal fun createInferenceResult(method: GrMethod): InferenceResult? {
if (!Registry.`is`(GROOVY_COLLECT_METHOD_CALLS_FOR_INFERENCE, false)) {
return null
@@ -32,18 +28,8 @@ class MethodParameterAugmenter : TypeAugmenter() {
val scope = with(originalMethod.containingFile?.virtualFile) {
if (this == null) return null else GlobalSearchScope.fileScope(originalMethod.project, this)
}
val involvedMethods = methodRegistry.get()
if (involvedMethods.getOrDefault(method, NOT_VISITED) == VISITED_MANY) {
return InferenceResult(method, PsiSubstitutor.EMPTY)
}
else {
involvedMethods[method] = involvedMethods.getOrDefault(method, NOT_VISITED).nextState()
try {
return computeInferredMethod(method, scope)
}
finally {
involvedMethods[method] = involvedMethods[method]!!.prevState()
}
return RecursionManager.doPreventingRecursion(method, true) {
computeInferredMethod(method, scope)
}
}
@@ -53,24 +39,6 @@ class MethodParameterAugmenter : TypeAugmenter() {
val typeParameterSubstitutor = createVirtualToActualSubstitutor(typedMethod, method)
CachedValueProvider.Result(InferenceResult(typedMethod, typeParameterSubstitutor), method)
}
private enum class VisitState {
NOT_VISITED {
override fun nextState(): VisitState = VISITED_ONCE
override fun prevState(): VisitState = NOT_VISITED
},
VISITED_ONCE {
override fun nextState(): VisitState = VISITED_MANY
override fun prevState(): VisitState = NOT_VISITED
},
VISITED_MANY {
override fun nextState(): VisitState = VISITED_MANY
override fun prevState(): VisitState = VISITED_ONCE
};
abstract fun nextState(): VisitState
abstract fun prevState(): VisitState
}
}
data class InferenceResult(val virtualMethod: GrMethod?, val typeParameterSubstitutor: PsiSubstitutor)
@@ -291,7 +291,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl
for (outerCall in calls.mapNotNull { it.element.parent }) {
val candidate = (outerCall.properResolve() as? GroovyMethodResult)?.candidate ?: continue
val argumentMapping = candidate.argumentMapping ?: continue
argumentMapping.expectedTypes.forEach { (_, argument) ->
argumentMapping.arguments.forEach { argument ->
val param = mapping[argumentMapping.targetParameter(argument)?.name] ?: return@forEach
processOuterArgument(argument, param)
}
@@ -303,7 +303,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl
val initializerType = parameter.initializerGroovy?.type ?: continue
induceDeepConstraints(parameter.type,
initializerType, dependentTypes, requiredTypesCollector,
method.typeParameters.toSet(), INHABIT)
method.typeParameters.toSet(), INHABIT, parameter)
}
}
@@ -311,7 +311,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl
val argtype = argument.type ?: return
val correctArgumentType = argtype.typeParameter()?.upperBound() ?: argtype
induceDeepConstraints(parameter.type, correctArgumentType, dependentTypes, requiredTypesCollector, method.typeParameters.toSet(),
INHABIT)
INHABIT, parameter)
}
companion object {
@@ -334,7 +334,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl
dependentTypes: MutableSet<PsiTypeParameter>,
requiredTypesCollector: MutableMap<PsiTypeParameter, MutableList<BoundConstraint>>,
variableParameters: Set<PsiTypeParameter>,
targetMarker: ContainMarker) {
targetMarker: ContainMarker, context: PsiElement) {
val leftTypeParameter = leftType.typeParameter()
val rightTypeParameter = rightType.typeParameter()
if (leftTypeParameter != null && rightTypeParameter != null && rightTypeParameter in variableParameters) {
@@ -345,21 +345,22 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl
rightTypeParameter.extendsListTypes.firstOrNull()
}
else if (leftTypeParameter != null) {
val typeSet = expandWildcards(rightType, leftTypeParameter)
val typeSet = expandWildcards(rightType, context)
typeSet.forEach { requiredTypesCollector.safePut(leftTypeParameter, BoundConstraint(it, targetMarker)) }
}
if (leftType is PsiArrayType && rightType is PsiArrayType) {
induceDeepConstraints(leftType.componentType, rightType.componentType, dependentTypes, requiredTypesCollector, variableParameters,
targetMarker)
targetMarker, context)
}
val leftBound = leftTypeParameter?.upperBound() ?: leftType
val rightBound = rightTypeParameter?.upperBound() ?: rightType
val leftTypeArguments = (leftBound as? PsiClassType)?.parameters ?: return
val rightTypeArguments = (rightBound as? PsiClassType)?.parameters ?: return
for ((leftTypeArgument, rightTypeArgument) in leftTypeArguments.zip(rightTypeArguments)) {
val rangedRightTypeArguments = rightTypeArguments.map { it ?: PsiWildcardType.createUnbounded(context.manager) }.toTypedArray()
for ((leftTypeArgument, rightTypeArgument) in leftTypeArguments.zip(rangedRightTypeArguments)) {
leftTypeArgument ?: continue
rightTypeArgument ?: continue
induceDeepConstraints(leftTypeArgument, rightTypeArgument, dependentTypes, requiredTypesCollector, variableParameters, targetMarker)
induceDeepConstraints(leftTypeArgument, rightTypeArgument, dependentTypes, requiredTypesCollector, variableParameters, targetMarker,
context)
}
}
@@ -120,7 +120,7 @@ fun collectClosureMethodInvocationDependencies(parameterizedClosure: Parameteriz
val argumentType = argument.type ?: continue
constraintCollector.add(TypeConstraint(expectedType, argumentType, method))
RecursiveMethodAnalyzer.induceDeepConstraints(expectedType, argumentType, dependentTypes, requiredTypesCollector,
method.typeParameters.toSet(), LOWER)
method.typeParameters.toSet(), LOWER, method)
}
}
}
@@ -64,7 +64,7 @@ foo(['q'], ['q'])
fun testClosure() {
val text = """
def<# [< [T extends A] >] #> foo(<# [T ] #>a, <# [[Closure < Object >] ] #>c) {
def<# [< [T extends A] >] #> foo(<# [T ] #>a, <# [[Closure < [? ] >] ] #>c) {
c(a)
}
@@ -80,7 +80,7 @@ foo(null as A) {
fun testInsideClosure() {
val text = """
def foo(<# [Integer ] #>arg, <# [[Closure < Byte >] ] #>closure) {
def foo(<# [Integer ] #>arg, <# [[Closure < [? ] >] ] #>closure) {
closure(arg)
}
@@ -1,7 +1,7 @@
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FirstParam
def <T> Object foo(@DelegatesTo.Target('a') T a, @DelegatesTo(target = 'a', strategy = 1) @ClosureParams(FirstParam) Closure<?> cl) {
def <T, U> U foo(@DelegatesTo.Target('a') T a, @DelegatesTo(target = 'a', strategy = 1) @ClosureParams(FirstParam) Closure<U> cl) {
a.with cl
}