diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 05653359631..df387d2ca5a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -56,11 +56,11 @@ public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? { * ([[JetQualifiedExpression]] or [[JetUserType]] or original expression) */ public fun JetSimpleNameExpression.getQualifiedElement(): JetElement { - val baseExpression: JetElement = (getParent() as? JetCallExpression) ?: this - val parent = baseExpression.getParent() + val baseExpression = (parent as? JetCallExpression) ?: this + val parent = baseExpression.parent return when (parent) { - is JetQualifiedExpression -> if (parent.getSelectorExpression().isAncestor(baseExpression)) parent else baseExpression - is JetUserType -> if (parent.getReferenceExpression().isAncestor(baseExpression)) parent else baseExpression + is JetQualifiedExpression -> if (parent.selectorExpression == baseExpression) parent else baseExpression + is JetUserType -> if (parent.referenceExpression == baseExpression) parent else baseExpression else -> baseExpression } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 39a3a766442..d437a5c1754 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.codeInsight.completion.CompletionSorter import com.intellij.codeInsight.completion.CompletionType import com.intellij.patterns.PatternCondition import com.intellij.patterns.StandardPatterns @@ -28,17 +29,16 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.core.SmartCastCalculator import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.isAncestor -import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.utils.addToStdlib.check class BasicCompletionSession(configuration: CompletionSessionConfiguration, parameters: CompletionParameters, @@ -274,6 +274,26 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, parameterNameAndTypeCompletion?.addFromAllClasses(parameters, indicesHelper) } + override fun createSorter(): CompletionSorter { + var sorter = super.createSorter() + + if (shouldCompleteParameterNameAndType()) { + sorter = sorter.weighBefore(DeprecatedWeigher.toString(), ParameterNameAndTypeCompletion.Weigher) + } + + val expectedInfos = nameExpression + ?.check { completionKind == CompletionKind.ALL } + ?.let { it.getQualifiedElement() as? JetExpression } + ?.let { ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor).calculate(it) } + + if (expectedInfos != null && expectedInfos.isNotEmpty()) { + val smartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, nameExpression!!) + sorter = sorter.weighBefore(KindWeigher.toString(), ExpectedInfoMatchWeigher(expectedInfos, smartCastCalculator)) + } + + return sorter + } + private companion object { object NonAnnotationClassifierExclude : DescriptorKindExclude { override fun excludes(descriptor: DeclarationDescriptor): Boolean { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 409156c4eca..29dbae4b613 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.idea.completion -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.completion.CompletionUtil -import com.intellij.codeInsight.completion.PrefixMatcher +import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile @@ -167,7 +164,10 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC else LookupElementsCollector.Context.NORMAL - protected val collector: LookupElementsCollector = LookupElementsCollector(prefixMatcher, parameters, resultSet, resolutionFacade, lookupElementFactory, inDescriptor, collectorContext) + // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes + protected val collector: LookupElementsCollector by lazy { + LookupElementsCollector(prefixMatcher, parameters, resultSet, resolutionFacade, lookupElementFactory, createSorter(), inDescriptor, collectorContext) + } protected val originalSearchScope: GlobalSearchScope = ResolutionFacade.getResolveScope(parameters.getOriginalFile() as JetFile) @@ -217,6 +217,18 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected abstract val descriptorKindFilter: DescriptorKindFilter? + protected open fun createSorter(): CompletionSorter { + var sorter = CompletionSorter.defaultSorter(parameters, prefixMatcher)!! + + sorter = sorter.weighBefore("stats", DeprecatedWeigher, PriorityWeigher, KindWeigher) + + sorter = sorter.weighAfter("stats", DeclarationRemotenessWeigher(parameters.originalFile as JetFile)) + + sorter = sorter.weighBefore("middleMatching", PreferMatchingItemWeigher) + + return sorter + } + protected val referenceVariants: Collection by lazy { if (descriptorKindFilter != null) { referenceVariantsHelper.getReferenceVariants( diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt index 901ef836a11..d962b87fa6e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt @@ -39,6 +39,7 @@ class LookupElementsCollector( resultSet: CompletionResultSet, private val resolutionFacade: ResolutionFacade, private val lookupElementFactory: LookupElementFactory, + private val sorter: CompletionSorter, private val inDescriptor: DeclarationDescriptor, private val context: LookupElementsCollector.Context ) { @@ -52,7 +53,7 @@ class LookupElementsCollector( private val defaultResultSet = resultSet .withPrefixMatcher(defaultPrefixMatcher) - .addKotlinSorting(completionParameters) + .withRelevanceSorter(sorter) private val postProcessors = ArrayList<(LookupElement) -> LookupElement>() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/SmartCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/SmartCompletionSession.kt index 5db1a35e1ed..27a3911372b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/SmartCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/SmartCompletionSession.kt @@ -18,10 +18,12 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.codeInsight.completion.CompletionResultSet +import com.intellij.codeInsight.completion.CompletionSorter import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper import org.jetbrains.kotlin.idea.completion.smart.LambdaItems +import org.jetbrains.kotlin.idea.completion.smart.NameSimilarityWeigher import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptorKindExclude @@ -126,4 +128,9 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para getTopLevelCallables().forEach(processor) } } + + override fun createSorter(): CompletionSorter { + return super.createSorter() + .weighBefore(KindWeigher.toString(), NameSimilarityWeigher, SmartCompletionPriorityWeigher) + } } \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSorting.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt similarity index 78% rename from idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSorting.kt rename to idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt index 0342cb014d0..c286648db8b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSorting.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt @@ -16,53 +16,37 @@ package org.jetbrains.kotlin.idea.completion -import com.intellij.codeInsight.completion.CompletionParameters -import com.intellij.codeInsight.completion.CompletionResultSet -import com.intellij.codeInsight.completion.CompletionSorter -import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementWeigher import com.intellij.codeInsight.lookup.WeighingContext import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.idea.completion.smart.NameSimilarityWeigher import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority +import org.jetbrains.kotlin.idea.completion.smart.fuzzyTypesForSmartCompletion +import org.jetbrains.kotlin.idea.core.SmartCastCalculator import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.util.ImportInsertHelper +import org.jetbrains.kotlin.idea.util.makeNotNullable +import org.jetbrains.kotlin.idea.util.nullability import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.types.typeUtil.TypeNullability import java.util.HashSet -public fun CompletionResultSet.addKotlinSorting(parameters: CompletionParameters): CompletionResultSet { - var sorter = CompletionSorter.defaultSorter(parameters, getPrefixMatcher())!! - - sorter = sorter.weighBefore("stats", ParameterNameAndTypeCompletion.Weigher, DeprecatedWeigher, PriorityWeigher, KindWeigher) - - if (parameters.getCompletionType() == CompletionType.SMART) { - sorter = sorter.weighBefore("kotlin.kind", NameSimilarityWeigher, SmartCompletionPriorityWeigher) - } - - sorter = sorter.weighAfter("stats", DeclarationRemotenessWeigher(parameters.getOriginalFile() as JetFile)) - - sorter = sorter.weighBefore("middleMatching", PreferMatchingItemWeigher) - - return withRelevanceSorter(sorter) -} - -private object PriorityWeigher : LookupElementWeigher("kotlin.priority") { +object PriorityWeigher : LookupElementWeigher("kotlin.priority") { override fun weigh(element: LookupElement, context: WeighingContext) = element.getUserData(ITEM_PRIORITY_KEY) ?: ItemPriority.DEFAULT } -private object SmartCompletionPriorityWeigher : LookupElementWeigher("kotlin.smartCompletionPriority") { +object SmartCompletionPriorityWeigher : LookupElementWeigher("kotlin.smartCompletionPriority") { override fun weigh(element: LookupElement, context: WeighingContext) = element.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) ?: SmartCompletionItemPriority.DEFAULT } -private object KindWeigher : LookupElementWeigher("kotlin.kind") { +object KindWeigher : LookupElementWeigher("kotlin.kind") { private enum class Weight { variable, // variable or property function, @@ -102,14 +86,14 @@ private object KindWeigher : LookupElementWeigher("kotlin.kind") { } } -private object DeprecatedWeigher : LookupElementWeigher("kotlin.deprecated") { +object DeprecatedWeigher : LookupElementWeigher("kotlin.deprecated") { override fun weigh(element: LookupElement): Int { val o = element.getObject() as? DeclarationLookupObject ?: return 0 return if (o.isDeprecated) 1 else 0 } } -private object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching", false, true) { +object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMatching", false, true) { private enum class Weight { keywordExactMatch, defaultExactMatch, @@ -133,7 +117,7 @@ private object PreferMatchingItemWeigher : LookupElementWeigher("kotlin.preferMa } } -private class DeclarationRemotenessWeigher(private val file: JetFile) : LookupElementWeigher("kotlin.declarationRemoteness") { +class DeclarationRemotenessWeigher(private val file: JetFile) : LookupElementWeigher("kotlin.declarationRemoteness") { private val importCache = ImportCache() private enum class Weight { @@ -203,3 +187,26 @@ private class DeclarationRemotenessWeigher(private val file: JetFile) : LookupEl fun hasPreciseImportFromPackage(packageName: FqName) = packageName in preciseImportPackages } } + +//TODO: prefer by name too (and only by name too) +class ExpectedInfoMatchWeigher( + private val expectedInfos: Collection, + private val smartCastCalculator: SmartCastCalculator +) : LookupElementWeigher("kotlin.expectedInfoMatch") { + private enum class Weight { + matches, + matchesIfNotNull, + doesNotMatch + } + + override fun weigh(element: LookupElement): Weight { + // TODO: keywords with type, this etc + val descriptor = (element.`object` as? DeclarationLookupObject)?.descriptor ?: return Weight.doesNotMatch + val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) + return when { + types.any { type -> expectedInfos.any { it.matchingSubstitutor(type) != null } } -> Weight.matches + types.any { type -> type.nullability() == TypeNullability.NULLABLE && expectedInfos.any { it.matchingSubstitutor(type.makeNotNullable()) != null } } -> Weight.matchesIfNotNull + else -> Weight.doesNotMatch + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt index 2e678d7afcb..70480736fa2 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt @@ -24,18 +24,18 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.JetDescriptorIconProvider import org.jetbrains.kotlin.idea.completion.* +import org.jetbrains.kotlin.idea.core.SmartCastCalculator import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.checker.JetTypeChecker import java.util.ArrayList import java.util.HashSet class MultipleArgumentsItemProvider(val bindingContext: BindingContext, - val smartCastTypes: (VariableDescriptor) -> Collection) { + val smartCastCalculator: SmartCastCalculator) { public fun addToCollection(collection: MutableCollection, expectedInfos: Collection, @@ -91,7 +91,7 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext, val name = parameter.getName() //TODO: there can be more than one property with such name in scope and we should be able to select one (but we need API for this) val variable = scope.getLocalVariable(name) ?: scope.getProperties(name).singleOrNull() ?: return null - return if (smartCastTypes(variable).any { JetTypeChecker.DEFAULT.isSubtypeOf(it, parameter.getType()) }) + return if (smartCastCalculator(variable).any { JetTypeChecker.DEFAULT.isSubtypeOf(it, parameter.getType()) }) variable else null 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 f44982e6f37..04458aa6f07 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 @@ -24,14 +24,13 @@ import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.psi.search.GlobalSearchScope +import com.intellij.util.SmartList import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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.SmartCastCalculator -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 @@ -41,7 +40,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.isNothing import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import java.util.ArrayList import java.util.HashSet @@ -127,18 +125,18 @@ class SmartCompletion( else originalExpectedInfos - val smartCastTypes: (VariableDescriptor) -> Collection - = SmartCastCalculator(bindingContext, moduleDescriptor).calculate(expressionWithType, receiver) + val smartCastCalculator = (expression as? JetSimpleNameExpression)?.let { SmartCastCalculator(bindingContext, moduleDescriptor, it) } val itemsToSkip = calcItemsToSkip(expressionWithType) val functionExpectedInfos = expectedInfos.filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) } fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { - if (descriptor in itemsToSkip) return listOf() + if (smartCastCalculator == null) return emptyList() // only happens for this@ completion + if (descriptor in itemsToSkip) return emptyList() - val result = ArrayList() - val types = descriptor.fuzzyTypes(smartCastTypes) + val result = SmartList() + val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) } result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor -> @@ -174,7 +172,9 @@ class SmartCompletion( KeywordValues.addToCollection(additionalItems, originalExpectedInfos/* use originalExpectedInfos to not include null after == */, expression) - MultipleArgumentsItemProvider(bindingContext, smartCastTypes).addToCollection(additionalItems, expectedInfos, expression) + if (smartCastCalculator != null) { + MultipleArgumentsItemProvider(bindingContext, smartCastCalculator).addToCollection(additionalItems, expectedInfos, expression) + } } val inheritanceSearcher = if (inheritanceSearchers.isNotEmpty()) @@ -200,28 +200,6 @@ class SmartCompletion( } } - private fun DeclarationDescriptor.fuzzyTypes(smartCastTypes: (VariableDescriptor) -> Collection): Collection { - if (this is CallableDescriptor) { - var returnType = fuzzyReturnType() ?: return listOf() - // skip declarations of type Nothing or of generic parameter type which has no real bounds - //TODO: maybe we should include them on the second press? - if (returnType.type.isNothing() || returnType.isAlmostEverything()) return listOf() - - if (this is VariableDescriptor) { - return smartCastTypes(this).map { FuzzyType(it, listOf()) } - } - else { - return listOf(fuzzyReturnType()!!) - } - } - else if (this is ClassDescriptor && getKind().isSingleton()) { - return listOf(FuzzyType(getDefaultType(), listOf())) - } - else { - return listOf() - } - } - private fun calcExpectedInfos(expression: JetExpression): Collection? { // if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function) val declaration = implicitlyTypedDeclarationFromInitializer(expression) 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 2bb00b81c1f..9f12914b7df 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 @@ -27,14 +27,14 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler -import org.jetbrains.kotlin.idea.util.FuzzyType -import org.jetbrains.kotlin.idea.util.makeNotNullable -import org.jetbrains.kotlin.idea.util.nullability +import org.jetbrains.kotlin.idea.core.SmartCastCalculator +import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.typeUtil.TypeNullability +import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import java.util.ArrayList import java.util.HashMap @@ -318,3 +318,24 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority) return this } + +fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(smartCastCalculator: SmartCastCalculator): Collection { + if (this is CallableDescriptor) { + var returnType = fuzzyReturnType() ?: return emptyList() + // skip declarations of type Nothing or of generic parameter type which has no real bounds + if (returnType.type.isNothing() || returnType.isAlmostEverything()) return emptyList() + + if (this is VariableDescriptor) { //TODO: generic properties! + return smartCastCalculator(this).map { FuzzyType(it, emptyList()) } + } + else { + return listOf(returnType) + } + } + else if (this is ClassDescriptor && kind.isSingleton) { + return listOf(FuzzyType(defaultType, emptyList())) + } + else { + return emptyList() + } +} diff --git a/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType.kt b/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType.kt new file mode 100644 index 00000000000..62a0f4787bd --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType.kt @@ -0,0 +1,7 @@ +fun foo(p1: Any, p2: Any?, p3: String?): String { + if (p2 is String) return p +} + +// ORDER: p2 +// ORDER: p3 +// ORDER: p1 diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java index f5f4aed2f15..f94047b4992 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java @@ -143,6 +143,21 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion doTest(fileName); } + @TestMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ExpectedInfo extends AbstractBasicCompletionWeigherTest { + public void testAllFilesPresentInExpectedInfo() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/expectedInfo"), Pattern.compile("^([^\\.]+)\\.kt$"), true); + } + + @TestMetadata("ExpectedType.kt") + public void testExpectedType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/idea-completion/testData/weighers/basic/parameterNameAndType") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt index 057f44d30db..0772e3e4644 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/SmartCastCalculator.kt @@ -16,29 +16,40 @@ package org.jetbrains.kotlin.idea.core -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue -import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import com.google.common.collect.SetMultimap -import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability -import java.util.Collections -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import java.util.HashMap -import java.util.HashSet import com.intellij.openapi.util.Pair import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.psi.JetExpression +import org.jetbrains.kotlin.psi.JetSimpleNameExpression +import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver +import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.typeUtil.makeNotNullable -import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext import org.jetbrains.kotlin.utils.singletonOrEmptyList +import java.util.Collections +import java.util.HashMap +import java.util.HashSet -class SmartCastCalculator(val bindingContext: BindingContext, val containingDeclarationOrModule: DeclarationDescriptor) { - public fun calculate(position: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection { - val dataFlowInfo = bindingContext.getDataFlowInfo(position) +class SmartCastCalculator( + val bindingContext: BindingContext, + val containingDeclarationOrModule: DeclarationDescriptor, + nameExpression: JetSimpleNameExpression +): (VariableDescriptor) -> Collection { + + override fun invoke(descriptor: VariableDescriptor): Collection { + return function(descriptor) + } + + private val function: (VariableDescriptor) -> Collection = run { + val receiver = nameExpression.getReceiverExpression() + val dataFlowInfo = bindingContext.getDataFlowInfo(nameExpression) val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver) fun typesOf(descriptor: VariableDescriptor): Collection { @@ -52,7 +63,7 @@ class SmartCastCalculator(val bindingContext: BindingContext, val containingDecl return smartCastTypes + type.singletonOrEmptyList() } - return ::typesOf + ::typesOf } private data class ProcessDataFlowInfoResult(