From cc351e55b0b2e1bf941a8955bb4da4d54e029b15 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 20 May 2015 12:22:08 +0300 Subject: [PATCH] Create from usage: Use type predicates provided by control-flow analysis to suggest more precise types #KT-7742 Fixed --- .../callableBuilder/CallableBuilder.kt | 4 +++ .../callableBuilder/CallableInfo.kt | 6 +++- .../callableBuilder/typeUtils.kt | 30 ++++++++++++++----- .../createClass/createClassUtils.kt | 4 +-- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index 2df9c1e2d51..1d5f4f92e2a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -35,6 +35,8 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode +import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor @@ -131,6 +133,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { val currentFileContext: BindingContext val currentFileModule: ModuleDescriptor + val pseudocode: Pseudocode? by Delegates.lazy { config.originalElement.getContainingPseudocode(currentFileContext) } + private val typeCandidates = HashMap>() init { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index 330bd7a3928..9fd0b51b9c5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -48,7 +48,11 @@ abstract class TypeInfo(val variance: Variance) { } override fun getPossibleTypes(builder: CallableBuilder): List = - expression.guessTypes(builder.currentFileContext, builder.currentFileModule).flatMap { it.getPossibleSupertypes(variance) } + expression.guessTypes( + context = builder.currentFileContext, + module = builder.currentFileModule, + pseudocode = builder.pseudocode + ).flatMap { it.getPossibleSupertypes(variance) } } class ByTypeReference(val typeReference: JetTypeReference, variance: Variance): TypeInfo(variance) { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index a2843b41bc0..cd4a485bd24 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder import com.intellij.refactoring.psi.SearchUtils import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.cfg.pseudocode.* import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -36,8 +37,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.JetTypeChecker -import java.util.HashSet -import java.util.LinkedHashSet +import java.util.* private fun JetType.contains(inner: JetType): Boolean { return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() } @@ -90,9 +90,9 @@ fun JetType.getTypeParameters(): Set { fun JetExpression.guessTypes( context: BindingContext, module: ModuleDescriptor, + pseudocode: Pseudocode? = null, coerceUnusedToUnit: Boolean = true ): Array { - if (coerceUnusedToUnit && this !is JetDeclaration && isUsedAsStatement(context) @@ -108,9 +108,7 @@ fun JetExpression.guessTypes( // expression has an expected type val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this] - if (theType2 != null) { - return array(theType2) - } + if (theType2 != null) return arrayOf(theType2) val parent = getParent() return when { @@ -174,7 +172,11 @@ fun JetExpression.guessTypes( parent is JetStringTemplateEntryWithExpression && parent.getExpression() == this -> { array(module.builtIns.getStringType()) } - else -> array() // can't infer anything + else -> { + pseudocode?.getElementValue(this)?.let { + getExpectedTypePredicate(it, context).getRepresentativeTypes().toTypedArray() + } ?: arrayOf() // can't infer anything + } } } @@ -230,4 +232,18 @@ fun JetExpression.getExpressionForTypeGuess() = getAssignmentByLHS()?.getRight() fun JetCallElement.getTypeInfoForTypeArguments(): List { return getTypeArguments().map { it.getTypeReference()?.let { TypeInfo(it, Variance.INVARIANT) } }.filterNotNull() +} + +private fun TypePredicate.getRepresentativeTypes(): Set { + return when (this) { + is SingleType -> Collections.singleton(targetType) + is AllSubtypes -> Collections.singleton(upperBound) + is ForAllTypes -> { + if (typeSets.isEmpty()) AllTypes.getRepresentativeTypes() + else typeSets.map { it.getRepresentativeTypes() }.reduce { a, b -> a intersect b } + } + is ForSomeType -> typeSets.flatMapTo(LinkedHashSet()) { it.getRepresentativeTypes() } + is AllTypes -> emptySet() + else -> throw AssertionError("Invalid type predicate: ${this}") + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt index 239d37f140e..e300c129e79 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createClass/createClassUtils.kt @@ -91,8 +91,8 @@ private fun JetExpression.getInheritableTypeInfo( context: BindingContext, moduleDescriptor: ModuleDescriptor, containingDeclaration: PsiElement): Pair Boolean> { - val types = guessTypes(context, moduleDescriptor, false) - if (types.size != 1) return TypeInfo.Empty to { classKind -> true } + val types = guessTypes(context, moduleDescriptor, coerceUnusedToUnit = false) + if (types.size() != 1) return TypeInfo.Empty to { classKind -> true } val type = types.first() val descriptor = type.getConstructor().getDeclarationDescriptor()