Smart completion: generic functions are correctly included into the list
#KT-6375 Fixed
This commit is contained in:
+1
@@ -42,6 +42,7 @@ public class LocalVariableDescriptor extends VariableDescriptorImpl {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public LocalVariableDescriptor substitute(@NotNull TypeSubstitutor substitutor) {
|
public LocalVariableDescriptor substitute(@NotNull TypeSubstitutor substitutor) {
|
||||||
|
if (substitutor.isEmpty()) return this;
|
||||||
throw new UnsupportedOperationException(); // TODO
|
throw new UnsupportedOperationException(); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -108,6 +108,7 @@ public class ValueParameterDescriptorImpl extends VariableDescriptorImpl impleme
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public ValueParameterDescriptor substitute(@NotNull TypeSubstitutor substitutor) {
|
public ValueParameterDescriptor substitute(@NotNull TypeSubstitutor substitutor) {
|
||||||
|
if (substitutor.isEmpty()) return this;
|
||||||
throw new UnsupportedOperationException(); // TODO
|
throw new UnsupportedOperationException(); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.types.JetType
|
|||||||
import org.jetbrains.jet.lang.types.Flexibility
|
import org.jetbrains.jet.lang.types.Flexibility
|
||||||
import org.jetbrains.jet.lang.types.TypeConstructor
|
import org.jetbrains.jet.lang.types.TypeConstructor
|
||||||
import org.jetbrains.jet.utils.toReadOnlyList
|
import org.jetbrains.jet.utils.toReadOnlyList
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||||
|
|
||||||
fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
|
||||||
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
|
||||||
@@ -59,3 +60,6 @@ public fun JetType.getContainedAndCapturedTypeParameterConstructors(): Collectio
|
|||||||
return typeParameters.map { it.getTypeConstructor() }.toReadOnlyList()
|
return typeParameters.map { it.getTypeConstructor() }.toReadOnlyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun JetType.isSubtypeOf(superType: JetType): Boolean = JetTypeChecker.DEFAULT.isSubtypeOf(this, superType)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
|
|||||||
import com.intellij.codeInsight.completion.InsertionContext
|
import com.intellij.codeInsight.completion.InsertionContext
|
||||||
import org.jetbrains.jet.plugin.completion.handlers.CastReceiverInsertHandler
|
import org.jetbrains.jet.plugin.completion.handlers.CastReceiverInsertHandler
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor
|
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||||
|
|
||||||
enum class ItemPriority {
|
enum class ItemPriority {
|
||||||
MULTIPLE_ARGUMENTS_ITEM
|
MULTIPLE_ARGUMENTS_ITEM
|
||||||
@@ -113,3 +114,23 @@ enum class CallableWeight {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
|
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
|
||||||
|
|
||||||
|
fun descriptorsEqualWithSubstitution(descriptor1: DeclarationDescriptor?, descriptor2: DeclarationDescriptor?): Boolean {
|
||||||
|
if (descriptor1 == descriptor2) return true
|
||||||
|
if (descriptor1 == null || descriptor2 == null) return false
|
||||||
|
if (descriptor1.getOriginal() != descriptor2.getOriginal()) return false
|
||||||
|
if (descriptor1 !is CallableDescriptor) return true
|
||||||
|
descriptor2 as CallableDescriptor
|
||||||
|
|
||||||
|
// optimization:
|
||||||
|
if (descriptor1 == descriptor1.getOriginal() && descriptor2 == descriptor2.getOriginal()) return true
|
||||||
|
|
||||||
|
if (descriptor1.getReturnType() != descriptor2.getReturnType()) return false
|
||||||
|
val parameters1 = descriptor1.getValueParameters()
|
||||||
|
val parameters2 = descriptor2.getValueParameters()
|
||||||
|
if (parameters1.size() != parameters2.size()) return false
|
||||||
|
for (i in parameters1.indices) {
|
||||||
|
if (parameters1[i].getType() != parameters2[i].getType()) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.diagnostic.Logger
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores information about resolved descriptor and position of that descriptor.
|
* Stores information about resolved descriptor and position of that descriptor.
|
||||||
@@ -50,22 +49,7 @@ public class DeclarationDescriptorLookupObject(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lookupObject.descriptor.getOriginal() != descriptor.getOriginal()) return false
|
return descriptorsEqualWithSubstitution(descriptor, lookupObject.descriptor)
|
||||||
if (descriptor !is CallableDescriptor) return true
|
|
||||||
// optimization:
|
|
||||||
if (descriptor == (descriptor as CallableDescriptor).getOriginal() && lookupObject.descriptor == lookupObject.descriptor.getOriginal()) return true
|
|
||||||
return substitutionsEqual(descriptor as CallableDescriptor, lookupObject.descriptor as CallableDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun substitutionsEqual(callable1: CallableDescriptor, callable2: CallableDescriptor): Boolean {
|
|
||||||
if (callable1.getReturnType() != callable2.getReturnType()) return false
|
|
||||||
val parameters1 = callable1.getValueParameters()
|
|
||||||
val parameters2 = callable2.getValueParameters()
|
|
||||||
if (parameters1.size() != parameters2.size()) return false
|
|
||||||
for (i in parameters1.indices) {
|
|
||||||
if (parameters1[i].getType() != parameters2[i].getType()) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class object {
|
class object {
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import org.jetbrains.jet.lexer.JetTokens
|
|||||||
import org.jetbrains.jet.lang.psi.JetIfExpression
|
import org.jetbrains.jet.lang.psi.JetIfExpression
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
import org.jetbrains.jet.lang.psi.JetContainerNode
|
import org.jetbrains.jet.lang.psi.JetContainerNode
|
||||||
import org.jetbrains.jet.plugin.completion.smart.isSubtypeOf
|
|
||||||
import org.jetbrains.jet.lang.resolve.calls.callUtil.noErrorsInValueArguments
|
import org.jetbrains.jet.lang.resolve.calls.callUtil.noErrorsInValueArguments
|
||||||
import org.jetbrains.jet.lang.descriptors.Visibilities
|
import org.jetbrains.jet.lang.descriptors.Visibilities
|
||||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||||
@@ -68,6 +67,7 @@ import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor
|
|||||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus
|
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||||
|
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
enum class Tail {
|
enum class Tail {
|
||||||
COMMA
|
COMMA
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.plugin.completion.smart
|
||||||
|
|
||||||
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.jet.utils.addIfNotNull
|
||||||
|
import java.util.HashSet
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl
|
||||||
|
import java.util.LinkedHashMap
|
||||||
|
import org.jetbrains.jet.lang.types.Variance
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil
|
||||||
|
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||||
|
import org.jetbrains.jet.plugin.util.makeNullable
|
||||||
|
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||||
|
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
|
fun CallableDescriptor.fuzzyReturnType(): FuzzyType? {
|
||||||
|
val returnType = getReturnType() ?: return null
|
||||||
|
return FuzzyType(returnType, getTypeParameters())
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: replace code in extensionsUtils.kt with use of FuzzyType
|
||||||
|
fun CallableDescriptor.fuzzyExtensionReceiverType(): FuzzyType? {
|
||||||
|
val receiverParameter = getExtensionReceiverParameter()
|
||||||
|
return if (receiverParameter != null) FuzzyType(receiverParameter.getType(), getTypeParameters()) else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FuzzyType.makeNotNullable() = FuzzyType(type.makeNotNullable(), freeParameters)
|
||||||
|
fun FuzzyType.makeNullable() = FuzzyType(type.makeNullable(), freeParameters)
|
||||||
|
fun FuzzyType.isNullable() = type.isNullable()
|
||||||
|
|
||||||
|
class FuzzyType(
|
||||||
|
val type: JetType,
|
||||||
|
val freeParameters: Collection<TypeParameterDescriptor>
|
||||||
|
) {
|
||||||
|
private val usedTypeParameters: HashSet<TypeParameterDescriptor>? = if (freeParameters.isNotEmpty()) HashSet() else null
|
||||||
|
|
||||||
|
;{
|
||||||
|
usedTypeParameters?.addUsedTypeParameters(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableSet<TypeParameterDescriptor>.addUsedTypeParameters(type: JetType) {
|
||||||
|
addIfNotNull(type.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor)
|
||||||
|
|
||||||
|
for (argument in type.getArguments()) {
|
||||||
|
addUsedTypeParameters(argument.getType())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun matchedSubstitutor(expectedType: JetType): TypeSubstitutor? {
|
||||||
|
if (type.isError()) return null
|
||||||
|
if (usedTypeParameters == null || usedTypeParameters.isEmpty()) {
|
||||||
|
return if (type.isSubtypeOf(expectedType)) TypeSubstitutor.EMPTY else null
|
||||||
|
}
|
||||||
|
|
||||||
|
val constraintSystem = ConstraintSystemImpl()
|
||||||
|
val typeVariables = LinkedHashMap<TypeParameterDescriptor, Variance>()
|
||||||
|
for (typeParameter in freeParameters) {
|
||||||
|
if (typeParameter in usedTypeParameters) {
|
||||||
|
typeVariables[typeParameter] = Variance.INVARIANT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constraintSystem.registerTypeVariables(typeVariables)
|
||||||
|
|
||||||
|
constraintSystem.addSubtypeConstraint(type, expectedType, ConstraintPosition.SPECIAL/*TODO?*/)
|
||||||
|
if (constraintSystem.getStatus().isSuccessful() && ConstraintsUtil.checkBoundsAreSatisfied(constraintSystem, true)) {
|
||||||
|
val substitutor = constraintSystem.getResultingSubstitutor()
|
||||||
|
val substitutedType = substitutor.substitute(type, Variance.INVARIANT/*TODO?*/)
|
||||||
|
return if (substitutedType.isSubtypeOf(expectedType)) substitutor else null
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.psi.*
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||||
import com.intellij.codeInsight.completion.InsertionContext
|
import com.intellij.codeInsight.completion.InsertionContext
|
||||||
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
||||||
|
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||||
|
|
||||||
object KeywordValues {
|
object KeywordValues {
|
||||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) {
|
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) {
|
||||||
@@ -48,14 +49,15 @@ object KeywordValues {
|
|||||||
|
|
||||||
if (!skipTrueFalse) {
|
if (!skipTrueFalse) {
|
||||||
val booleanInfoClassifier = { (info: ExpectedInfo) ->
|
val booleanInfoClassifier = { (info: ExpectedInfo) ->
|
||||||
if (info.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.MATCHES else ExpectedInfoClassification.NOT_MATCHES
|
if (info.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||||
}
|
}
|
||||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) })
|
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) })
|
||||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) })
|
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) })
|
||||||
}
|
}
|
||||||
|
|
||||||
collection.addLookupElements(expectedInfos,
|
collection.addLookupElements(null,
|
||||||
{ info -> if (info.type.isNullable()) ExpectedInfoClassification.MATCHES else ExpectedInfoClassification.NOT_MATCHES },
|
expectedInfos,
|
||||||
|
{ info -> if (info.type.isNullable()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches },
|
||||||
{ LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL) })
|
{ LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,13 +19,10 @@ package org.jetbrains.jet.plugin.completion.smart
|
|||||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
import com.intellij.codeInsight.lookup.LookupElement
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
|
||||||
import com.intellij.ui.LayeredIcon
|
import com.intellij.ui.LayeredIcon
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import org.jetbrains.jet.plugin.completion.Tail
|
import org.jetbrains.jet.plugin.completion.Tail
|
||||||
import org.jetbrains.jet.plugin.completion.ItemPriority
|
import org.jetbrains.jet.plugin.completion.ItemPriority
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
|
||||||
import org.jetbrains.jet.lang.types.JetType
|
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
import java.util.HashSet
|
import java.util.HashSet
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||||
@@ -37,9 +34,11 @@ import java.util.ArrayList
|
|||||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.jet.plugin.completion.assignPriority
|
import org.jetbrains.jet.plugin.completion.assignPriority
|
||||||
import com.intellij.codeInsight.lookup.Lookup
|
import com.intellij.codeInsight.lookup.Lookup
|
||||||
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||||
|
|
||||||
class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
|
class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
|
||||||
val typesWithAutoCasts: (DeclarationDescriptor) -> Iterable<JetType>) {
|
val smartCastTypes: (VariableDescriptor) -> Collection<JetType>) {
|
||||||
|
|
||||||
public fun addToCollection(collection: MutableCollection<LookupElement>,
|
public fun addToCollection(collection: MutableCollection<LookupElement>,
|
||||||
expectedInfos: Collection<ExpectedInfo>,
|
expectedInfos: Collection<ExpectedInfo>,
|
||||||
@@ -95,7 +94,7 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
|
|||||||
val name = parameter.getName()
|
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)
|
//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
|
val variable = scope.getLocalVariable(name) ?: scope.getProperties(name).singleOrNull() ?: return null
|
||||||
return if (typesWithAutoCasts(variable).any { JetTypeChecker.DEFAULT.isSubtypeOf(it, parameter.getType()) })
|
return if (smartCastTypes(variable).any { JetTypeChecker.DEFAULT.isSubtypeOf(it, parameter.getType()) })
|
||||||
variable
|
variable
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ import org.jetbrains.jet.plugin.completion.*
|
|||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||||
import org.jetbrains.jet.plugin.util.makeNullable
|
import org.jetbrains.jet.plugin.util.makeNullable
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.resolveToDescriptor
|
import org.jetbrains.jet.plugin.caches.resolve.resolveToDescriptor
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
trait InheritanceItemsSearcher {
|
trait InheritanceItemsSearcher {
|
||||||
fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit)
|
fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit)
|
||||||
@@ -115,30 +115,40 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
|||||||
else
|
else
|
||||||
filteredExpectedInfos
|
filteredExpectedInfos
|
||||||
|
|
||||||
val typesWithSmartCasts: (DeclarationDescriptor) -> Iterable<JetType> = TypesWithSmartCasts(bindingContext).calculate(expressionWithType, receiver)
|
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = TypesWithSmartCasts(bindingContext).calculate(expressionWithType, receiver)
|
||||||
|
|
||||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||||
|
|
||||||
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) }
|
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) }
|
||||||
|
|
||||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||||
|
if (descriptor in itemsToSkip) return listOf()
|
||||||
|
|
||||||
val result = ArrayList<LookupElement>()
|
val result = ArrayList<LookupElement>()
|
||||||
if (!itemsToSkip.contains(descriptor)) {
|
val types = descriptor.fuzzyTypes(smartCastTypes)
|
||||||
val types = typesWithSmartCasts(descriptor)
|
val classifier = { (expectedInfo: ExpectedInfo) ->
|
||||||
val nonNullTypes = types.map { it.makeNotNullable() }
|
val substitutor = types.stream().map { it.matchedSubstitutor(expectedInfo.type) }.firstOrNull()
|
||||||
val classifier = { (expectedInfo: ExpectedInfo) ->
|
if (substitutor != null) {
|
||||||
when {
|
ExpectedInfoClassification.matches(substitutor)
|
||||||
types.any { it.isSubtypeOf(expectedInfo.type) } -> ExpectedInfoClassification.MATCHES
|
}
|
||||||
nonNullTypes.any { it.isSubtypeOf(expectedInfo.type) } -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
|
else {
|
||||||
else -> ExpectedInfoClassification.NOT_MATCHES
|
val substitutor2 = types.stream().map { it.makeNotNullable().matchedSubstitutor(expectedInfo.type) }.firstOrNull()
|
||||||
|
if (substitutor2 != null) {
|
||||||
|
ExpectedInfoClassification.matchesIfNotNullable(substitutor2)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ExpectedInfoClassification.notMatches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.addLookupElements(expectedInfos, classifier, { lookupElementFactory.createLookupElement(descriptor, resolutionFacade, bindingContext, true) })
|
|
||||||
|
|
||||||
if (receiver == null) {
|
|
||||||
toFunctionReferenceLookupElement(descriptor, functionExpectedInfos)?.let { result.add(it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
result.addLookupElements(descriptor, expectedInfos, classifier) { descriptor ->
|
||||||
|
lookupElementFactory.createLookupElement(descriptor, resolutionFacade, bindingContext, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver == null) {
|
||||||
|
toFunctionReferenceLookupElement(descriptor, functionExpectedInfos)?.let { result.add(it) }
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +166,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
|||||||
|
|
||||||
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expressionWithType)
|
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expressionWithType)
|
||||||
|
|
||||||
MultipleArgumentsItemProvider(bindingContext, typesWithSmartCasts).addToCollection(additionalItems, expectedInfos, expression)
|
MultipleArgumentsItemProvider(bindingContext, smartCastTypes).addToCollection(additionalItems, expectedInfos, expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
val inheritanceSearcher = if (inheritanceSearchers.isNotEmpty())
|
val inheritanceSearcher = if (inheritanceSearchers.isNotEmpty())
|
||||||
@@ -170,6 +180,37 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
|||||||
return Result(::filterDeclaration, additionalItems, inheritanceSearcher)
|
return Result(::filterDeclaration, additionalItems, inheritanceSearcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun DeclarationDescriptor.fuzzyTypes(smartCastTypes: (VariableDescriptor) -> Collection<JetType>): Collection<FuzzyType> {
|
||||||
|
if (this is CallableDescriptor) {
|
||||||
|
var returnType = fuzzyReturnType() ?: return listOf()
|
||||||
|
//TODO: maybe we should include them on the second press?
|
||||||
|
if (shouldSkipDeclarationsOfType(returnType)) 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip declarations of type Nothing or of generic parameter type which has no real bounds
|
||||||
|
private fun shouldSkipDeclarationsOfType(type: FuzzyType): Boolean {
|
||||||
|
if (KotlinBuiltIns.getInstance().isNothing(type.type)) return true
|
||||||
|
if (type.freeParameters.isEmpty()) return false
|
||||||
|
val typeParameter = type.type.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor ?: return false
|
||||||
|
if (!type.freeParameters.contains(typeParameter)) return false
|
||||||
|
return KotlinBuiltIns.getInstance().isAnyOrNullableAny(typeParameter.getUpperBoundsAsType())
|
||||||
|
//TODO: check for class object constraint when there will be supported
|
||||||
|
}
|
||||||
|
|
||||||
private fun calcExpectedInfos(expression: JetExpression): Collection<ExpectedInfo>? {
|
private fun calcExpectedInfos(expression: JetExpression): Collection<ExpectedInfo>? {
|
||||||
// if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function)
|
// if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function)
|
||||||
val declaration = implicitlyTypedDeclarationFromInitializer(expression)
|
val declaration = implicitlyTypedDeclarationFromInitializer(expression)
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ import com.intellij.codeInsight.completion.InsertionContext
|
|||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
|
||||||
import org.jetbrains.jet.plugin.completion.qualifiedNameForSourceCode
|
import org.jetbrains.jet.plugin.completion.qualifiedNameForSourceCode
|
||||||
import org.jetbrains.jet.lang.resolve.descriptorUtil.isExtension
|
import org.jetbrains.jet.lang.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||||
import org.jetbrains.jet.plugin.completion.LookupElementFactory
|
import org.jetbrains.jet.plugin.completion.LookupElementFactory
|
||||||
|
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||||
|
|
||||||
// adds java static members, enum members and members from class object
|
// adds java static members, enum members and members from class object
|
||||||
class StaticMembers(
|
class StaticMembers(
|
||||||
@@ -68,24 +68,19 @@ class StaticMembers(
|
|||||||
|
|
||||||
val classifier: (ExpectedInfo) -> ExpectedInfoClassification
|
val classifier: (ExpectedInfo) -> ExpectedInfoClassification
|
||||||
if (descriptor is CallableDescriptor) {
|
if (descriptor is CallableDescriptor) {
|
||||||
val returnType = descriptor.getReturnType() ?: return
|
val returnType = descriptor.fuzzyReturnType() ?: return
|
||||||
classifier = {
|
classifier = { expectedInfo -> returnType.classifyExpectedInfo(expectedInfo) }
|
||||||
expectedInfo ->
|
|
||||||
when {
|
|
||||||
returnType.isSubtypeOf(expectedInfo.type) -> ExpectedInfoClassification.MATCHES
|
|
||||||
returnType.isNullable() && returnType.makeNotNullable().isSubtypeOf(expectedInfo.type) -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
|
|
||||||
else -> ExpectedInfoClassification.NOT_MATCHES
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (DescriptorUtils.isEnumEntry(descriptor) && !enumEntriesToSkip.contains(descriptor)) {
|
else if (DescriptorUtils.isEnumEntry(descriptor) && !enumEntriesToSkip.contains(descriptor)) {
|
||||||
classifier = { ExpectedInfoClassification.MATCHES } /* we do not need to check type of enum entry because it's taken from proper enum */
|
classifier = { ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) } /* we do not need to check type of enum entry because it's taken from proper enum */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
collection.addLookupElements(expectedInfos, classifier, { createLookupElement(descriptor, classDescriptor) })
|
collection.addLookupElements(descriptor, expectedInfos, classifier) {
|
||||||
|
descriptor -> createLookupElement(descriptor, classDescriptor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classDescriptor.getStaticScope().getAllDescriptors().forEach(::processMember)
|
classDescriptor.getStaticScope().getAllDescriptors().forEach(::processMember)
|
||||||
|
|||||||
@@ -28,27 +28,21 @@ import org.jetbrains.jet.lang.psi.JetCallExpression
|
|||||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
|
||||||
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||||
|
|
||||||
class ThisItems(val bindingContext: BindingContext) {
|
class ThisItems(val bindingContext: BindingContext) {
|
||||||
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
|
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
|
||||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return
|
||||||
if (scope == null) return
|
|
||||||
|
|
||||||
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
|
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
|
||||||
for (i in 0..receivers.size - 1) {
|
for (i in 0..receivers.size - 1) {
|
||||||
val receiver = receivers[i]
|
val receiver = receivers[i]
|
||||||
val thisType = receiver.getType()
|
val thisType = receiver.getType()
|
||||||
val classifier = { (expectedInfo: ExpectedInfo) ->
|
val fuzzyType = FuzzyType(thisType, listOf())
|
||||||
when {
|
val classifier = { (expectedInfo: ExpectedInfo) -> fuzzyType.classifyExpectedInfo(expectedInfo) }
|
||||||
thisType.isSubtypeOf(expectedInfo.type) -> ExpectedInfoClassification.MATCHES
|
fun createLookupElement(): LookupElement? {
|
||||||
thisType.isNullable() && thisType.makeNotNullable().isSubtypeOf(expectedInfo.type) -> ExpectedInfoClassification.MAKE_NOT_NULLABLE
|
|
||||||
else -> ExpectedInfoClassification.NOT_MATCHES
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun lookupElementFactory(): LookupElement? {
|
|
||||||
//TODO: use this code when KT-4258 fixed
|
//TODO: use this code when KT-4258 fixed
|
||||||
//val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: return null)
|
//val expressionText = if (i == 0) "this" else "this@" + (thisQualifierName(receiver, bindingContext) ?: return null)
|
||||||
val qualifier = if (i == 0) null else (thisQualifierName(receiver) ?: return null)
|
val qualifier = if (i == 0) null else (thisQualifierName(receiver) ?: return null)
|
||||||
@@ -57,7 +51,7 @@ class ThisItems(val bindingContext: BindingContext) {
|
|||||||
.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(thisType))
|
||||||
.assignSmartCompletionPriority(SmartCompletionItemPriority.THIS)
|
.assignSmartCompletionPriority(SmartCompletionItemPriority.THIS)
|
||||||
}
|
}
|
||||||
collection.addLookupElements(expectedInfos, classifier, ::lookupElementFactory)
|
collection.addLookupElements(null, expectedInfos, classifier) { createLookupElement() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import org.jetbrains.jet.lang.resolve.resolveTopLevelClass
|
|||||||
import org.jetbrains.jet.lang.types.TypeProjectionImpl
|
import org.jetbrains.jet.lang.types.TypeProjectionImpl
|
||||||
import org.jetbrains.jet.lang.types.Variance
|
import org.jetbrains.jet.lang.types.Variance
|
||||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||||
|
import org.jetbrains.jet.lang.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
class TypeInstantiationItems(
|
class TypeInstantiationItems(
|
||||||
val resolutionFacade: ResolutionFacade,
|
val resolutionFacade: ResolutionFacade,
|
||||||
|
|||||||
@@ -17,13 +17,8 @@
|
|||||||
package org.jetbrains.jet.plugin.completion.smart
|
package org.jetbrains.jet.plugin.completion.smart
|
||||||
|
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
|
||||||
import org.jetbrains.jet.lang.types.JetType
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo
|
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo
|
||||||
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowValue
|
||||||
@@ -34,40 +29,23 @@ import org.jetbrains.jet.lang.resolve.calls.smartcasts.Nullability
|
|||||||
import java.util.HashSet
|
import java.util.HashSet
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver
|
||||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
|
||||||
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
|
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||||
|
|
||||||
class TypesWithSmartCasts(val bindingContext: BindingContext) {
|
class TypesWithSmartCasts(val bindingContext: BindingContext) {
|
||||||
public fun calculate(expression: JetExpression, receiver: JetExpression?): (DeclarationDescriptor) -> Iterable<JetType> {
|
public fun calculate(expression: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection<JetType> {
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||||
val (variableToTypes: Map<VariableDescriptor, Collection<JetType>>, notNullVariables: Set<VariableDescriptor>)
|
val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver)
|
||||||
= processDataFlowInfo(dataFlowInfo, receiver)
|
|
||||||
|
|
||||||
fun typesOf(descriptor: DeclarationDescriptor): Iterable<JetType> {
|
fun typesOf(descriptor: VariableDescriptor): Collection<JetType> {
|
||||||
if (descriptor is CallableDescriptor) {
|
var type = descriptor.getReturnType() ?: return listOf()
|
||||||
var returnType = descriptor.getReturnType()
|
if (notNullVariables.contains(descriptor)) {
|
||||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType!!)) {
|
type = type.makeNotNullable()
|
||||||
//TODO: maybe we should include them on the second press?
|
}
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
if (descriptor is VariableDescriptor) {
|
|
||||||
if (notNullVariables.contains(descriptor)) {
|
|
||||||
returnType = returnType?.makeNotNullable()
|
|
||||||
}
|
|
||||||
|
|
||||||
val smartCastTypes = variableToTypes[descriptor]
|
val smartCastTypes = variableToTypes[descriptor]
|
||||||
if (smartCastTypes != null && !smartCastTypes.isEmpty()) {
|
if (smartCastTypes == null || smartCastTypes.isEmpty()) return type.toList()
|
||||||
return smartCastTypes + returnType.toList()
|
return smartCastTypes + type.toList()
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnType.toList()
|
|
||||||
}
|
|
||||||
else if (descriptor is ClassDescriptor && descriptor.getKind().isSingleton()) {
|
|
||||||
return listOf(descriptor.getDefaultType())
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ::typesOf
|
return ::typesOf
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
|
|||||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.jet.lang.types.JetType
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||||
import java.util.ArrayList
|
|
||||||
import org.jetbrains.jet.plugin.completion.*
|
import org.jetbrains.jet.plugin.completion.*
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
||||||
@@ -39,6 +37,9 @@ import com.intellij.openapi.util.Key
|
|||||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
|
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||||
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
|
||||||
|
import org.jetbrains.jet.lang.types.TypeSubstitutor
|
||||||
|
import java.util.ArrayList
|
||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
class ArtificialElementInsertHandler(
|
class ArtificialElementInsertHandler(
|
||||||
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
|
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
|
||||||
@@ -98,36 +99,71 @@ fun LookupElement.addTailAndNameSimilarity(matchedExpectedInfos: Collection<Expe
|
|||||||
return lookupElement
|
return lookupElement
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ExpectedInfoClassification {
|
class ExpectedInfoClassification private(val substitutor: TypeSubstitutor?, val makeNotNullable: Boolean) {
|
||||||
MATCHES
|
class object {
|
||||||
MAKE_NOT_NULLABLE
|
val notMatches = ExpectedInfoClassification(null, false)
|
||||||
NOT_MATCHES
|
fun matches(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, false)
|
||||||
|
fun matchesIfNotNullable(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MutableCollection<LookupElement>.addLookupElements(expectedInfos: Collection<ExpectedInfo>,
|
fun FuzzyType.classifyExpectedInfo(expectedInfo: ExpectedInfo): ExpectedInfoClassification {
|
||||||
infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
|
val substitutor = matchedSubstitutor(expectedInfo.type)
|
||||||
lookupElementFactory: () -> LookupElement?) {
|
if (substitutor != null) {
|
||||||
val matchedInfos = ArrayList<ExpectedInfo>()
|
return ExpectedInfoClassification.matches(substitutor)
|
||||||
val matchedInfosNotNullable = ArrayList<ExpectedInfo>()
|
}
|
||||||
|
|
||||||
|
if (isNullable()) {
|
||||||
|
val substitutor2 = makeNotNullable().matchedSubstitutor(expectedInfo.type)
|
||||||
|
if (substitutor2 != null) {
|
||||||
|
return ExpectedInfoClassification.matchesIfNotNullable(substitutor2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExpectedInfoClassification.notMatches
|
||||||
|
}
|
||||||
|
|
||||||
|
fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLookupElements(
|
||||||
|
descriptor: TDescriptor,
|
||||||
|
expectedInfos: Collection<ExpectedInfo>,
|
||||||
|
infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
|
||||||
|
lookupElementFactory: (TDescriptor) -> LookupElement?
|
||||||
|
) {
|
||||||
|
class DescriptorWrapper(val descriptor: TDescriptor) {
|
||||||
|
override fun equals(other: Any?) = other is DescriptorWrapper && descriptorsEqualWithSubstitution(this.descriptor, other.descriptor)
|
||||||
|
override fun hashCode() = if (this.descriptor != null) this.descriptor.getOriginal().hashCode() else 0
|
||||||
|
}
|
||||||
|
fun TDescriptor.wrap() = DescriptorWrapper(this)
|
||||||
|
fun DescriptorWrapper.unwrap() = this.descriptor
|
||||||
|
|
||||||
|
val matchedInfos = HashMap<DescriptorWrapper, MutableList<ExpectedInfo>>()
|
||||||
|
val makeNullableInfos = HashMap<DescriptorWrapper, MutableList<ExpectedInfo>>()
|
||||||
for (info in expectedInfos) {
|
for (info in expectedInfos) {
|
||||||
when (infoClassifier(info)) {
|
val classification = infoClassifier(info)
|
||||||
ExpectedInfoClassification.MATCHES -> matchedInfos.add(info)
|
if (classification.substitutor != null) {
|
||||||
ExpectedInfoClassification.MAKE_NOT_NULLABLE -> matchedInfosNotNullable.add(info)
|
[suppress("UNCHECKED_CAST")]
|
||||||
|
val substitutedDescriptor = descriptor?.substitute(classification.substitutor) as TDescriptor
|
||||||
|
val map = if (classification.makeNotNullable) makeNullableInfos else matchedInfos
|
||||||
|
map.getOrPut(substitutedDescriptor.wrap()) { ArrayList() }.add(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedInfos.isNotEmpty()) {
|
if (!matchedInfos.isEmpty()) {
|
||||||
val lookupElement = lookupElementFactory()
|
for ((substitutedDescriptor, infos) in matchedInfos) {
|
||||||
if (lookupElement != null) {
|
val lookupElement = lookupElementFactory(substitutedDescriptor.unwrap())
|
||||||
add(lookupElement.addTailAndNameSimilarity(matchedInfos))
|
if (lookupElement != null) {
|
||||||
|
add(lookupElement.addTailAndNameSimilarity(infos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (matchedInfosNotNullable.isNotEmpty()) {
|
else {
|
||||||
addLookupElementsForNullable(lookupElementFactory, matchedInfosNotNullable)
|
for ((substitutedDescriptor, infos) in makeNullableInfos) {
|
||||||
|
addLookupElementsForNullable({ lookupElementFactory(substitutedDescriptor.unwrap()) }, infos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection<ExpectedInfo>) {
|
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection<ExpectedInfo>) {
|
||||||
var lookupElement = factory()
|
var lookupElement = factory()
|
||||||
if (lookupElement != null) {
|
if (lookupElement != null) {
|
||||||
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement!!) {
|
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement!!) {
|
||||||
@@ -205,8 +241,6 @@ fun LookupElementFactory.createLookupElement(
|
|||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
|
|
||||||
fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.DEFAULT.isSubtypeOf(this, expectedType)
|
|
||||||
|
|
||||||
fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
||||||
fun <T : Any> T?.toSet(): Set<T> = if (this != null) setOf(this) else setOf()
|
fun <T : Any> T?.toSet(): Set<T> = if (this != null) setOf(this) else setOf()
|
||||||
|
|
||||||
@@ -235,4 +269,3 @@ fun LookupElement.assignSmartCompletionPriority(priority: SmartCompletionItemPri
|
|||||||
putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority)
|
putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo(array: Array<String>){}
|
||||||
|
|
||||||
|
fun f(){
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "array", tailText: "(vararg t: String) (kotlin)", typeText: "Array<String>" }
|
||||||
|
// ABSENT: arrayOfNulls
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo(array: Array<String?>){}
|
||||||
|
|
||||||
|
fun f(){
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "array", tailText: "(vararg t: String?) (kotlin)", typeText: "Array<String?>" }
|
||||||
|
// EXIST: { lookupString: "arrayOfNulls", tailText: "(size: Int) (kotlin)", typeText: "Array<String?>" }
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun foo(list: List<String>){}
|
||||||
|
|
||||||
|
fun f(){
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "listOf", tailText: "() (kotlin)", typeText: "List<String>" }
|
||||||
|
// EXIST: { lookupString: "listOf", tailText: "(vararg values: String) (kotlin)", typeText: "List<String>" }
|
||||||
|
// EXIST: { lookupString: "arrayListOf", tailText: "(vararg values: String!) (kotlin)", typeText: "ArrayList<String!>" }
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
fun foo(list: List<String>): Collection<Int> {
|
||||||
|
return list.<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "map", tailText: "(transform: (String) -> Int) for Iterable<T> in kotlin", typeText: "List<Int>" }
|
||||||
|
// ABSENT: filter
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class C<T> {
|
||||||
|
class object {
|
||||||
|
fun<T> create(t: T): C<T>{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(c: C<String>){}
|
||||||
|
|
||||||
|
fun f(){
|
||||||
|
foo(<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "create", itemText: "C.create", tailText: "(t: String) (<root>)", typeText: "C<String>" }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
fun<T> emptyList(): List<T> = listOf()
|
||||||
|
|
||||||
|
fun foo(list: List<String>){}
|
||||||
|
fun foo(list: List<String>, i: Int){}
|
||||||
|
fun foo(list: List<Int>, b: Boolean){}
|
||||||
|
fun foo(list: List<Int>, c: Char){}
|
||||||
|
|
||||||
|
fun f(){
|
||||||
|
foo(empty<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "emptyList", typeText: "List<String>" }
|
||||||
|
// EXIST: { lookupString: "emptyList", typeText: "List<Int>" }
|
||||||
|
// NUMBER: 2
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
fun nothingFoo() = throw Exception()
|
||||||
|
|
||||||
|
fun<T> genericFoo1(t: T): T = t
|
||||||
|
|
||||||
|
fun<T: Any> genericFoo2(t: T): T? = t
|
||||||
|
|
||||||
|
fun<T: Runnable> genericFoo3(t: T): T = t
|
||||||
|
|
||||||
|
fun foo(): Runnable {
|
||||||
|
return <caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABSENT: nothingFoo
|
||||||
|
// ABSENT: genericFoo1
|
||||||
|
// ABSENT: genericFoo2
|
||||||
|
// EXIST: { itemText: "genericFoo3", tailText: "(t: Runnable) (<root>)", typeText: "Runnable" }
|
||||||
@@ -9,6 +9,7 @@ fun bar(p: (String) -> Boolean) {
|
|||||||
|
|
||||||
// ORDER: ::doFilter
|
// ORDER: ::doFilter
|
||||||
// ORDER: p
|
// ORDER: p
|
||||||
|
// ORDER: countTo
|
||||||
// ORDER: {...}
|
// ORDER: {...}
|
||||||
// ORDER: { String -> ... }
|
// ORDER: { String -> ... }
|
||||||
// ORDER: ::error
|
// ORDER: ::error
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jetbrains.jet.completion;
|
|||||||
import com.intellij.testFramework.TestDataPath;
|
import com.intellij.testFramework.TestDataPath;
|
||||||
import org.jetbrains.jet.JUnit3RunnerWithInners;
|
import org.jetbrains.jet.JUnit3RunnerWithInners;
|
||||||
import org.jetbrains.jet.JetTestUtils;
|
import org.jetbrains.jet.JetTestUtils;
|
||||||
import org.jetbrains.jet.test.InnerTestClasses;
|
|
||||||
import org.jetbrains.jet.test.TestMetadata;
|
import org.jetbrains.jet.test.TestMetadata;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@@ -258,12 +257,48 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GenericFunction1.kt")
|
||||||
|
public void testGenericFunction1() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericFunction1.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GenericFunction2.kt")
|
||||||
|
public void testGenericFunction2() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericFunction2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GenericFunction3.kt")
|
||||||
|
public void testGenericFunction3() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericFunction3.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GenericFunction4.kt")
|
||||||
|
public void testGenericFunction4() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericFunction4.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GenericFunction5.kt")
|
||||||
|
public void testGenericFunction5() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericFunction5.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("GenericMethodArgument.kt")
|
@TestMetadata("GenericMethodArgument.kt")
|
||||||
public void testGenericMethodArgument() throws Exception {
|
public void testGenericMethodArgument() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericMethodArgument.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GenericMethodArgument.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("GroupBySubstitutor.kt")
|
||||||
|
public void testGroupBySubstitutor() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/GroupBySubstitutor.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IfCondition.kt")
|
@TestMetadata("IfCondition.kt")
|
||||||
public void testIfCondition() throws Exception {
|
public void testIfCondition() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/IfCondition.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/IfCondition.kt");
|
||||||
@@ -732,6 +767,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SkipDeclarationsOfType.kt")
|
||||||
|
public void testSkipDeclarationsOfType() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/SkipDeclarationsOfType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SkipUnresolvedTypes.kt")
|
@TestMetadata("SkipUnresolvedTypes.kt")
|
||||||
public void testSkipUnresolvedTypes() throws Exception {
|
public void testSkipUnresolvedTypes() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/SkipUnresolvedTypes.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/SkipUnresolvedTypes.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user