mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
refactor dealing with TypeUsageInformation
GitOrigin-RevId: c7dbb584134783240456e4f5d3d586cdb1948271
This commit is contained in:
committed by
intellij-monorepo-bot
parent
95cf4188c9
commit
e057b003c8
+16
-11
@@ -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)
|
||||
|
||||
-57
@@ -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 {
|
||||
|
||||
+35
-80
@@ -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()
|
||||
}
|
||||
|
||||
+119
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-10
@@ -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 {
|
||||
|
||||
+13
-24
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user