Smart completion works for cases when no exact type known (generic parameter substitution is not known)
This commit is contained in:
@@ -128,4 +128,6 @@ fun JetType.getNestedArguments(): List<TypeProjection> {
|
||||
return result
|
||||
}
|
||||
|
||||
fun JetType.containsError() = ErrorUtils.containsErrorType(this)
|
||||
|
||||
public fun JetType.isDefaultBound(): Boolean = KotlinBuiltIns.isDefaultBound(getSupertypeRepresentative())
|
||||
|
||||
@@ -78,20 +78,26 @@ class FuzzyType(
|
||||
}
|
||||
}
|
||||
|
||||
public fun checkIsSubtypeOf(otherType: JetType): TypeSubstitutor?
|
||||
public fun checkIsSubtypeOf(otherType: FuzzyType): TypeSubstitutor?
|
||||
= matchedSubstitutor(otherType, MatchKind.IS_SUBTYPE)
|
||||
|
||||
public fun checkIsSuperTypeOf(otherType: JetType): TypeSubstitutor?
|
||||
public fun checkIsSuperTypeOf(otherType: FuzzyType): TypeSubstitutor?
|
||||
= matchedSubstitutor(otherType, MatchKind.IS_SUPERTYPE)
|
||||
|
||||
public fun checkIsSubtypeOf(otherType: JetType): TypeSubstitutor?
|
||||
= checkIsSubtypeOf(FuzzyType(otherType, emptyList()))
|
||||
|
||||
public fun checkIsSuperTypeOf(otherType: JetType): TypeSubstitutor?
|
||||
= checkIsSuperTypeOf(FuzzyType(otherType, emptyList()))
|
||||
|
||||
private enum class MatchKind {
|
||||
IS_SUBTYPE,
|
||||
IS_SUPERTYPE
|
||||
}
|
||||
|
||||
private fun matchedSubstitutor(otherType: JetType, matchKind: MatchKind): TypeSubstitutor? {
|
||||
private fun matchedSubstitutor(otherType: FuzzyType, matchKind: MatchKind): TypeSubstitutor? {
|
||||
if (type.isError()) return null
|
||||
if (otherType.isError()) return null
|
||||
if (otherType.type.isError()) return null
|
||||
|
||||
fun JetType.checkInheritance(otherType: JetType): Boolean {
|
||||
return when (matchKind) {
|
||||
@@ -100,16 +106,17 @@ class FuzzyType(
|
||||
}
|
||||
}
|
||||
|
||||
if (freeParameters.isEmpty()) {
|
||||
return if (type.checkInheritance(otherType)) TypeSubstitutor.EMPTY else null
|
||||
if (freeParameters.isEmpty() && otherType.freeParameters.isEmpty()) {
|
||||
return if (type.checkInheritance(otherType.type)) TypeSubstitutor.EMPTY else null
|
||||
}
|
||||
|
||||
val constraintSystem = ConstraintSystemImpl()
|
||||
constraintSystem.registerTypeVariables(freeParameters, { Variance.INVARIANT })
|
||||
constraintSystem.registerTypeVariables(otherType.freeParameters, { Variance.INVARIANT })
|
||||
|
||||
when (matchKind) {
|
||||
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType, ConstraintPositionKind.RECEIVER_POSITION.position())
|
||||
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType, type, ConstraintPositionKind.RECEIVER_POSITION.position())
|
||||
MatchKind.IS_SUBTYPE -> constraintSystem.addSubtypeConstraint(type, otherType.type, ConstraintPositionKind.RECEIVER_POSITION.position())
|
||||
MatchKind.IS_SUPERTYPE -> constraintSystem.addSubtypeConstraint(otherType.type, type, ConstraintPositionKind.RECEIVER_POSITION.position())
|
||||
}
|
||||
|
||||
constraintSystem.fixVariables()
|
||||
@@ -119,7 +126,11 @@ class FuzzyType(
|
||||
// that's why we have to check subtyping manually
|
||||
val substitutor = constraintSystem.getResultingSubstitutor()
|
||||
val substitutedType = substitutor.substitute(type, Variance.INVARIANT)
|
||||
return if (substitutedType != null && substitutedType.checkInheritance(otherType)) substitutor else null
|
||||
val otherSubstitutedType = substitutor.substitute(otherType.type, Variance.INVARIANT)
|
||||
return if (substitutedType != null && otherSubstitutedType != null && substitutedType.checkInheritance(otherSubstitutedType))
|
||||
substitutor
|
||||
else
|
||||
null
|
||||
}
|
||||
else {
|
||||
return null
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.frontend.di.createContainerForMacros
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.completion.smart.toList
|
||||
import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -46,6 +47,7 @@ import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsError
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.HashSet
|
||||
@@ -64,7 +66,9 @@ data class ItemOptions(val starPrefix: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
open data class ExpectedInfo(val type: JetType, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT)
|
||||
open data class ExpectedInfo(val fuzzyType: FuzzyType, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) {
|
||||
constructor(type: JetType, expectedName: String?, tail: Tail?) : this(FuzzyType(type, emptyList()), expectedName, tail)
|
||||
}
|
||||
|
||||
data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val isFunctionLiteralArgument: Boolean) {
|
||||
constructor(argumentIndex: Int, isFunctionLiteralArgument: Boolean = false) : this(argumentIndex, null, isFunctionLiteralArgument)
|
||||
@@ -72,7 +76,7 @@ data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val
|
||||
}
|
||||
|
||||
class ArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, val function: FunctionDescriptor, val position: ArgumentPosition, itemOptions: ItemOptions = ItemOptions.DEFAULT)
|
||||
: ExpectedInfo(type, name, tail, itemOptions) {
|
||||
: ExpectedInfo(FuzzyType(type, function.typeParameters), name, tail, itemOptions) {
|
||||
|
||||
override fun equals(other: Any?)
|
||||
= other is ArgumentExpectedInfo && super.equals(other) && function == other.function && position == other.position
|
||||
@@ -132,8 +136,9 @@ class ExpectedInfos(
|
||||
}
|
||||
|
||||
public fun calculateForArgument(call: Call, argument: ValueArgument): Collection<ExpectedInfo>? {
|
||||
//TODO: it can too slow for deep nested calls (esp DSL's)
|
||||
val callExpression = (call.callElement as? JetExpression)?.getQualifiedExpressionForSelectorOrThis()
|
||||
var expectedTypes = callExpression?.let { calculate(it) }?.map { it.type }
|
||||
var expectedTypes = callExpression?.let { calculate(it) }?.map { it.fuzzyType.type }
|
||||
if (expectedTypes == null || expectedTypes.isEmpty()) {
|
||||
expectedTypes = listOf(TypeUtils.NO_EXPECTED_TYPE)
|
||||
}
|
||||
@@ -179,18 +184,25 @@ class ExpectedInfos(
|
||||
// check that all arguments before the current one matched
|
||||
if (!candidate.noErrorsInValueArguments()) continue
|
||||
|
||||
val descriptor = candidate.getResultingDescriptor()
|
||||
val parameters = descriptor.getValueParameters()
|
||||
if (parameters.isEmpty()) continue
|
||||
|
||||
val argumentToParameter = call.mapArgumentsToParameters(descriptor)
|
||||
val parameter = argumentToParameter[argument] ?: continue
|
||||
var descriptor = candidate.getResultingDescriptor()
|
||||
if (descriptor.valueParameters.isEmpty()) continue
|
||||
|
||||
val thisReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(candidate.getDispatchReceiver(), bindingContext)
|
||||
if (!Visibilities.isVisible(thisReceiver, descriptor, resolutionScope.getContainingDeclaration())) continue
|
||||
|
||||
var argumentToParameter = call.mapArgumentsToParameters(descriptor)
|
||||
var parameter = argumentToParameter[argument] ?: continue
|
||||
|
||||
if (parameter.type.containsError()) {
|
||||
parameter = parameter.original
|
||||
descriptor = descriptor.original
|
||||
argumentToParameter = call.mapArgumentsToParameters(descriptor)
|
||||
}
|
||||
|
||||
val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString()
|
||||
|
||||
val parameters = descriptor.valueParameters
|
||||
|
||||
fun needCommaForParameter(parameter: ValueParameterDescriptor): Boolean {
|
||||
if (parameter.hasDefaultValue()) return false // parameter is optional
|
||||
if (parameter.getVarargElementType() != null) return false // vararg arguments list can be empty
|
||||
@@ -243,7 +255,6 @@ class ExpectedInfos(
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, tail, descriptor, argumentPosition))
|
||||
}
|
||||
}
|
||||
@@ -283,13 +294,13 @@ class ExpectedInfos(
|
||||
return when (expressionWithType) {
|
||||
ifExpression.getCondition() -> listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null, Tail.RPARENTH))
|
||||
|
||||
ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.type, it.expectedName, Tail.ELSE) }
|
||||
ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, Tail.ELSE) }
|
||||
|
||||
ifExpression.getElse() -> {
|
||||
val ifExpectedInfo = calculate(ifExpression)
|
||||
val thenType = ifExpression.getThen()?.let { bindingContext.getType(it) }
|
||||
if (thenType != null)
|
||||
ifExpectedInfo?.filter { it.type.isSubtypeOf(thenType) }
|
||||
if (thenType != null && !thenType.isError())
|
||||
ifExpectedInfo?.filter { it.fuzzyType.checkIsSubtypeOf(thenType) != null }
|
||||
else
|
||||
ifExpectedInfo
|
||||
}
|
||||
@@ -309,7 +320,7 @@ class ExpectedInfos(
|
||||
val expectedInfos = calculate(binaryExpression)
|
||||
if (expectedInfos != null) {
|
||||
return if (leftTypeNotNullable != null)
|
||||
expectedInfos.filter { leftTypeNotNullable.isSubtypeOf(it.type) }
|
||||
expectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(leftTypeNotNullable) != null }
|
||||
else
|
||||
expectedInfos
|
||||
}
|
||||
@@ -329,13 +340,15 @@ class ExpectedInfos(
|
||||
if (functionLiteral != null) {
|
||||
val literalExpression = functionLiteral.parent as JetFunctionLiteralExpression
|
||||
return calculate(literalExpression)
|
||||
?.map { it.type }
|
||||
?.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }
|
||||
?.map { KotlinBuiltIns.getReturnTypeFromFunctionType(it) }
|
||||
?.map { ExpectedInfo(it, null, Tail.RBRACE) }
|
||||
?.map { it.fuzzyType }
|
||||
?.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) }
|
||||
?.map {
|
||||
val returnType = KotlinBuiltIns.getReturnTypeFromFunctionType(it.type)
|
||||
ExpectedInfo(FuzzyType(returnType, it.freeParameters), null, Tail.RBRACE)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return calculate(block)?.map { ExpectedInfo(it.type, it.expectedName, null) }
|
||||
return calculate(block)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, null) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan
|
||||
val resolutionFacade = file.getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.PARTIAL)
|
||||
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, resolutionFacade.findModuleDescriptor(file), false).calculate(expression) ?: return false
|
||||
val functionTypes = expectedInfos.map { it.type }.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }.toSet()
|
||||
val functionTypes = expectedInfos.map { it.fuzzyType.type }.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }.toSet()
|
||||
if (functionTypes.size() <= 1) return false
|
||||
|
||||
val lambdaParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(lambdaType).size()
|
||||
|
||||
+2
-2
@@ -50,13 +50,13 @@ object KeywordValues {
|
||||
|
||||
if (!skipTrueFalse) {
|
||||
val booleanInfoClassifier = { info: ExpectedInfo ->
|
||||
if (info.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||
if (info.fuzzyType.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) }
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) }
|
||||
}
|
||||
|
||||
val classifier = { info: ExpectedInfo -> if (info.type.isMarkedNullable()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches }
|
||||
val classifier = { info: ExpectedInfo -> if (info.fuzzyType.type.isMarkedNullable()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches }
|
||||
collection.addLookupElements(null, expectedInfos, classifier) {
|
||||
LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ object LambdaItems {
|
||||
}
|
||||
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, functionExpectedInfos: Collection<ExpectedInfo>) {
|
||||
val distinctTypes = functionExpectedInfos.map { it.type }.toSet()
|
||||
val distinctTypes = functionExpectedInfos.map { it.fuzzyType.type }.toSet()
|
||||
|
||||
val singleType = if (distinctTypes.size() == 1) distinctTypes.single() else null
|
||||
val singleSignatureLength = singleType?.let { KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(it).size() }
|
||||
@@ -60,7 +60,7 @@ object LambdaItems {
|
||||
})
|
||||
.suppressAutoInsertion()
|
||||
.assignSmartCompletionPriority(SmartCompletionItemPriority.LAMBDA)
|
||||
.addTailAndNameSimilarity(functionExpectedInfos.filter { it.type == functionType })
|
||||
.addTailAndNameSimilarity(functionExpectedInfos.filter { it.fuzzyType.type == functionType })
|
||||
collection.add(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-11
@@ -31,10 +31,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.completion.*
|
||||
import org.jetbrains.kotlin.idea.core.IterableTypesDetector
|
||||
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
|
||||
import org.jetbrains.kotlin.idea.util.makeNotNullable
|
||||
import org.jetbrains.kotlin.idea.util.nullability
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
@@ -129,21 +126,21 @@ class SmartCompletion(
|
||||
if (inOperatorArgumentResult != null) return inOperatorArgumentResult
|
||||
|
||||
val allExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null
|
||||
val filteredExpectedInfos = allExpectedInfos.filter { !it.type.isError() }
|
||||
val filteredExpectedInfos = allExpectedInfos.filter { !it.fuzzyType.type.isError() }
|
||||
if (filteredExpectedInfos.isEmpty()) return null
|
||||
|
||||
// if we complete argument of == or !=, make types in expected info's nullable to allow nullable items too
|
||||
val expectedInfos = if ((expressionWithType.getParent() as? JetBinaryExpression)?.getOperationToken() in COMPARISON_TOKENS)
|
||||
filteredExpectedInfos.map { ExpectedInfo(it.type.makeNullable(), it.expectedName, it.tail) }
|
||||
filteredExpectedInfos.map { ExpectedInfo(it.fuzzyType.makeNullable(), it.expectedName, it.tail) }
|
||||
else
|
||||
filteredExpectedInfos
|
||||
|
||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(
|
||||
bindingContext, moduleDescriptor).calculate(expressionWithType, receiver)
|
||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType>
|
||||
= SmartCastCalculator(bindingContext, moduleDescriptor).calculate(expressionWithType, receiver)
|
||||
|
||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||
|
||||
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) }
|
||||
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType.type) }
|
||||
|
||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||
if (descriptor in itemsToSkip) return listOf()
|
||||
@@ -324,7 +321,7 @@ class SmartCompletion(
|
||||
fun toLookupElement(descriptor: FunctionDescriptor): LookupElement? {
|
||||
val functionType = functionType(descriptor) ?: return null
|
||||
|
||||
val matchedExpectedInfos = functionExpectedInfos.filter { functionType.isSubtypeOf(it.type) }
|
||||
val matchedExpectedInfos = functionExpectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(functionType) != null }
|
||||
if (matchedExpectedInfos.isEmpty()) return null
|
||||
|
||||
var lookupElement = lookupElementFactory.createLookupElement(descriptor, bindingContext, true)
|
||||
@@ -372,7 +369,7 @@ class SmartCompletion(
|
||||
if (elementType != JetTokens.AS_KEYWORD && elementType != JetTokens.AS_SAFE) return null
|
||||
val expectedInfos = calcExpectedInfos(binaryExpression) ?: return null
|
||||
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.type.makeNotNullable() }
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
|
||||
|
||||
val items = ArrayList<LookupElement>()
|
||||
for ((type, infos) in expectedInfosGrouped) {
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ class StaticMembers(
|
||||
context: JetSimpleNameExpression,
|
||||
enumEntriesToSkip: Set<DeclarationDescriptor>) {
|
||||
|
||||
val expectedInfosByClass = expectedInfos.groupBy { TypeUtils.getClassDescriptor(it.type) }
|
||||
val expectedInfosByClass = expectedInfos.groupBy { TypeUtils.getClassDescriptor(it.fuzzyType.type) }
|
||||
for ((classDescriptor, expectedInfosForClass) in expectedInfosByClass) {
|
||||
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
|
||||
addToCollection(collection, classDescriptor, expectedInfosForClass, context, enumEntriesToSkip)
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ class TypeInstantiationItems(
|
||||
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
|
||||
expectedInfos: Collection<ExpectedInfo>
|
||||
) {
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.type.makeNotNullable() }
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
|
||||
for ((type, infos) in expectedInfosGrouped) {
|
||||
val tail = mergeTails(infos.map { it.tail })
|
||||
addTo(items, inheritanceSearchers, type, tail)
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.debugger.ui.tree.LocalVariableDescriptor
|
||||
import com.intellij.openapi.util.Key
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.completion.*
|
||||
@@ -132,13 +133,13 @@ private constructor(
|
||||
|
||||
fun Collection<FuzzyType>.classifyExpectedInfo(expectedInfo: ExpectedInfo): ExpectedInfoClassification {
|
||||
val sequence = asSequence()
|
||||
val substitutor = sequence.map { it.checkIsSubtypeOf(expectedInfo.type) }.firstOrNull()
|
||||
val substitutor = sequence.map { it.checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull()
|
||||
if (substitutor != null) {
|
||||
return ExpectedInfoClassification.matches(substitutor)
|
||||
}
|
||||
|
||||
if (sequence.any { it.nullability() == TypeNullability.NULLABLE }) {
|
||||
val substitutor2 = sequence.map { it.makeNotNullable().checkIsSubtypeOf(expectedInfo.type) }.firstOrNull()
|
||||
val substitutor2 = sequence.map { it.makeNotNullable().checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull()
|
||||
if (substitutor2 != null) {
|
||||
return ExpectedInfoClassification.matchesIfNotNullable(substitutor2)
|
||||
}
|
||||
@@ -170,7 +171,7 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
|
||||
val classification = infoClassifier(info)
|
||||
if (classification.substitutor != null) {
|
||||
@suppress("UNCHECKED_CAST")
|
||||
val substitutedDescriptor = descriptor?.substitute(classification.substitutor) as TDescriptor
|
||||
val substitutedDescriptor = descriptor.substituteFixed(classification.substitutor)
|
||||
val map = if (classification.makeNotNullable) makeNullableInfos else matchedInfos
|
||||
map.getOrPut(ItemData(substitutedDescriptor, info.itemOptions)) { ArrayList() }.add(info)
|
||||
}
|
||||
@@ -196,6 +197,13 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : DeclarationDescriptor?> T.substituteFixed(substitutor: TypeSubstitutor): T {
|
||||
if (this is LocalVariableDescriptor || this is ValueParameterDescriptor || this is TypeParameterDescriptor) { // TODO: it's not implemented for them
|
||||
return this
|
||||
}
|
||||
return this?.substitute(substitutor) as T
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection<ExpectedInfo>) {
|
||||
for (element in lookupElementsForNullable(factory)) {
|
||||
add(element.addTailAndNameSimilarity(matchedInfos))
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(list: List<String>, intList: MutableList<Int>, stringList: MutableList<String>): Collection<Int> {
|
||||
list.mapTo(<caret>)
|
||||
}
|
||||
|
||||
// EXIST: intList
|
||||
// EXIST: stringList
|
||||
// EXIST: arrayListOf
|
||||
+6
@@ -281,6 +281,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MapTo2.kt")
|
||||
public void testMapTo2() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/MapTo2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MethodCallArgument.kt")
|
||||
public void testMethodCallArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/MethodCallArgument.kt");
|
||||
|
||||
Reference in New Issue
Block a user