diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/MethodParameterAugmenter.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/MethodParameterAugmenter.kt index 23fb4a659552..faf86c5161d3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/MethodParameterAugmenter.kt +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/MethodParameterAugmenter.kt @@ -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> = - ThreadLocal.withInitial { mutableMapOf() } - 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) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/RecursiveMethodAnalyzer.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/RecursiveMethodAnalyzer.kt index 5bde681f719d..c4e6cc20c7c5 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/RecursiveMethodAnalyzer.kt +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/RecursiveMethodAnalyzer.kt @@ -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, requiredTypesCollector: MutableMap>, variableParameters: Set, - 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) } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureInferenceUtil.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureInferenceUtil.kt index 5dbbb90cb399..7aa5068ebb4e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureInferenceUtil.kt +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureInferenceUtil.kt @@ -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) } } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInsight/hint/GroovyParameterTypeHintsInlayProviderTest.kt b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInsight/hint/GroovyParameterTypeHintsInlayProviderTest.kt index ec47fbddff1b..a50dcbd93ef8 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInsight/hint/GroovyParameterTypeHintsInlayProviderTest.kt +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/codeInsight/hint/GroovyParameterTypeHintsInlayProviderTest.kt @@ -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) } diff --git a/plugins/groovy/testdata/refactoring/inferMethodParametersTypes/DelegationFromDgm_after.groovy b/plugins/groovy/testdata/refactoring/inferMethodParametersTypes/DelegationFromDgm_after.groovy index 102b4c8fd9c0..ecfb79199534 100644 --- a/plugins/groovy/testdata/refactoring/inferMethodParametersTypes/DelegationFromDgm_after.groovy +++ b/plugins/groovy/testdata/refactoring/inferMethodParametersTypes/DelegationFromDgm_after.groovy @@ -1,7 +1,7 @@ import groovy.transform.stc.ClosureParams import groovy.transform.stc.FirstParam -def Object foo(@DelegatesTo.Target('a') T a, @DelegatesTo(target = 'a', strategy = 1) @ClosureParams(FirstParam) Closure cl) { +def U foo(@DelegatesTo.Target('a') T a, @DelegatesTo(target = 'a', strategy = 1) @ClosureParams(FirstParam) Closure cl) { a.with cl }