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 faf86c5161d3..03fcb092ca9b 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 @@ -24,21 +24,26 @@ class MethodParameterAugmenter : TypeAugmenter() { if (!Registry.`is`(GROOVY_COLLECT_METHOD_CALLS_FOR_INFERENCE, false)) { return null } - val originalMethod = getOriginalMethod(method) - val scope = with(originalMethod.containingFile?.virtualFile) { - if (this == null) return null else GlobalSearchScope.fileScope(originalMethod.project, this) + val scope = getFileScope(method) ?: return null + return computeInferredMethod(method, scope) + } + + private fun computeInferredMethod(method: GrMethod, scope: GlobalSearchScope): InferenceResult? = + RecursionManager.doPreventingRecursion(method, true) { + CachedValuesManager.getCachedValue(method) { + val typedMethod = runInferenceProcess(method, scope) + val typeParameterSubstitutor = createVirtualToActualSubstitutor(typedMethod, method) + CachedValueProvider.Result(InferenceResult(typedMethod, typeParameterSubstitutor), method) + } } - return RecursionManager.doPreventingRecursion(method, true) { - computeInferredMethod(method, scope) + + private fun getFileScope(method: GrMethod): GlobalSearchScope? { + val originalMethod = getOriginalMethod(method) + return with(originalMethod.containingFile?.virtualFile) { + if (this == null) return null else GlobalSearchScope.fileScope(originalMethod.project, this) } } - private fun computeInferredMethod(method: GrMethod, scope: GlobalSearchScope): InferenceResult = - CachedValuesManager.getCachedValue(method) { - val typedMethod = runInferenceProcess(method, scope) - val typeParameterSubstitutor = createVirtualToActualSubstitutor(typedMethod, method) - CachedValueProvider.Result(InferenceResult(typedMethod, typeParameterSubstitutor), method) - } } data class InferenceResult(val virtualMethod: GrMethod?, val typeParameterSubstitutor: PsiSubstitutor) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/InferenceDriverUtil.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/InferenceDriverUtil.kt index 35f0c2669868..8837bc8aaea2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/InferenceDriverUtil.kt +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/InferenceDriverUtil.kt @@ -5,14 +5,12 @@ import com.intellij.psi.PsiClassType import com.intellij.psi.PsiElement import com.intellij.psi.PsiType import com.intellij.psi.PsiTypeParameter -import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula import org.jetbrains.plugins.groovy.intentions.style.inference.driver.BoundConstraint.ContainMarker.* import org.jetbrains.plugins.groovy.intentions.style.inference.resolve import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyMethodCandidate import org.jetbrains.plugins.groovy.lang.resolve.impl.GdkMethodCandidate import org.jetbrains.plugins.groovy.lang.resolve.processors.inference.type -import kotlin.LazyThreadSafetyMode.NONE data class BoundConstraint(val type: PsiType, val marker: ContainMarker) { @@ -36,61 +34,6 @@ data class BoundConstraint(val type: PsiType, val marker: ContainMarker) { } } -data class TypeUsageInformation(val requiredClassTypes: Map>, - val constraints: Collection, - val dependentTypes: Set = emptySet()) { - operator fun plus(typeUsageInformation: TypeUsageInformation): TypeUsageInformation { - return merge(listOf(this, typeUsageInformation)) - } - - val contravariantTypes: List by lazy(NONE) { - requiredClassTypes.mapNotNull { (typeParameter, bounds) -> - if (bounds.all { it.marker != UPPER } && bounds.count { it.marker == LOWER } > 0) { - typeParameter.type() - } - else { - null - } - } - } - - val invariantTypes: List by lazy(NONE) { - requiredClassTypes.filter { (_, bounds) -> - bounds.all { it.marker == INHABIT } - }.map { it.key.type() } - } - - val covariantTypes: List by lazy(NONE) { - requiredClassTypes.mapNotNull { (typeParameter, bounds) -> - val inhabitTypes = bounds.filter { it.marker == INHABIT }.toSet() - if (bounds.all { it.marker != LOWER } && inhabitTypes.size > 1 && bounds.count { it.marker == UPPER } > 0) { - typeParameter.type() - } - else { - null - } - } - } - - companion object { - - private fun flattenMap(data: Iterable>>): Map> = - data - .flatMap { it.entries } - .groupBy { it.key } - .map { (key, values) -> key to values.flatMap { it.value } } - .toMap() - - - fun merge(data: Collection): TypeUsageInformation { - val requiredClassTypes = flattenMap(data.map { it.requiredClassTypes }) - val constraints = data.flatMap { it.constraints } - val dependentTypes = data.flatMap { it.dependentTypes }.toSet() - return TypeUsageInformation(requiredClassTypes, constraints, dependentTypes) - } - } -} - fun setUpParameterMapping(sourceMethod: GrMethod, sinkMethod: GrMethod) = sourceMethod.parameters.zip(sinkMethod.parameters).toMap() fun getJavaLangObject(context: PsiElement): PsiClassType { 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 c4e6cc20c7c5..c6988afe9755 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 @@ -2,7 +2,6 @@ package org.jetbrains.plugins.groovy.intentions.style.inference.driver import com.intellij.psi.* -import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula import com.intellij.psi.util.parentOfType import org.jetbrains.plugins.groovy.intentions.style.inference.driver.BoundConstraint.ContainMarker import org.jetbrains.plugins.groovy.intentions.style.inference.driver.BoundConstraint.ContainMarker.* @@ -26,7 +25,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.ConversionResult.OK import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrTypeConverter.Position.* -import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_OBJECT import org.jetbrains.plugins.groovy.lang.resolve.api.Argument import org.jetbrains.plugins.groovy.lang.resolve.api.Arguments import org.jetbrains.plugins.groovy.lang.resolve.api.ExpressionArgument @@ -37,30 +35,7 @@ import kotlin.LazyThreadSafetyMode.NONE internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveElementVisitor() { - private val requiredTypesCollector = mutableMapOf>() - private val constraintsCollector = mutableListOf() - private val variableTypeParameters = method.typeParameters - private val dependentTypes = mutableSetOf() - private val javaLangObject = getJavaLangObject(method) - - private fun generateRequiredTypes(typeParameter: PsiTypeParameter, type: PsiType, marker: ContainMarker) { - if (type == javaLangObject && marker == UPPER) return - val bindingTypes = expandWildcards(type, typeParameter) - bindingTypes.forEach { addRequiredType(typeParameter, BoundConstraint(it, marker)) } - } - - private fun addRequiredType(typeParameter: PsiTypeParameter, constraint: BoundConstraint) { - if (typeParameter in variableTypeParameters && !constraint.type.equalsToText(GROOVY_OBJECT)) { - val constraintTypeParameter = constraint.type.typeParameter() - if (constraintTypeParameter != null && constraintTypeParameter in variableTypeParameters) { - dependentTypes.add(typeParameter) - dependentTypes.add(constraintTypeParameter) - } - else { - requiredTypesCollector.safePut(typeParameter, constraint) - } - } - } + val builder = TypeUsageInformationBuilder(method) private fun processMethod(result: GroovyResolveResult, arguments: Arguments = emptyList()) { val methodResult = result as? GroovyMethodResult ?: return @@ -84,7 +59,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl private fun processReceiverConstraints(candidate: GroovyMethodCandidate) { val receiverTypeParameter = candidate.smartReceiver()?.typeParameter() ?: return val containingType = candidate.smartContainingType() ?: return - generateRequiredTypes(receiverTypeParameter, containingType, UPPER) + builder.generateRequiredTypes(receiverTypeParameter, containingType, UPPER) } private fun processArgumentConstraints(parameterType: PsiType, argument: Argument, resolveResult: GroovyMethodResult) { @@ -95,7 +70,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl }.filterNotNull() val erasureSubstitutor = lazy(NONE) { methodTypeParametersErasureSubstitutor(resolveResult.element) } val callContextSubstitutor = resolveResult.substitutor - argumentTypes.forEach { argtype -> + for (argtype in argumentTypes) { val upperType = callContextSubstitutor.substitute(parameterType).run { if (argtype == this) callContextSubstitutor.substitute(erasureSubstitutor.value.substitute(parameterType)) else this } @@ -137,19 +112,19 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl val lowerTypeParameter = currentLowerType.typeParameter() val upperTypeParameter = classType.typeParameter() if (firstVisit) { - if (classType != javaLangObject && lowerTypeParameter != null) { - generateRequiredTypes(lowerTypeParameter, classType, UPPER) + if (classType != getJavaLangObject(context) && lowerTypeParameter != null) { + builder.generateRequiredTypes(lowerTypeParameter, classType, UPPER) } if (upperTypeParameter != null) { - generateRequiredTypes(upperTypeParameter, currentLowerType, LOWER) + builder.generateRequiredTypes(upperTypeParameter, currentLowerType, LOWER) } } else { if (lowerTypeParameter != null) { - generateRequiredTypes(lowerTypeParameter, classType, EQUAL) + builder.generateRequiredTypes(lowerTypeParameter, classType, EQUAL) } if (upperTypeParameter != null) { - generateRequiredTypes(upperTypeParameter, currentLowerType, EQUAL) + builder.generateRequiredTypes(upperTypeParameter, currentLowerType, EQUAL) } } // firstVisit is necessary because java generics are invariant @@ -174,18 +149,18 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl val upperTypeParameter = bound.typeParameter() if (wildcardType.isExtends) { if (lowerTypeParameter != null) { - generateRequiredTypes(lowerTypeParameter, bound, UPPER) + builder.generateRequiredTypes(lowerTypeParameter, bound, UPPER) } if (upperTypeParameter != null) { - generateRequiredTypes(upperTypeParameter, currentLowerType, LOWER) + builder.generateRequiredTypes(upperTypeParameter, currentLowerType, LOWER) } } else if (wildcardType.isSuper) { if (lowerTypeParameter != null) { - generateRequiredTypes(lowerTypeParameter, bound, LOWER) + builder.generateRequiredTypes(lowerTypeParameter, bound, LOWER) } if (upperTypeParameter != null) { - generateRequiredTypes(upperTypeParameter, currentLowerType, UPPER) + builder.generateRequiredTypes(upperTypeParameter, currentLowerType, UPPER) } } visitClassParameters(bound) @@ -201,12 +176,12 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl override fun visitCallExpression(callExpression: GrCallExpression) { processMethod(callExpression.advancedResolve()) - constraintsCollector.add(ExpressionConstraint(null, callExpression)) + builder.addConstraint(ExpressionConstraint(null, callExpression)) super.visitCallExpression(callExpression) } override fun visitAssignmentExpression(expression: GrAssignmentExpression) { - constraintsCollector.add(ExpressionConstraint(null, expression)) + builder.addConstraint(ExpressionConstraint(null, expression)) val lValueReference = (expression.lValue as? GrReferenceExpression)?.lValueReference if (lValueReference != null) { processSetter(lValueReference) @@ -227,7 +202,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl for (rightExpression in rightExpressions) { val rightType = rightExpression.type ?: continue processRequiredParameters(rightType, leftType) - constraintsCollector.add(TypeConstraint(leftType, rightType, method)) + builder.addConstraint(TypeConstraint(leftType, rightType, method)) } } @@ -250,7 +225,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl processRequiredParameters(initializerType, variable.type) val declaredType = variable.declaredType val expectedType = if (declaredType == null) null else ExpectedType(declaredType, ASSIGNMENT) - constraintsCollector.add(ExpressionConstraint(expectedType, initializer)) + builder.addConstraint(ExpressionConstraint(expectedType, initializer)) } super.visitVariableDeclaration(variableDeclaration) } @@ -262,7 +237,7 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl if (operatorMethodResolveResult != null) { processMethod(operatorMethodResolveResult) } - constraintsCollector.add(OperatorExpressionConstraint(expression)) + builder.addConstraint(OperatorExpressionConstraint(expression)) } if (expression is GrIndexProperty) { expression.lValueReference?.advancedResolve()?.run { @@ -301,66 +276,48 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl private fun processCallInitializers() { for (parameter in method.parameters) { val initializerType = parameter.initializerGroovy?.type ?: continue - induceDeepConstraints(parameter.type, - initializerType, dependentTypes, requiredTypesCollector, - method.typeParameters.toSet(), INHABIT, parameter) + induceDeepConstraints(parameter.type, initializerType, builder, method, INHABIT) } } private fun processOuterArgument(argument: Argument, parameter: GrParameter) { val argtype = argument.type ?: return val correctArgumentType = argtype.typeParameter()?.upperBound() ?: argtype - induceDeepConstraints(parameter.type, correctArgumentType, dependentTypes, requiredTypesCollector, method.typeParameters.toSet(), - INHABIT, parameter) + induceDeepConstraints(parameter.type, correctArgumentType, builder, method, INHABIT) } companion object { - private fun MutableMap>.safePut(key: K, value: V) = computeIfAbsent(key) { mutableListOf() }.add(value) - - private fun expandWildcards(type: PsiType, context: PsiElement): List = - when (type) { - is PsiWildcardType -> when { - type.isSuper -> listOf(type.superBound, getJavaLangObject(context)) - type.isExtends -> listOf(type.extendsBound, PsiType.NULL) - else -> listOf(getJavaLangObject(context), PsiType.NULL) - } - else -> listOf(type) - } - - - fun induceDeepConstraints(leftType: PsiType, rightType: PsiType, - dependentTypes: MutableSet, - requiredTypesCollector: MutableMap>, - variableParameters: Set, - targetMarker: ContainMarker, context: PsiElement) { + fun induceDeepConstraints(leftType: PsiType, + rightType: PsiType, + builder: TypeUsageInformationBuilder, + method: GrMethod, + targetMarker: ContainMarker) { + val variableParameters = method.typeParameters.toSet() val leftTypeParameter = leftType.typeParameter() val rightTypeParameter = rightType.typeParameter() if (leftTypeParameter != null && rightTypeParameter != null && rightTypeParameter in variableParameters) { - dependentTypes.run { - add(leftTypeParameter) - add(rightTypeParameter) + builder.run { + addDependentType(leftTypeParameter) + addDependentType(rightTypeParameter) } rightTypeParameter.extendsListTypes.firstOrNull() } else if (leftTypeParameter != null) { - val typeSet = expandWildcards(rightType, context) - typeSet.forEach { requiredTypesCollector.safePut(leftTypeParameter, BoundConstraint(it, targetMarker)) } + builder.generateRequiredTypes(leftTypeParameter, rightType, targetMarker) } if (leftType is PsiArrayType && rightType is PsiArrayType) { - induceDeepConstraints(leftType.componentType, rightType.componentType, dependentTypes, requiredTypesCollector, variableParameters, - targetMarker, context) + induceDeepConstraints(leftType.componentType, rightType.componentType, builder, method, targetMarker) } val leftBound = leftTypeParameter?.upperBound() ?: leftType val rightBound = rightTypeParameter?.upperBound() ?: rightType val leftTypeArguments = (leftBound as? PsiClassType)?.parameters ?: return val rightTypeArguments = (rightBound as? PsiClassType)?.parameters ?: return - val rangedRightTypeArguments = rightTypeArguments.map { it ?: PsiWildcardType.createUnbounded(context.manager) }.toTypedArray() + val rangedRightTypeArguments = rightTypeArguments.map { it ?: PsiWildcardType.createUnbounded(method.manager) }.toTypedArray() for ((leftTypeArgument, rightTypeArgument) in leftTypeArguments.zip(rangedRightTypeArguments)) { leftTypeArgument ?: continue - induceDeepConstraints(leftTypeArgument, rightTypeArgument, dependentTypes, requiredTypesCollector, variableParameters, targetMarker, - context) + induceDeepConstraints(leftTypeArgument, rightTypeArgument, builder, method, targetMarker) } } @@ -381,12 +338,10 @@ internal class RecursiveMethodAnalyzer(val method: GrMethod) : GroovyRecursiveEl private fun processExitExpression(expression: GrExpression) { val returnType = expression.parentOfType()?.returnType?.takeIf { it != PsiType.NULL && it != PsiType.VOID } ?: return - constraintsCollector.add(ExpressionConstraint(ExpectedType(returnType, RETURN_VALUE), expression)) + builder.addConstraint(ExpressionConstraint(ExpectedType(returnType, RETURN_VALUE), expression)) val typeParameter = expression.type.typeParameter() ?: return - generateRequiredTypes(typeParameter, returnType, UPPER) + builder.generateRequiredTypes(typeParameter, returnType, UPPER) } - - fun buildUsageInformation(): TypeUsageInformation = - TypeUsageInformation(requiredTypesCollector, constraintsCollector, dependentTypes) + fun buildUsageInformation(): TypeUsageInformation = builder.build() } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/TypeUsageInformation.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/TypeUsageInformation.kt new file mode 100644 index 000000000000..82c228bea450 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/TypeUsageInformation.kt @@ -0,0 +1,119 @@ +// 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.driver + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypeParameter +import com.intellij.psi.PsiWildcardType +import com.intellij.psi.impl.source.resolve.graphInference.constraints.ConstraintFormula +import org.jetbrains.plugins.groovy.intentions.style.inference.typeParameter +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames +import org.jetbrains.plugins.groovy.lang.resolve.processors.inference.type + +class TypeUsageInformationBuilder(method: GrMethod) { + private val requiredClassTypes: MutableMap> = mutableMapOf() + private val constraints: MutableList = mutableListOf() + private val dependentTypes: MutableSet = mutableSetOf() + private val variableTypeParameters = method.typeParameters + private val javaLangObject = getJavaLangObject(method) + + fun generateRequiredTypes(typeParameter: PsiTypeParameter, type: PsiType, marker: BoundConstraint.ContainMarker) { + if (type == javaLangObject && marker == BoundConstraint.ContainMarker.UPPER) return + val bindingTypes = expandWildcards(type, typeParameter) + bindingTypes.forEach { addRequiredType(typeParameter, BoundConstraint(it, marker)) } + } + + fun addConstraint(constraintFormula: ConstraintFormula) { + constraints.add(constraintFormula) + } + + fun addDependentType(typeParameter: PsiTypeParameter) { + dependentTypes.add(typeParameter) + } + + fun build(): TypeUsageInformation = TypeUsageInformation(requiredClassTypes, constraints, dependentTypes) + + + private fun addRequiredType(typeParameter: PsiTypeParameter, constraint: BoundConstraint) { + if (typeParameter in variableTypeParameters && !constraint.type.equalsToText(GroovyCommonClassNames.GROOVY_OBJECT)) { + val constraintTypeParameter = constraint.type.typeParameter() + if (constraintTypeParameter != null && constraintTypeParameter in variableTypeParameters) { + dependentTypes.add(typeParameter) + dependentTypes.add(constraintTypeParameter) + } + else { + requiredClassTypes.safePut(typeParameter, constraint) + } + } + } + + private fun expandWildcards(type: PsiType, context: PsiElement): List = + when (type) { + is PsiWildcardType -> when { + type.isSuper -> listOf(type.superBound, getJavaLangObject(context)) + type.isExtends -> listOf(type.extendsBound, PsiType.NULL) + else -> listOf(javaLangObject, PsiType.NULL) + } + else -> listOf(type) + } + + companion object { + private fun MutableMap>.safePut(key: K, value: V) = computeIfAbsent(key) { mutableListOf() }.add(value) + } +} + +data class TypeUsageInformation(val requiredClassTypes: Map>, + val constraints: Collection, + val dependentTypes: Set = emptySet()) { + operator fun plus(typeUsageInformation: TypeUsageInformation): TypeUsageInformation { + return merge(listOf(this, typeUsageInformation)) + } + + val contravariantTypes: List by lazy(LazyThreadSafetyMode.NONE) { + requiredClassTypes.mapNotNull { (typeParameter, bounds) -> + if (bounds.all { it.marker != BoundConstraint.ContainMarker.UPPER } && bounds.count { it.marker == BoundConstraint.ContainMarker.LOWER } > 0) { + typeParameter.type() + } + else { + null + } + } + } + + val invariantTypes: List by lazy(LazyThreadSafetyMode.NONE) { + requiredClassTypes.filter { (_, bounds) -> + bounds.all { it.marker == BoundConstraint.ContainMarker.INHABIT } + }.map { it.key.type() } + } + + val covariantTypes: List by lazy(LazyThreadSafetyMode.NONE) { + requiredClassTypes.mapNotNull { (typeParameter, bounds) -> + val inhabitTypes = bounds.filter { it.marker == BoundConstraint.ContainMarker.INHABIT }.toSet() + if (bounds.all { it.marker != BoundConstraint.ContainMarker.LOWER } && inhabitTypes.size > 1 && bounds.count { it.marker == BoundConstraint.ContainMarker.UPPER } > 0) { + typeParameter.type() + } + else { + null + } + } + } + + companion object { + + private fun flattenMap(data: Iterable>>): Map> = + data + .flatMap { it.entries } + .groupBy { it.key } + .map { (key, values) -> key to values.flatMap { it.value } } + .toMap() + + + fun merge(data: Collection): TypeUsageInformation { + val requiredClassTypes = flattenMap(data.map { it.requiredClassTypes }) + val constraints = data.flatMap { it.constraints } + val dependentTypes = data.flatMap { it.dependentTypes }.toSet() + return TypeUsageInformation(requiredClassTypes, constraints, dependentTypes) + } + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureDriver.kt b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureDriver.kt index 6940a2b7a9d6..97ae83782f08 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureDriver.kt +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/inference/driver/closure/ClosureDriver.kt @@ -107,19 +107,13 @@ class ClosureDriver private constructor(private val closureParameters: Map() - val requiredTypesCollector = mutableMapOf>() - val dependentTypes = mutableSetOf() + val builder = TypeUsageInformationBuilder(method) method.forEachParameterUsage { parameter, instructions -> - if (parameter !in closureParameters.keys) { - return@forEachParameterUsage + if (parameter in closureParameters.keys) { + analyzeClosureUsages(closureParameters.getValue(parameter), instructions, builder) } - analyzeClosureUsages(constraintCollector, closureParameters.getValue(parameter), instructions, dependentTypes, requiredTypesCollector) } - return TypeUsageInformation( - requiredTypesCollector.filter { it.key in closureParameters.flatMap { (_, parameterizedClosure) -> parameterizedClosure.typeParameters } }, - constraintCollector, - dependentTypes) + return builder.build() } override fun collectInnerConstraints(): TypeUsageInformation { 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 7aa5068ebb4e..047ac356bd44 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 @@ -9,9 +9,9 @@ import com.intellij.psi.impl.source.resolve.graphInference.constraints.Constrain import com.intellij.psi.util.parentOfType import org.jetbrains.plugins.groovy.intentions.closure.isClosureCall import org.jetbrains.plugins.groovy.intentions.closure.isClosureCallMethod -import org.jetbrains.plugins.groovy.intentions.style.inference.driver.BoundConstraint import org.jetbrains.plugins.groovy.intentions.style.inference.driver.BoundConstraint.ContainMarker.LOWER import org.jetbrains.plugins.groovy.intentions.style.inference.driver.RecursiveMethodAnalyzer +import org.jetbrains.plugins.groovy.intentions.style.inference.driver.TypeUsageInformationBuilder import org.jetbrains.plugins.groovy.intentions.style.inference.driver.closure.ClosureParametersStorageBuilder.Companion.isReferenceTo import org.jetbrains.plugins.groovy.intentions.style.inference.properResolve import org.jetbrains.plugins.groovy.intentions.style.inference.resolve @@ -71,14 +71,11 @@ inline fun GrMethod.forEachParameterUsage(action: (GrParameter, List, - closureParameter: ParameterizedClosure, +fun analyzeClosureUsages(closureParameter: ParameterizedClosure, usages: List, - dependentTypes: MutableSet, - requiredTypesCollector: MutableMap>) { + builder: TypeUsageInformationBuilder) { val parameter = closureParameter.parameter val parameterType = parameter.type - val outerMethod = parameter.parentOfType()!! val delegatesToCombiner = closureParameter.delegatesToCombiner parameter.setType(PsiType.getTypeByName(GROOVY_LANG_CLOSURE, parameter.project, parameter.resolveScope)) for (usage in usages) { @@ -91,15 +88,13 @@ fun analyzeClosureUsages(constraintCollector: MutableList, val resolveResult = nearestCall.properResolve() as? GroovyMethodResult ?: return if (nearestCall.resolveMethod()?.containingClass?.qualifiedName == GROOVY_LANG_CLOSURE) { delegatesToCombiner.acceptResolveResult(resolveResult) - collectClosureMethodInvocationDependencies(closureParameter, dependentTypes, requiredTypesCollector, constraintCollector, - resolveResult) + collectClosureMethodInvocationDependencies(closureParameter, builder, resolveResult) } else { val mapping = resolveResult.candidate?.argumentMapping ?: continue val requiredArgument = mapping.arguments.find { it.isReferenceTo(parameter) } ?: continue val innerParameter = mapping.targetParameter(requiredArgument) ?: continue - collectClosureParamsDependencies(innerParameter, resolveResult, closureParameter, constraintCollector, dependentTypes, outerMethod, - requiredTypesCollector) + collectClosureParamsDependencies(innerParameter, resolveResult, closureParameter, builder) processDelegatesToAnnotation(innerParameter, resolveResult, closureParameter.delegatesToCombiner) } } @@ -108,9 +103,7 @@ fun analyzeClosureUsages(constraintCollector: MutableList, fun collectClosureMethodInvocationDependencies(parameterizedClosure: ParameterizedClosure, - dependentTypes: MutableSet, - requiredTypesCollector: MutableMap>, - constraintCollector: MutableList, + builder: TypeUsageInformationBuilder, resolveResult: GroovyMethodResult) { if (resolveResult.candidate?.method.isClosureCallMethod()) { val arguments = resolveResult.candidate?.argumentMapping?.arguments ?: return @@ -118,9 +111,8 @@ fun collectClosureMethodInvocationDependencies(parameterizedClosure: Parameteriz val method = parameterizedClosure.parameter.parentOfType() ?: return for ((expectedType, argument) in expectedTypes) { val argumentType = argument.type ?: continue - constraintCollector.add(TypeConstraint(expectedType, argumentType, method)) - RecursiveMethodAnalyzer.induceDeepConstraints(expectedType, argumentType, dependentTypes, requiredTypesCollector, - method.typeParameters.toSet(), LOWER, method) + builder.addConstraint(TypeConstraint(expectedType, argumentType, method)) + RecursiveMethodAnalyzer.induceDeepConstraints(expectedType, argumentType, builder, method, LOWER) } } } @@ -139,20 +131,17 @@ fun extractSignature(innerParameter: PsiParameter, resolveResult: GroovyMethodRe fun collectClosureParamsDependencies(innerParameter: PsiParameter, resolveResult: GroovyMethodResult, closureParameter: ParameterizedClosure, - constraintCollector: MutableList, - dependentTypes: MutableSet, - outerMethod: GrMethod, - requiredTypesCollector: MutableMap>) { + builder: TypeUsageInformationBuilder) { val signature = extractSignature(innerParameter, resolveResult) ?: return for ((inferredType, typeParameter) in signature.zip(closureParameter.typeParameters)) { - constraintCollector.add(TypeConstraint(typeParameter.type(), inferredType, outerMethod)) + builder.addConstraint(TypeConstraint(typeParameter.type(), inferredType, innerParameter)) val inferredTypeParameter = inferredType.typeParameter() if (inferredTypeParameter != null) { - dependentTypes.add(typeParameter) - dependentTypes.add(inferredTypeParameter) + builder.addDependentType(typeParameter) + builder.addDependentType(inferredTypeParameter) } else { - requiredTypesCollector.computeIfAbsent(typeParameter) { mutableListOf() }.add(BoundConstraint(inferredType, LOWER)) + builder.generateRequiredTypes(typeParameter, inferredType, LOWER) } } }