From 3c573d555a03e11eea3595ed2b3d7304c9016b8a Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 20 Jan 2015 12:43:10 +0300 Subject: [PATCH] Smart completion: heuristic parameter types for some methods like Collection.contains() --- .idea/dictionaries/valentin.xml | 1 + .../kotlin/di/InjectorForMacros.java | 32 +++--- .../generators/injectors/GenerateInjectors.kt | 1 + .../kotlin/idea/completion/ExpectedInfos.kt | 14 ++- .../idea/completion/HeuristicSignatures.kt | 107 ++++++++++++++++++ .../completion/handlers/GenerateLambda.kt | 18 +-- .../idea/completion/smart/SmartCompletion.kt | 4 +- .../smart/TypesWithContainsDetector.kt | 13 ++- .../smart/heuristicSignatures/Contains.kt | 7 ++ .../smart/heuristicSignatures/ContainsAll.kt | 6 + .../smart/heuristicSignatures/ContainsKey.kt | 6 + .../heuristicSignatures/ContainsValue.kt | 6 + .../smart/heuristicSignatures/Get.kt | 6 + .../smart/heuristicSignatures/InOperator.kt | 24 ++++ .../smart/heuristicSignatures/IndexOf.kt | 7 ++ .../smart/heuristicSignatures/LastIndexOf.kt | 7 ++ .../smart/heuristicSignatures/MapRemove.kt | 6 + .../smart/heuristicSignatures/Remove.kt | 9 ++ .../smart/heuristicSignatures/RemoveAll.kt | 8 ++ .../smart/heuristicSignatures/RetainAll.kt | 8 ++ .../JvmSmartCompletionTestGenerated.java | 83 +++++++++++++- 21 files changed, 341 insertions(+), 32 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/completion/HeuristicSignatures.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/Contains.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/ContainsAll.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/ContainsKey.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/ContainsValue.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/Get.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/InOperator.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/IndexOf.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/LastIndexOf.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/MapRemove.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/Remove.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/RemoveAll.kt create mode 100644 idea/testData/completion/smart/heuristicSignatures/RetainAll.kt diff --git a/.idea/dictionaries/valentin.xml b/.idea/dictionaries/valentin.xml index 5d30758219a..8fee9efdd5d 100644 --- a/.idea/dictionaries/valentin.xml +++ b/.idea/dictionaries/valentin.xml @@ -1,6 +1,7 @@ + funs initializers inserter pparent diff --git a/compiler/frontend/src/org/jetbrains/kotlin/di/InjectorForMacros.java b/compiler/frontend/src/org/jetbrains/kotlin/di/InjectorForMacros.java index c4409732e8a..e42576466d0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/di/InjectorForMacros.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/di/InjectorForMacros.java @@ -23,20 +23,17 @@ import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap; import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices; import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents; import org.jetbrains.kotlin.resolve.calls.CallResolver; +import org.jetbrains.kotlin.resolve.TypeResolver; import org.jetbrains.kotlin.context.GlobalContext; import org.jetbrains.kotlin.storage.StorageManager; import org.jetbrains.kotlin.resolve.AdditionalCheckerProvider.DefaultProvider; import org.jetbrains.kotlin.resolve.AnnotationResolver; -import org.jetbrains.kotlin.resolve.TypeResolver; -import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver; -import org.jetbrains.kotlin.resolve.TypeResolver.FlexibleTypeCapabilitiesProvider; -import org.jetbrains.kotlin.context.LazinessToken; -import org.jetbrains.kotlin.types.DynamicTypesSettings; import org.jetbrains.kotlin.resolve.calls.CallExpressionResolver; import org.jetbrains.kotlin.resolve.DescriptorResolver; import org.jetbrains.kotlin.resolve.DelegatedPropertyResolver; import org.jetbrains.kotlin.resolve.StatementFilter; import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils; +import org.jetbrains.kotlin.types.DynamicTypesSettings; import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils; import org.jetbrains.kotlin.types.expressions.ForLoopConventionsChecker; import org.jetbrains.kotlin.types.expressions.LocalClassifierAnalyzer; @@ -45,6 +42,9 @@ import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver; import org.jetbrains.kotlin.resolve.calls.CallCompleter; import org.jetbrains.kotlin.resolve.calls.CandidateResolver; import org.jetbrains.kotlin.resolve.calls.tasks.TaskPrioritizer; +import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver; +import org.jetbrains.kotlin.resolve.TypeResolver.FlexibleTypeCapabilitiesProvider; +import org.jetbrains.kotlin.context.LazinessToken; import org.jetbrains.annotations.NotNull; import javax.annotation.PreDestroy; @@ -59,20 +59,17 @@ public class InjectorForMacros { private final ExpressionTypingServices expressionTypingServices; private final ExpressionTypingComponents expressionTypingComponents; private final CallResolver callResolver; + private final TypeResolver typeResolver; private final GlobalContext globalContext; private final StorageManager storageManager; private final DefaultProvider defaultProvider; private final AnnotationResolver annotationResolver; - private final TypeResolver typeResolver; - private final QualifiedExpressionResolver qualifiedExpressionResolver; - private final FlexibleTypeCapabilitiesProvider flexibleTypeCapabilitiesProvider; - private final LazinessToken lazinessToken; - private final DynamicTypesSettings dynamicTypesSettings; private final CallExpressionResolver callExpressionResolver; private final DescriptorResolver descriptorResolver; private final DelegatedPropertyResolver delegatedPropertyResolver; private final StatementFilter statementFilter; private final ControlStructureTypingUtils controlStructureTypingUtils; + private final DynamicTypesSettings dynamicTypesSettings; private final ExpressionTypingUtils expressionTypingUtils; private final ForLoopConventionsChecker forLoopConventionsChecker; private final LocalClassifierAnalyzer localClassifierAnalyzer; @@ -81,6 +78,9 @@ public class InjectorForMacros { private final CallCompleter callCompleter; private final CandidateResolver candidateResolver; private final TaskPrioritizer taskPrioritizer; + private final QualifiedExpressionResolver qualifiedExpressionResolver; + private final FlexibleTypeCapabilitiesProvider flexibleTypeCapabilitiesProvider; + private final LazinessToken lazinessToken; public InjectorForMacros( @NotNull Project project, @@ -93,15 +93,15 @@ public class InjectorForMacros { this.expressionTypingComponents = new ExpressionTypingComponents(); this.expressionTypingServices = new ExpressionTypingServices(getExpressionTypingComponents()); this.callResolver = new CallResolver(); - this.globalContext = org.jetbrains.kotlin.context.ContextPackage.GlobalContext(); - this.storageManager = globalContext.getStorageManager(); - this.defaultProvider = DefaultProvider.INSTANCE$; this.annotationResolver = new AnnotationResolver(); this.qualifiedExpressionResolver = new QualifiedExpressionResolver(); this.flexibleTypeCapabilitiesProvider = new FlexibleTypeCapabilitiesProvider(); + this.globalContext = org.jetbrains.kotlin.context.ContextPackage.GlobalContext(); + this.storageManager = globalContext.getStorageManager(); this.lazinessToken = new LazinessToken(); this.dynamicTypesSettings = new DynamicTypesSettings(); this.typeResolver = new TypeResolver(annotationResolver, qualifiedExpressionResolver, moduleDescriptor, flexibleTypeCapabilitiesProvider, storageManager, lazinessToken, dynamicTypesSettings); + this.defaultProvider = DefaultProvider.INSTANCE$; this.callExpressionResolver = new CallExpressionResolver(); this.descriptorResolver = new DescriptorResolver(); this.delegatedPropertyResolver = new DelegatedPropertyResolver(); @@ -109,7 +109,7 @@ public class InjectorForMacros { this.controlStructureTypingUtils = new ControlStructureTypingUtils(getExpressionTypingServices()); this.expressionTypingUtils = new ExpressionTypingUtils(getExpressionTypingServices(), getCallResolver(), kotlinBuiltIns); this.forLoopConventionsChecker = new ForLoopConventionsChecker(); - this.localClassifierAnalyzer = new LocalClassifierAnalyzer(descriptorResolver, typeResolver, annotationResolver); + this.localClassifierAnalyzer = new LocalClassifierAnalyzer(descriptorResolver, getTypeResolver(), annotationResolver); this.reflectionTypes = new ReflectionTypes(moduleDescriptor); this.argumentTypeResolver = new ArgumentTypeResolver(); this.candidateResolver = new CandidateResolver(); @@ -191,4 +191,8 @@ public class InjectorForMacros { return this.callResolver; } + public TypeResolver getTypeResolver() { + return this.typeResolver; + } + } diff --git a/generators/src/org/jetbrains/kotlin/generators/injectors/GenerateInjectors.kt b/generators/src/org/jetbrains/kotlin/generators/injectors/GenerateInjectors.kt index f1f0443849a..f01675d18af 100644 --- a/generators/src/org/jetbrains/kotlin/generators/injectors/GenerateInjectors.kt +++ b/generators/src/org/jetbrains/kotlin/generators/injectors/GenerateInjectors.kt @@ -222,6 +222,7 @@ private fun generatorForMacro() = publicField() publicField() publicField() + publicField() field(useAsContext = true, init = GivenExpression("org.jetbrains.kotlin.context.ContextPackage.GlobalContext()")) diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt b/idea/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt index 63768d7467b..6afc7df1981 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt @@ -70,6 +70,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker +import org.jetbrains.kotlin.descriptors.ModuleDescriptor enum class Tail { COMMA @@ -89,7 +90,12 @@ class PositionalArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, = function.hashCode() } -class ExpectedInfos(val bindingContext: BindingContext, val resolutionFacade: ResolutionFacade) { +class ExpectedInfos( + val bindingContext: BindingContext, + val resolutionFacade: ResolutionFacade, + val moduleDescriptor: ModuleDescriptor, + val useHeuristicSignatures: Boolean +) { public fun calculate(expressionWithType: JetExpression): Collection? { return calculateForArgument(expressionWithType) ?: calculateForFunctionLiteralArgument(expressionWithType) @@ -207,7 +213,11 @@ class ExpectedInfos(val bindingContext: BindingContext, val resolutionFacade: Re val parameter = parameters[argumentIndex] val expectedName = if (descriptor.hasSynthesizedParameterNames()) null else parameter.getName().asString() - expectedInfos.add(PositionalArgumentExpectedInfo(parameter.getType(), expectedName, tail, descriptor, argumentIndex)) + val parameterType = if (useHeuristicSignatures) + HeuristicSignatures.correctedParameterType(descriptor, argumentIndex, moduleDescriptor, callElement.getProject()) ?: parameter.getType() + else + parameter.getType() + expectedInfos.add(PositionalArgumentExpectedInfo(parameterType, expectedName, tail, descriptor, argumentIndex)) } } return expectedInfos diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/HeuristicSignatures.kt b/idea/src/org/jetbrains/kotlin/idea/completion/HeuristicSignatures.kt new file mode 100644 index 00000000000..0536690d953 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/completion/HeuristicSignatures.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2010-2015 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.kotlin.idea.completion + +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import java.util.HashMap +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.types.SubstitutionUtils +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.di.InjectorForMacros +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.resolve.JetModuleUtil +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.resolve.scopes.ChainedScope +import org.jetbrains.kotlin.types.TypeUtils + +public object HeuristicSignatures { + private val signatures = HashMap, List>() + + ;{ + registerSignature("kotlin.Collection", "contains", "E") + registerSignature("kotlin.Collection", "containsAll", "kotlin.Collection") + registerSignature("kotlin.MutableCollection", "remove", "E") + registerSignature("kotlin.MutableCollection", "removeAll", "kotlin.Collection") + registerSignature("kotlin.MutableCollection", "retainAll", "kotlin.Collection") + registerSignature("kotlin.List", "indexOf", "E") + registerSignature("kotlin.List", "lastIndexOf", "E") + registerSignature("kotlin.Map", "get", "K") + registerSignature("kotlin.Map", "containsKey", "K") + registerSignature("kotlin.Map", "containsValue", "V") + registerSignature("kotlin.MutableMap", "remove", "K") + } + + private fun registerSignature( + classFqName: String, + name: String, + vararg parameterTypes: String) { + signatures[FqName(classFqName) to Name.identifier(name)] = parameterTypes.toList() + } + + public fun correctedParameterType(function: FunctionDescriptor, parameterIndex: Int, moduleDescriptor: ModuleDescriptor, project: Project): JetType? { + val ownerType = function.getDispatchReceiverParameter()?.getType() ?: return null + + val superFunctions = function.getOverriddenDescriptors() + if (superFunctions.isNotEmpty()) { + for (superFunction in superFunctions) { + val correctedType = correctedParameterType(superFunction, parameterIndex, moduleDescriptor, project) ?: continue + val typeSubstitutor = SubstitutionUtils.buildDeepSubstitutor(ownerType) + return typeSubstitutor.safeSubstitute(correctedType, Variance.INVARIANT) + } + return null + } + else { + val ownerClass = ownerType.getConstructor().getDeclarationDescriptor() ?: return null + val classFqName = DescriptorUtils.getFqNameSafe(ownerClass) + val parameterTypes = signatures[classFqName to function.getName()] ?: return null + val typeStr = parameterTypes[parameterIndex] + val typeParameters = ownerClass.getTypeConstructor().getParameters() + + val type = typeFromText(typeStr, typeParameters, moduleDescriptor, project) + + // now substitute type parameters with actual arguments + val typeArgs = ownerType.getArguments() + val typeArgsMap = typeParameters.indices.map { typeParameters[it] to typeArgs[it] }.toMap() + val substitutor = TypeUtils.makeSubstitutorForTypeParametersMap(typeArgsMap) + return substitutor.substitute(type, Variance.INVARIANT) + } + } + + private fun typeFromText(text: String, typeParameters: Collection, moduleDescriptor: ModuleDescriptor, project: Project): JetType { + val typeRef = JetPsiFactory(project).createType(text) + val injector = InjectorForMacros(project, moduleDescriptor) + val rootPackagesScope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor) + val typeParametersScope = TypeParametersScope(typeParameters) + val scope = ChainedScope(moduleDescriptor, "Root packages + type parameters", typeParametersScope, rootPackagesScope) + val type = injector.getTypeResolver().resolveType(scope, typeRef, BindingTraceContext(), false) + assert(!type.isError()) { "No type resolved from '$text'" } + return type + } + + private class TypeParametersScope(params: Collection) : JetScope by JetScope.Empty { + private val paramsByName = params.map { it.getName() to it }.toMap() + + override fun getClassifier(name: Name) = paramsByName[name] + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt b/idea/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt index 1695cbf6abc..852fb179a58 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/handlers/GenerateLambda.kt @@ -73,8 +73,8 @@ fun insertLambdaTemplate(context: InsertionContext, placeholderRange: TextRange, fun buildLambdaPresentation(lambdaType: JetType): String { val parameterTypes = functionParameterTypes(lambdaType) - val parametersPresentation = parameterTypes.map { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) }.makeString(", ") - fun wrap(s: String) = if (parameterTypes.size != 1) "($s)" else s + val parametersPresentation = parameterTypes.map { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) }.joinToString(", ") + fun wrap(s: String) = if (parameterTypes.size() != 1) "($s)" else s return "{ ${wrap(parametersPresentation)} -> ... }" } @@ -86,18 +86,18 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan val resolutionFacade = file.getResolutionFacade() val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.PARTIAL) - val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade).calculate(expression) ?: return false + val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, resolutionFacade.findModuleDescriptor(file), false).calculate(expression) ?: return false val functionTypes = expectedInfos.map { it.type }.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }.toSet() - if (functionTypes.size <= 1) return false + if (functionTypes.size() <= 1) return false - val lambdaParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(lambdaType).size - return functionTypes.filter { KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(it).size == lambdaParameterCount }.size > 1 + val lambdaParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(lambdaType).size() + return functionTypes.filter { KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(it).size() == lambdaParameterCount }.size() > 1 } private fun buildTemplate(lambdaType: JetType, explicitParameterTypes: Boolean, project: Project): Template { val parameterTypes = functionParameterTypes(lambdaType) - val useParenthesis = explicitParameterTypes || parameterTypes.size != 1 + val useParenthesis = explicitParameterTypes || parameterTypes.size() != 1 val manager = TemplateManager.getInstance(project) @@ -109,7 +109,7 @@ private fun buildTemplate(lambdaType: JetType, explicitParameterTypes: Boolean, template.addTextSegment("(") } - for ((i, parameterType) in parameterTypes.withIndices()) { + for ((i, parameterType) in parameterTypes.withIndex()) { if (i > 0) { template.addTextSegment(", ") } @@ -135,7 +135,7 @@ private class ParameterNameExpression(val nameSuggestions: Array) : Expr override fun calculateQuickResult(context: ExpressionContext?): Result? = null override fun calculateLookupItems(context: ExpressionContext?) - = Array(nameSuggestions.size, { LookupElementBuilder.create(nameSuggestions[it]) }) + = Array(nameSuggestions.size(), { LookupElementBuilder.create(nameSuggestions[it]) }) } fun functionParameterTypes(functionType: JetType): List diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index 7089f3a3562..0093e664fb6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -238,7 +238,7 @@ class SmartCompletion( } } - return ExpectedInfos(bindingContext, resolutionFacade).calculate(expression) + return ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, true).calculate(expression) } private fun implicitlyTypedDeclarationFromInitializer(expression: JetExpression): JetDeclaration? { @@ -400,7 +400,7 @@ class SmartCompletion( val leftOperandType = bindingContext.get(BindingContext.EXPRESSION_TYPE, binaryExpression.getLeft()) ?: return null val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType) - val detector = TypesWithContainsDetector(scope, leftOperandType) + val detector = TypesWithContainsDetector(scope, leftOperandType, project, moduleDescriptor) return buildResultByTypeFilter(expressionWithType, receiver, null) { detector.hasContains(it) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypesWithContainsDetector.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypesWithContainsDetector.kt index fac12cd7bac..17110eae4e0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypesWithContainsDetector.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/TypesWithContainsDetector.kt @@ -27,11 +27,15 @@ import org.jetbrains.kotlin.idea.util.nullability import org.jetbrains.kotlin.idea.util.TypeNullability import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.completion.HeuristicSignatures -//TODO: heuristics for collection's? class TypesWithContainsDetector( private val scope: JetScope, - private val argumentType: JetType + private val argumentType: JetType, + private val project: Project, + private val moduleDescriptor: ModuleDescriptor ) { private val cache = HashMap() @@ -54,7 +58,8 @@ class TypesWithContainsDetector( private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection): Boolean { if (!TypeUtils.equalTypes(function.getReturnType(), booleanType)) return false val parameter = function.getValueParameters().singleOrNull() ?: return false - val parameterType = FuzzyType(parameter.getType(), function.getTypeParameters() + freeTypeParams) - return parameterType.checkIsSuperTypeOf(argumentType) != null + val parameterType = HeuristicSignatures.correctedParameterType(function, 0, moduleDescriptor, project) ?: parameter.getType() + val fuzzyParameterType = FuzzyType(parameterType, function.getTypeParameters() + freeTypeParams) + return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null } } diff --git a/idea/testData/completion/smart/heuristicSignatures/Contains.kt b/idea/testData/completion/smart/heuristicSignatures/Contains.kt new file mode 100644 index 00000000000..7545addde88 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/Contains.kt @@ -0,0 +1,7 @@ +fun foo(list: List, p1: Any, p2: String) { + list.contains() +} + +// ABSENT: p1 +// EXIST: p2 +// EXIST: { itemText: "String", tailText: "() (kotlin)" } diff --git a/idea/testData/completion/smart/heuristicSignatures/ContainsAll.kt b/idea/testData/completion/smart/heuristicSignatures/ContainsAll.kt new file mode 100644 index 00000000000..966a156cbad --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/ContainsAll.kt @@ -0,0 +1,6 @@ +fun foo(list: List, p1: Collection, p2: List) { + list.containsAll() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/ContainsKey.kt b/idea/testData/completion/smart/heuristicSignatures/ContainsKey.kt new file mode 100644 index 00000000000..b2d87cec67f --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/ContainsKey.kt @@ -0,0 +1,6 @@ +fun foo(list: Map, p1: Any, p2: String) { + list.containsKey() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/ContainsValue.kt b/idea/testData/completion/smart/heuristicSignatures/ContainsValue.kt new file mode 100644 index 00000000000..c636075dfa5 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/ContainsValue.kt @@ -0,0 +1,6 @@ +fun foo(list: Map, p1: Any, p2: Int) { + list.containsValue() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/Get.kt b/idea/testData/completion/smart/heuristicSignatures/Get.kt new file mode 100644 index 00000000000..a96bdff34fb --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/Get.kt @@ -0,0 +1,6 @@ +fun foo(list: Map, p1: Any, p2: String) { + list.get() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/InOperator.kt b/idea/testData/completion/smart/heuristicSignatures/InOperator.kt new file mode 100644 index 00000000000..4ed1ab47a2f --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/InOperator.kt @@ -0,0 +1,24 @@ +import java.util.ArrayList + +trait A +trait B : A +trait C + +fun foo(a: A, cA: Collection, cB: Collection, cC: Collection, cAny: Collection, + lA: List, lb: List, lC: List, lAny: List, + aA: ArrayList, aB: ArrayList, aC: ArrayList, aAny: ArrayList) { + if (a in +} + +// EXIST: cA +// ABSENT: cB +// ABSENT: cC +// EXIST: cAny +// EXIST: lA +// ABSENT: lB +// ABSENT: lC +// EXIST: lAny +// EXIST: aA +// ABSENT: aB +// ABSENT: aC +// EXIST: aAny diff --git a/idea/testData/completion/smart/heuristicSignatures/IndexOf.kt b/idea/testData/completion/smart/heuristicSignatures/IndexOf.kt new file mode 100644 index 00000000000..713032bf828 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/IndexOf.kt @@ -0,0 +1,7 @@ +fun foo(list: List, p1: Any, p2: String) { + list.indexOf() +} + +// ABSENT: p1 +// EXIST: p2 +// EXIST: { itemText: "String", tailText: "() (kotlin)" } diff --git a/idea/testData/completion/smart/heuristicSignatures/LastIndexOf.kt b/idea/testData/completion/smart/heuristicSignatures/LastIndexOf.kt new file mode 100644 index 00000000000..f6aa9141a4f --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/LastIndexOf.kt @@ -0,0 +1,7 @@ +fun foo(list: List, p1: Any, p2: String) { + list.lastIndexOf() +} + +// ABSENT: p1 +// EXIST: p2 +// EXIST: { itemText: "String", tailText: "() (kotlin)" } diff --git a/idea/testData/completion/smart/heuristicSignatures/MapRemove.kt b/idea/testData/completion/smart/heuristicSignatures/MapRemove.kt new file mode 100644 index 00000000000..7e69e509564 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/MapRemove.kt @@ -0,0 +1,6 @@ +fun foo(list: MutableMap, p1: Any, p2: String) { + list.remove() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/Remove.kt b/idea/testData/completion/smart/heuristicSignatures/Remove.kt new file mode 100644 index 00000000000..a6c765b5c7a --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/Remove.kt @@ -0,0 +1,9 @@ +import java.util.* + +fun foo(list: ArrayList, p1: Any, p2: String) { + list.remove() +} + +// ABSENT: p1 +// EXIST: p2 +// EXIST: { itemText: "String", tailText: "() (kotlin)" } diff --git a/idea/testData/completion/smart/heuristicSignatures/RemoveAll.kt b/idea/testData/completion/smart/heuristicSignatures/RemoveAll.kt new file mode 100644 index 00000000000..dc8674db438 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/RemoveAll.kt @@ -0,0 +1,8 @@ +import java.util.* + +fun foo(list: ArrayList, p1: ArrayList, p2: ArrayList) { + list.removeAll() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/testData/completion/smart/heuristicSignatures/RetainAll.kt b/idea/testData/completion/smart/heuristicSignatures/RetainAll.kt new file mode 100644 index 00000000000..80fc715a2c8 --- /dev/null +++ b/idea/testData/completion/smart/heuristicSignatures/RetainAll.kt @@ -0,0 +1,8 @@ +import java.util.* + +fun foo(list: ArrayList, p1: ArrayList, p2: ArrayList) { + list.retainAll() +} + +// ABSENT: p1 +// EXIST: p2 diff --git a/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java index d93b4a022c1..ad48d88663e 100644 --- a/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java @@ -30,7 +30,7 @@ import java.util.regex.Pattern; @SuppressWarnings("all") @TestMetadata("idea/testData/completion/smart") @TestDataPath("$PROJECT_ROOT") -@InnerTestClasses({JvmSmartCompletionTestGenerated.AfterAs.class, JvmSmartCompletionTestGenerated.AnonymousObject.class, JvmSmartCompletionTestGenerated.Constructor.class, JvmSmartCompletionTestGenerated.ForLoopRange.class, JvmSmartCompletionTestGenerated.FunctionLiterals.class, JvmSmartCompletionTestGenerated.FunctionReference.class, JvmSmartCompletionTestGenerated.Generics.class, JvmSmartCompletionTestGenerated.IfValue.class, JvmSmartCompletionTestGenerated.InElvisOperator.class, JvmSmartCompletionTestGenerated.InOperator.class, JvmSmartCompletionTestGenerated.Inheritors.class, JvmSmartCompletionTestGenerated.MultipleArgsItem.class, JvmSmartCompletionTestGenerated.SmartCasts.class, JvmSmartCompletionTestGenerated.This.class, JvmSmartCompletionTestGenerated.WhenEntry.class}) +@InnerTestClasses({JvmSmartCompletionTestGenerated.AfterAs.class, JvmSmartCompletionTestGenerated.AnonymousObject.class, JvmSmartCompletionTestGenerated.Constructor.class, JvmSmartCompletionTestGenerated.ForLoopRange.class, JvmSmartCompletionTestGenerated.FunctionLiterals.class, JvmSmartCompletionTestGenerated.FunctionReference.class, JvmSmartCompletionTestGenerated.Generics.class, JvmSmartCompletionTestGenerated.HeuristicSignatures.class, JvmSmartCompletionTestGenerated.IfValue.class, JvmSmartCompletionTestGenerated.InElvisOperator.class, JvmSmartCompletionTestGenerated.InOperator.class, JvmSmartCompletionTestGenerated.Inheritors.class, JvmSmartCompletionTestGenerated.MultipleArgsItem.class, JvmSmartCompletionTestGenerated.SmartCasts.class, JvmSmartCompletionTestGenerated.This.class, JvmSmartCompletionTestGenerated.WhenEntry.class}) @RunWith(JUnit3RunnerWithInners.class) public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionTest { @TestMetadata("AfterExclSign.kt") @@ -736,6 +736,87 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT } } + @TestMetadata("idea/testData/completion/smart/heuristicSignatures") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class HeuristicSignatures extends AbstractJvmSmartCompletionTest { + public void testAllFilesPresentInHeuristicSignatures() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/smart/heuristicSignatures"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("Contains.kt") + public void testContains() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/Contains.kt"); + doTest(fileName); + } + + @TestMetadata("ContainsAll.kt") + public void testContainsAll() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/ContainsAll.kt"); + doTest(fileName); + } + + @TestMetadata("ContainsKey.kt") + public void testContainsKey() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/ContainsKey.kt"); + doTest(fileName); + } + + @TestMetadata("ContainsValue.kt") + public void testContainsValue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/ContainsValue.kt"); + doTest(fileName); + } + + @TestMetadata("Get.kt") + public void testGet() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/Get.kt"); + doTest(fileName); + } + + @TestMetadata("InOperator.kt") + public void testInOperator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/InOperator.kt"); + doTest(fileName); + } + + @TestMetadata("IndexOf.kt") + public void testIndexOf() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/IndexOf.kt"); + doTest(fileName); + } + + @TestMetadata("LastIndexOf.kt") + public void testLastIndexOf() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/LastIndexOf.kt"); + doTest(fileName); + } + + @TestMetadata("MapRemove.kt") + public void testMapRemove() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/MapRemove.kt"); + doTest(fileName); + } + + @TestMetadata("Remove.kt") + public void testRemove() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/Remove.kt"); + doTest(fileName); + } + + @TestMetadata("RemoveAll.kt") + public void testRemoveAll() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/RemoveAll.kt"); + doTest(fileName); + } + + @TestMetadata("RetainAll.kt") + public void testRetainAll() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/heuristicSignatures/RetainAll.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/completion/smart/ifValue") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)