Refactored smart completion so that completion in for-loop range and after "in" are covered by ExpectedInfo's

This commit is contained in:
Valentin Kipyatkov
2015-08-04 14:11:39 +03:00
parent 89e0d310e3
commit 99f604f1c3
8 changed files with 127 additions and 109 deletions
@@ -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<ExpectedInfo>? {
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<ExpectedInfo>? {
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<ExpectedInfo>? {
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<ExpectedInfo>? {
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
return listOf(ExpectedInfo(expectedType, null, null))
@@ -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()
@@ -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)
}
@@ -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<ExpectedInfo>): Collection<LookupElement> {
@@ -34,7 +35,10 @@ object LambdaItems {
}
public fun addToCollection(collection: MutableCollection<LookupElement>, functionExpectedInfos: Collection<ExpectedInfo>) {
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)
}
}
@@ -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<JetType>
= 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<LookupElement> {
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<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
val expectedInfosGrouped: Map<JetType?, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType?.type?.makeNotNullable() }
val items = ArrayList<LookupElement>()
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<JetType> = SmartCastCalculator(
bindingContext, moduleDescriptor).calculate(position, receiver)
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
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")
@@ -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<DeclarationDescriptor>) {
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)
@@ -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<InheritanceItemsSearcher>,
expectedInfos: Collection<ExpectedInfo>
) {
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
val expectedInfosGrouped: Map<JetType?, List<ExpectedInfo>> = 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)
}
@@ -133,13 +133,13 @@ private constructor(
fun Collection<FuzzyType>.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)
}