refactor dealing with TypeUsageInformation

GitOrigin-RevId: c7dbb584134783240456e4f5d3d586cdb1948271
This commit is contained in:
Konstantin.Nisht
2019-10-09 14:08:11 +00:00
committed by intellij-monorepo-bot
parent 95cf4188c9
commit e057b003c8
6 changed files with 187 additions and 182 deletions
@@ -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)
@@ -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<PsiTypeParameter, List<BoundConstraint>>,
val constraints: Collection<ConstraintFormula>,
val dependentTypes: Set<PsiTypeParameter> = emptySet()) {
operator fun plus(typeUsageInformation: TypeUsageInformation): TypeUsageInformation {
return merge(listOf(this, typeUsageInformation))
}
val contravariantTypes: List<PsiType> 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<PsiType> by lazy(NONE) {
requiredClassTypes.filter { (_, bounds) ->
bounds.all { it.marker == INHABIT }
}.map { it.key.type() }
}
val covariantTypes: List<PsiType> 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 <K, V> flattenMap(data: Iterable<Map<out K, List<V>>>): Map<K, List<V>> =
data
.flatMap { it.entries }
.groupBy { it.key }
.map { (key, values) -> key to values.flatMap { it.value } }
.toMap()
fun merge(data: Collection<TypeUsageInformation>): 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 {
@@ -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<PsiTypeParameter, MutableList<BoundConstraint>>()
private val constraintsCollector = mutableListOf<ConstraintFormula>()
private val variableTypeParameters = method.typeParameters
private val dependentTypes = mutableSetOf<PsiTypeParameter>()
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 <K, V> MutableMap<K, MutableList<V>>.safePut(key: K, value: V) = computeIfAbsent(key) { mutableListOf() }.add(value)
private fun expandWildcards(type: PsiType, context: PsiElement): List<PsiType> =
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<PsiTypeParameter>,
requiredTypesCollector: MutableMap<PsiTypeParameter, MutableList<BoundConstraint>>,
variableParameters: Set<PsiTypeParameter>,
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<GrMethod>()?.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()
}
@@ -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<PsiTypeParameter, MutableList<BoundConstraint>> = mutableMapOf()
private val constraints: MutableList<ConstraintFormula> = mutableListOf()
private val dependentTypes: MutableSet<PsiTypeParameter> = 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<PsiType> =
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 <K, V> MutableMap<in K, MutableList<V>>.safePut(key: K, value: V) = computeIfAbsent(key) { mutableListOf() }.add(value)
}
}
data class TypeUsageInformation(val requiredClassTypes: Map<PsiTypeParameter, List<BoundConstraint>>,
val constraints: Collection<ConstraintFormula>,
val dependentTypes: Set<PsiTypeParameter> = emptySet()) {
operator fun plus(typeUsageInformation: TypeUsageInformation): TypeUsageInformation {
return merge(listOf(this, typeUsageInformation))
}
val contravariantTypes: List<PsiType> 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<PsiType> by lazy(LazyThreadSafetyMode.NONE) {
requiredClassTypes.filter { (_, bounds) ->
bounds.all { it.marker == BoundConstraint.ContainMarker.INHABIT }
}.map { it.key.type() }
}
val covariantTypes: List<PsiType> 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 <K, V> flattenMap(data: Iterable<Map<out K, List<V>>>): Map<K, List<V>> =
data
.flatMap { it.entries }
.groupBy { it.key }
.map { (key, values) -> key to values.flatMap { it.value } }
.toMap()
fun merge(data: Collection<TypeUsageInformation>): 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)
}
}
}
@@ -107,19 +107,13 @@ class ClosureDriver private constructor(private val closureParameters: Map<GrPar
}
private fun collectClosureInvocationConstraints(): TypeUsageInformation {
val constraintCollector = mutableListOf<ConstraintFormula>()
val requiredTypesCollector = mutableMapOf<PsiTypeParameter, MutableList<BoundConstraint>>()
val dependentTypes = mutableSetOf<PsiTypeParameter>()
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 {
@@ -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<ReadWriteVa
}
}
fun analyzeClosureUsages(constraintCollector: MutableList<ConstraintFormula>,
closureParameter: ParameterizedClosure,
fun analyzeClosureUsages(closureParameter: ParameterizedClosure,
usages: List<ReadWriteVariableInstruction>,
dependentTypes: MutableSet<PsiTypeParameter>,
requiredTypesCollector: MutableMap<PsiTypeParameter, MutableList<BoundConstraint>>) {
builder: TypeUsageInformationBuilder) {
val parameter = closureParameter.parameter
val parameterType = parameter.type
val outerMethod = parameter.parentOfType<GrMethod>()!!
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<ConstraintFormula>,
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<ConstraintFormula>,
fun collectClosureMethodInvocationDependencies(parameterizedClosure: ParameterizedClosure,
dependentTypes: MutableSet<PsiTypeParameter>,
requiredTypesCollector: MutableMap<PsiTypeParameter, MutableList<BoundConstraint>>,
constraintCollector: MutableList<ConstraintFormula>,
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<GrMethod>() ?: 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<ConstraintFormula>,
dependentTypes: MutableSet<PsiTypeParameter>,
outerMethod: GrMethod,
requiredTypesCollector: MutableMap<PsiTypeParameter, MutableList<BoundConstraint>>) {
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)
}
}
}