Create from usage: Use type predicates provided by control-flow analysis to suggest more precise types
#KT-7742 Fixed
This commit is contained in:
+4
@@ -35,6 +35,8 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager
|
|||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
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.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
||||||
@@ -131,6 +133,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
val currentFileContext: BindingContext
|
val currentFileContext: BindingContext
|
||||||
val currentFileModule: ModuleDescriptor
|
val currentFileModule: ModuleDescriptor
|
||||||
|
|
||||||
|
val pseudocode: Pseudocode? by Delegates.lazy { config.originalElement.getContainingPseudocode(currentFileContext) }
|
||||||
|
|
||||||
private val typeCandidates = HashMap<TypeInfo, List<TypeCandidate>>()
|
private val typeCandidates = HashMap<TypeInfo, List<TypeCandidate>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
+5
-1
@@ -48,7 +48,11 @@ abstract class TypeInfo(val variance: Variance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
|
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) {
|
class ByTypeReference(val typeReference: JetTypeReference, variance: Variance): TypeInfo(variance) {
|
||||||
|
|||||||
+23
-7
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
|||||||
|
|
||||||
import com.intellij.refactoring.psi.SearchUtils
|
import com.intellij.refactoring.psi.SearchUtils
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.*
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
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.resolve.scopes.JetScope
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||||
import java.util.HashSet
|
import java.util.*
|
||||||
import java.util.LinkedHashSet
|
|
||||||
|
|
||||||
private fun JetType.contains(inner: JetType): Boolean {
|
private fun JetType.contains(inner: JetType): Boolean {
|
||||||
return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() }
|
return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() }
|
||||||
@@ -90,9 +90,9 @@ fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
|
|||||||
fun JetExpression.guessTypes(
|
fun JetExpression.guessTypes(
|
||||||
context: BindingContext,
|
context: BindingContext,
|
||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
|
pseudocode: Pseudocode? = null,
|
||||||
coerceUnusedToUnit: Boolean = true
|
coerceUnusedToUnit: Boolean = true
|
||||||
): Array<JetType> {
|
): Array<JetType> {
|
||||||
|
|
||||||
if (coerceUnusedToUnit
|
if (coerceUnusedToUnit
|
||||||
&& this !is JetDeclaration
|
&& this !is JetDeclaration
|
||||||
&& isUsedAsStatement(context)
|
&& isUsedAsStatement(context)
|
||||||
@@ -108,9 +108,7 @@ fun JetExpression.guessTypes(
|
|||||||
|
|
||||||
// expression has an expected type
|
// expression has an expected type
|
||||||
val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this]
|
val theType2 = context[BindingContext.EXPECTED_EXPRESSION_TYPE, this]
|
||||||
if (theType2 != null) {
|
if (theType2 != null) return arrayOf(theType2)
|
||||||
return array(theType2)
|
|
||||||
}
|
|
||||||
|
|
||||||
val parent = getParent()
|
val parent = getParent()
|
||||||
return when {
|
return when {
|
||||||
@@ -174,7 +172,11 @@ fun JetExpression.guessTypes(
|
|||||||
parent is JetStringTemplateEntryWithExpression && parent.getExpression() == this -> {
|
parent is JetStringTemplateEntryWithExpression && parent.getExpression() == this -> {
|
||||||
array(module.builtIns.getStringType())
|
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> {
|
fun JetCallElement.getTypeInfoForTypeArguments(): List<TypeInfo> {
|
||||||
return getTypeArguments().map { it.getTypeReference()?.let { TypeInfo(it, Variance.INVARIANT) } }.filterNotNull()
|
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}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -91,8 +91,8 @@ private fun JetExpression.getInheritableTypeInfo(
|
|||||||
context: BindingContext,
|
context: BindingContext,
|
||||||
moduleDescriptor: ModuleDescriptor,
|
moduleDescriptor: ModuleDescriptor,
|
||||||
containingDeclaration: PsiElement): Pair<TypeInfo, (ClassKind) -> Boolean> {
|
containingDeclaration: PsiElement): Pair<TypeInfo, (ClassKind) -> Boolean> {
|
||||||
val types = guessTypes(context, moduleDescriptor, false)
|
val types = guessTypes(context, moduleDescriptor, coerceUnusedToUnit = false)
|
||||||
if (types.size != 1) return TypeInfo.Empty to { classKind -> true }
|
if (types.size() != 1) return TypeInfo.Empty to { classKind -> true }
|
||||||
|
|
||||||
val type = types.first()
|
val type = types.first()
|
||||||
val descriptor = type.getConstructor().getDeclarationDescriptor()
|
val descriptor = type.getConstructor().getDeclarationDescriptor()
|
||||||
|
|||||||
Reference in New Issue
Block a user