Create from usage: Use type predicates provided by control-flow analysis to suggest more precise types

#KT-7742 Fixed
This commit is contained in:
Alexey Sedunov
2015-05-20 12:22:08 +03:00
parent c757701a87
commit cc351e55b0
4 changed files with 34 additions and 10 deletions
@@ -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<TypeInfo, List<TypeCandidate>>()
init {
@@ -48,7 +48,11 @@ abstract class TypeInfo(val variance: Variance) {
}
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
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) {
@@ -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<TypeParameterDescriptor> {
fun JetExpression.guessTypes(
context: BindingContext,
module: ModuleDescriptor,
pseudocode: Pseudocode? = null,
coerceUnusedToUnit: Boolean = true
): Array<JetType> {
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<TypeInfo> {
return getTypeArguments().map { it.getTypeReference()?.let { TypeInfo(it, Variance.INVARIANT) } }.filterNotNull()
}
private fun TypePredicate.getRepresentativeTypes(): Set<JetType> {
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<JetType>()) { it.getRepresentativeTypes() }
is AllTypes -> emptySet()
else -> throw AssertionError("Invalid type predicate: ${this}")
}
}
@@ -91,8 +91,8 @@ private fun JetExpression.getInheritableTypeInfo(
context: BindingContext,
moduleDescriptor: ModuleDescriptor,
containingDeclaration: PsiElement): Pair<TypeInfo, (ClassKind) -> 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()