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 f60068c7ec1..450df1007b5 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 @@ -21,7 +21,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.frontend.di.createContainerForMacros import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.completion.smart.TypesWithContainsDetector import org.jetbrains.kotlin.idea.completion.smart.toList +import org.jetbrains.kotlin.idea.core.IterableTypesDetector import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.fuzzyReturnType @@ -45,10 +47,12 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeSubstitutor 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.makeNotNullable +import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.LinkedHashSet enum class Tail { @@ -65,10 +69,29 @@ data class ItemOptions(val starPrefix: Boolean) { } } -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) +interface ByTypeFilter { + fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? } +class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter { + override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType) +} + +open data class ExpectedInfo(val filter: ByTypeFilter, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) { + constructor(fuzzyType: FuzzyType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT) + : this(ByExpectedTypeFilter(fuzzyType), expectedName, tail, itemOptions) + + constructor(type: JetType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT) + : this(FuzzyType(type, emptyList()), expectedName, tail, itemOptions) + + fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? = filter.matchingSubstitutor(descriptorType) + + fun matchingSubstitutor(descriptorType: JetType): TypeSubstitutor? = matchingSubstitutor(FuzzyType(descriptorType, emptyList())) +} + +val ExpectedInfo.fuzzyType: FuzzyType? + get() = (this.filter as? ByExpectedTypeFilter)?.fuzzyType + data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val isFunctionLiteralArgument: Boolean) { constructor(argumentIndex: Int, isFunctionLiteralArgument: Boolean = false) : this(argumentIndex, null, isFunctionLiteralArgument) constructor(argumentIndex: Int, argumentName: Name?) : this(argumentIndex, argumentName, false) @@ -111,6 +134,8 @@ class ExpectedInfos( ?: calculateForInitializer(expressionWithType) ?: calculateForExpressionBody(expressionWithType) ?: calculateForReturn(expressionWithType) + ?: calculateForLoopRange(expressionWithType) + ?: calculateForInOperatorArgument(expressionWithType) ?: getFromBindingContext(expressionWithType) } @@ -137,11 +162,12 @@ class ExpectedInfos( public fun calculateForArgument(call: Call, argument: ValueArgument): Collection? { val results = calculateForArgument(call, TypeUtils.NO_EXPECTED_TYPE, argument) ?: return null - if (useOuterCallsExpectedTypeCount > 0 && results.any { it.fuzzyType.freeParameters.isNotEmpty() && it.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false }) { + if (useOuterCallsExpectedTypeCount > 0 && results.any { it.fuzzyType != null && it.fuzzyType!!.freeParameters.isNotEmpty() && it.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false }) { val callExpression = (call.callElement as? JetExpression)?.getQualifiedExpressionForSelectorOrThis() ?: return results val expectedFuzzyTypes = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useHeuristicSignatures, useOuterCallsExpectedTypeCount - 1) .calculate(callExpression) - ?.map { it.fuzzyType } ?: return results + ?.map { it.fuzzyType } + ?.filterNotNull() ?: return results if (expectedFuzzyTypes.isEmpty() || expectedFuzzyTypes.any { it.freeParameters.isNotEmpty() }) return results return expectedFuzzyTypes @@ -304,13 +330,13 @@ class ExpectedInfos( return when (expressionWithType) { ifExpression.getCondition() -> listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null, Tail.RPARENTH)) - ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, Tail.ELSE) } + ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.filter, it.expectedName, Tail.ELSE) } ifExpression.getElse() -> { val ifExpectedInfo = calculate(ifExpression) val thenType = ifExpression.getThen()?.let { bindingContext.getType(it) } if (thenType != null && !thenType.isError()) - ifExpectedInfo?.filter { it.fuzzyType.checkIsSubtypeOf(thenType) != null } + ifExpectedInfo?.filter { it.matchingSubstitutor(thenType) != null } else ifExpectedInfo } @@ -330,7 +356,7 @@ class ExpectedInfos( val expectedInfos = calculate(binaryExpression) if (expectedInfos != null) { return if (leftTypeNotNullable != null) - expectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(leftTypeNotNullable) != null } + expectedInfos.filter { it.matchingSubstitutor(leftTypeNotNullable) != null } else expectedInfos } @@ -351,6 +377,7 @@ class ExpectedInfos( val literalExpression = functionLiteral.parent as JetFunctionLiteralExpression return calculate(literalExpression) ?.map { it.fuzzyType } + ?.filterNotNull() ?.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) } ?.map { val returnType = KotlinBuiltIns.getReturnTypeFromFunctionType(it.type) @@ -358,7 +385,7 @@ class ExpectedInfos( } } else { - return calculate(block)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, null) } + return calculate(block)?.map { ExpectedInfo(it.filter, it.expectedName, null) } } } @@ -416,6 +443,45 @@ class ExpectedInfos( } } + private fun calculateForLoopRange(expressionWithType: JetExpression): Collection? { + val forExpression = (expressionWithType.parent as? JetContainerNode) + ?.parent as? JetForExpression ?: return null + if (expressionWithType != forExpression.loopRange) return null + + val loopVar = forExpression.loopParameter + val loopVarType = if (loopVar != null && loopVar.typeReference != null) + (resolutionFacade.resolveToDescriptor(loopVar) as VariableDescriptor).type.check { !it.isError } + else + null + + val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!! + val iterableDetector = IterableTypesDetector(forExpression.project, moduleDescriptor, scope) + + val byTypeFilter = object : ByTypeFilter { + override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? { + return if (iterableDetector.isIterable(descriptorType, loopVarType)) TypeSubstitutor.EMPTY else null + } + } + return listOf(ExpectedInfo(byTypeFilter, null, Tail.RPARENTH)) + } + + private fun calculateForInOperatorArgument(expressionWithType: JetExpression): Collection? { + val binaryExpression = expressionWithType.parent as? JetBinaryExpression ?: return null + val operationToken = binaryExpression.operationToken + if (operationToken != JetTokens.IN_KEYWORD && operationToken != JetTokens.NOT_IN || expressionWithType != binaryExpression.right) return null + + val leftOperandType = binaryExpression.left?.let { bindingContext.getType(it) } ?: return null + val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!! + val detector = TypesWithContainsDetector(scope, leftOperandType, binaryExpression.project, moduleDescriptor) + + val byTypeFilter = object : ByTypeFilter { + override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? { + return if (detector.hasContains(descriptorType)) TypeSubstitutor.EMPTY else null + } + } + return listOf(ExpectedInfo(byTypeFilter, null, null)) + } + private fun getFromBindingContext(expressionWithType: JetExpression): Collection? { val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null return listOf(ExpectedInfo(expectedType, null, 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 8e3c266c762..dd6c8b3b390 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 @@ -28,11 +28,10 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.completion.ExpectedInfos +import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.idea.util.application.executeCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand -import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -81,7 +80,11 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.PARTIAL) val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, resolutionFacade.findModuleDescriptor(file), useHeuristicSignatures = false) .calculate(expression) ?: return false - val functionTypes = expectedInfos.map { it.fuzzyType.type }.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }.toSet() + val functionTypes = expectedInfos + .map { it.fuzzyType?.type } + .filterNotNull() + .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 a4e372bd5a1..a14b2ee4495 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 @@ -16,14 +16,18 @@ package org.jetbrains.kotlin.idea.completion.smart -import com.intellij.codeInsight.lookup.LookupElement -import org.jetbrains.kotlin.idea.completion.ExpectedInfo -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import com.intellij.codeInsight.lookup.LookupElementBuilder -import org.jetbrains.kotlin.psi.* -import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.codeInsight.lookup.LookupElementDecorator +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.idea.completion.ExpectedInfo +import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetWhenConditionWithExpression +import org.jetbrains.kotlin.psi.JetWhenEntry +import org.jetbrains.kotlin.psi.JetWhenExpression import org.jetbrains.kotlin.types.TypeSubstitutor object KeywordValues { @@ -50,13 +54,18 @@ object KeywordValues { if (!skipTrueFalse) { val booleanInfoClassifier = { info: ExpectedInfo -> - if (info.fuzzyType.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.fuzzyType.type.isMarkedNullable()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches } + val classifier = { info: ExpectedInfo -> + if (info.fuzzyType != null && 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 605289de89d..1fc83c480eb 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 @@ -21,10 +21,11 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.completion.ExpectedInfo +import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.completion.handlers.buildLambdaPresentation import org.jetbrains.kotlin.idea.completion.handlers.insertLambdaTemplate import org.jetbrains.kotlin.idea.completion.suppressAutoInsertion -import java.util.* +import java.util.ArrayList object LambdaItems { public fun collect(functionExpectedInfos: Collection): Collection { @@ -34,7 +35,10 @@ object LambdaItems { } public fun addToCollection(collection: MutableCollection, functionExpectedInfos: Collection) { - val distinctTypes = functionExpectedInfos.map { it.fuzzyType.type }.toSet() + val distinctTypes = functionExpectedInfos + .map { it.fuzzyType?.type } + .filterNotNull() + .toSet() val singleType = if (distinctTypes.size() == 1) distinctTypes.single() else null val singleSignatureLength = singleType?.let { KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(it).size() } @@ -60,7 +64,7 @@ object LambdaItems { }) .suppressAutoInsertion() .assignSmartCompletionPriority(SmartCompletionItemPriority.LAMBDA) - .addTailAndNameSimilarity(functionExpectedInfos.filter { it.fuzzyType.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 265ed4e21ad..f44982e6f37 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 @@ -29,9 +29,11 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade 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.* +import org.jetbrains.kotlin.idea.util.FuzzyType +import org.jetbrains.kotlin.idea.util.fuzzyReturnType +import org.jetbrains.kotlin.idea.util.isAlmostEverything +import org.jetbrains.kotlin.idea.util.makeNullable import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression @@ -39,7 +41,6 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import java.util.ArrayList @@ -61,7 +62,6 @@ class SmartCompletion( val toFromOriginalFileMapper: ToFromOriginalFileMapper, val lookupElementFactory: LookupElementFactory ) { - private val project = expression.getProject() private val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null public class Result( @@ -118,28 +118,21 @@ class SmartCompletion( expression } - val loopRangePositionResult = buildForLoopRangePosition(expressionWithType, receiver) - if (loopRangePositionResult != null) return loopRangePositionResult - - val inOperatorArgumentResult = buildForInOperatorArgument(expressionWithType, receiver) - if (inOperatorArgumentResult != null) return inOperatorArgumentResult - - val allExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null - val filteredExpectedInfos = allExpectedInfos.filter { !it.fuzzyType.type.isError() } - if (filteredExpectedInfos.isEmpty()) return null + var originalExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null + originalExpectedInfos = originalExpectedInfos.filterNot { it.fuzzyType?.type?.isError ?: false } // 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.fuzzyType.makeNullable(), it.expectedName, it.tail) } + originalExpectedInfos.map { if (it.fuzzyType != null) ExpectedInfo(it.fuzzyType!!.makeNullable(), it.expectedName, it.tail) else it } else - filteredExpectedInfos + originalExpectedInfos val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator(bindingContext, moduleDescriptor).calculate(expressionWithType, receiver) val itemsToSkip = calcItemsToSkip(expressionWithType) - val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType.type) } + val functionExpectedInfos = expectedInfos.filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) } fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { if (descriptor in itemsToSkip) return listOf() @@ -179,7 +172,7 @@ class SmartCompletion( LambdaItems.addToCollection(additionalItems, functionExpectedInfos) - KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expression) + KeywordValues.addToCollection(additionalItems, originalExpectedInfos/* use originalExpectedInfos to not include null after == */, expression) MultipleArgumentsItemProvider(bindingContext, smartCastTypes).addToCollection(additionalItems, expectedInfos, expression) } @@ -246,7 +239,7 @@ class SmartCompletion( while (true) { val infos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useOuterCallsExpectedTypeCount = count) .calculate(expression) ?: return null - if (count == 2 /* use two outer calls maximum */ || infos.none { it.fuzzyType.isAlmostEverything() }) return infos + if (count == 2 /* use two outer calls maximum */ || infos.none { it.fuzzyType?.isAlmostEverything() ?: false }) return infos count++ } //TODO: we could always give higher priority to results with outer call expected type used @@ -319,7 +312,7 @@ class SmartCompletion( fun toLookupElement(descriptor: FunctionDescriptor): LookupElement? { val functionType = functionType(descriptor) ?: return null - val matchedExpectedInfos = functionExpectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(functionType) != null } + val matchedExpectedInfos = functionExpectedInfos.filter { it.matchingSubstitutor(functionType) != null } if (matchedExpectedInfos.isEmpty()) return null var lookupElement = lookupElementFactory.createLookupElement(descriptor, bindingContext, true) @@ -367,78 +360,17 @@ 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.fuzzyType.type.makeNotNullable() } + val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType?.type?.makeNotNullable() } val items = ArrayList() for ((type, infos) in expectedInfosGrouped) { + if (type == null) continue val lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue items.add(lookupElement.addTailAndNameSimilarity(infos)) } return Result(null, items, null) } - private fun buildForLoopRangePosition(expressionWithType: JetExpression, receiver: JetExpression?): Result? { - val forExpression = (expressionWithType.getParent() as? JetContainerNode) - ?.getParent() as? JetForExpression ?: return null - if (expressionWithType != forExpression.getLoopRange()) return null - - val loopVar = forExpression.getLoopParameter() - val loopVarType = if (loopVar != null && loopVar.getTypeReference() != null) { - val type = (resolutionFacade.resolveToDescriptor(loopVar) as VariableDescriptor).getType() - if (type.isError()) null else type - } - else { - null - } - - val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!! - val iterableDetector = IterableTypesDetector(project, moduleDescriptor, scope) - - return buildResultByTypeFilter(expressionWithType, receiver, Tail.RPARENTH) { iterableDetector.isIterable(it, loopVarType) } - } - - private fun buildForInOperatorArgument(expressionWithType: JetExpression, receiver: JetExpression?): Result? { - val binaryExpression = expressionWithType.getParent() as? JetBinaryExpression ?: return null - val operationToken = binaryExpression.getOperationToken() - if (operationToken != JetTokens.IN_KEYWORD && operationToken != JetTokens.NOT_IN || expressionWithType != binaryExpression.getRight()) return null - - val leftOperandType = binaryExpression.getLeft()?.let { bindingContext.getType(it) } ?: return null - val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!! - val detector = TypesWithContainsDetector(scope, leftOperandType, project, moduleDescriptor) - - return buildResultByTypeFilter(expressionWithType, receiver, null) { detector.hasContains(it) } - } - - private fun buildResultByTypeFilter( - position: JetExpression, - receiver: JetExpression?, - tail: Tail?, - typeFilter: (FuzzyType) -> Boolean - ): Result { - val smartCastTypes: (VariableDescriptor) -> Collection = SmartCastCalculator( - bindingContext, moduleDescriptor).calculate(position, receiver) - - fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { - val types = descriptor.fuzzyTypes(smartCastTypes) - - fun createLookupElement(): LookupElement { - return lookupElementFactory.createLookupElement(descriptor, bindingContext, true) - } - - if (types.any { typeFilter(it) }) { - return listOf(createLookupElement().addTail(tail)) - } - - if (types.any { it.nullability() == TypeNullability.NULLABLE && typeFilter(it.makeNotNullable()) }) { - return lookupElementsForNullable(::createLookupElement).map { it.addTail(tail) } - } - - return listOf() - } - - return Result(::filterDeclaration, listOf(), null) - } - companion object { public val OLD_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("nonFunctionReplacementOffset") public val MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("multipleArgumentsReplacementOffset") 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 87c59cdaac3..d660439ac83 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 @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.completion.ExpectedInfo import org.jetbrains.kotlin.idea.completion.LookupElementFactory +import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.completion.shortenReferences import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers @@ -46,7 +47,9 @@ class StaticMembers( context: JetSimpleNameExpression, enumEntriesToSkip: Set) { - val expectedInfosByClass = expectedInfos.groupBy { TypeUtils.getClassDescriptor(it.fuzzyType.type) } + val expectedInfosByClass = expectedInfos.groupBy { + expectedInfo -> expectedInfo.fuzzyType?.type?.let { TypeUtils.getClassDescriptor(it) } + } 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 3f1a678838a..3df7150d10a 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 @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMethodsHandler import org.jetbrains.kotlin.idea.core.psiClassToDescriptor import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.JetClassOrObject @@ -48,6 +47,7 @@ import org.jetbrains.kotlin.resolve.PossiblyBareType import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.utils.addIfNotNull class TypeInstantiationItems( @@ -64,8 +64,9 @@ class TypeInstantiationItems( inheritanceSearchers: MutableCollection, expectedInfos: Collection ) { - val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() } + val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType?.type?.makeNotNullable() } for ((type, infos) in expectedInfosGrouped) { + if (type == null) continue 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 7c187c7e574..2bb00b81c1f 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 @@ -133,13 +133,13 @@ private constructor( fun Collection.classifyExpectedInfo(expectedInfo: ExpectedInfo): ExpectedInfoClassification { val sequence = asSequence() - val substitutor = sequence.map { it.checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull() + val substitutor = sequence.map { expectedInfo.matchingSubstitutor(it) }.firstOrNull() if (substitutor != null) { return ExpectedInfoClassification.matches(substitutor) } if (sequence.any { it.nullability() == TypeNullability.NULLABLE }) { - val substitutor2 = sequence.map { it.makeNotNullable().checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull() + val substitutor2 = sequence.map { expectedInfo.matchingSubstitutor(it.makeNotNullable()) }.firstOrNull() if (substitutor2 != null) { return ExpectedInfoClassification.matchesIfNotNullable(substitutor2) }