diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 92ff89712d3..32bb997691e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -128,4 +128,6 @@ fun JetType.getNestedArguments(): List { return result } +fun JetType.containsError() = ErrorUtils.containsErrorType(this) + public fun JetType.isDefaultBound(): Boolean = KotlinBuiltIns.isDefaultBound(getSupertypeRepresentative()) diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt index 62d3065ebcd..628133c78f1 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/FuzzyType.kt @@ -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 diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt index b9cd3a76c28..8c9970defab 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt @@ -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? { + //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) } } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt index dfc23d319f0..881d06b66af 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt @@ -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() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt index 16320ff143b..a4e372bd5a1 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt @@ -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) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/LambdaItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/LambdaItems.kt index 9a72d5b7689..605289de89d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/LambdaItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/LambdaItems.kt @@ -34,7 +34,7 @@ object LambdaItems { } public fun addToCollection(collection: MutableCollection, functionExpectedInfos: Collection) { - 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) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index b35b789f5a2..820e9e96b2d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -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 = SmartCastCalculator( - bindingContext, moduleDescriptor).calculate(expressionWithType, receiver) + val smartCastTypes: (VariableDescriptor) -> Collection + = 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 { 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> = expectedInfos.groupBy { it.type.makeNotNullable() } + val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() } val items = ArrayList() for ((type, infos) in expectedInfosGrouped) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt index 7771d980c6f..87c59cdaac3 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt @@ -46,7 +46,7 @@ class StaticMembers( context: JetSimpleNameExpression, enumEntriesToSkip: Set) { - 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) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index 3e5fe197101..75e42cc01e5 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -64,7 +64,7 @@ class TypeInstantiationItems( inheritanceSearchers: MutableCollection, expectedInfos: Collection ) { - val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.type.makeNotNullable() } + val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() } for ((type, infos) in expectedInfosGrouped) { val tail = mergeTails(infos.map { it.tail }) addTo(items, inheritanceSearchers, type, tail) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index 03b1146dec9..5f360842e6b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -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.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 MutableCollection.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 MutableCollection.addLoo } } +private fun 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.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection) { for (element in lookupElementsForNullable(factory)) { add(element.addTailAndNameSimilarity(matchedInfos)) diff --git a/idea/idea-completion/testData/smart/MapTo2.kt b/idea/idea-completion/testData/smart/MapTo2.kt new file mode 100644 index 00000000000..de9ce76e637 --- /dev/null +++ b/idea/idea-completion/testData/smart/MapTo2.kt @@ -0,0 +1,7 @@ +fun foo(list: List, intList: MutableList, stringList: MutableList): Collection { + list.mapTo() +} + +// EXIST: intList +// EXIST: stringList +// EXIST: arrayListOf diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index b68b5a85bdf..7e0ec0dafbd 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -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");