Completion: fixed filtering shadowed declarations for generic functions
This commit is contained in:
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.resolve.scopes.ChainedScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.ExplicitImportsScope
|
||||
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
|
||||
@@ -168,8 +169,12 @@ public class ShadowedDeclarationsFilter(
|
||||
CompositeChecker(listOf()), SymbolUsageValidator.Empty, AdditionalTypeChecker.Composite(listOf()), false)
|
||||
val callResolver = createContainerForMacros(project, moduleDescriptor).callResolver
|
||||
val results = if (isFunction) callResolver.resolveFunctionCall(context) else callResolver.resolveSimpleProperty(context)
|
||||
val resultingDescriptors = results.getResultingCalls().map { it.getResultingDescriptor() }.toSet()
|
||||
val filtered = descriptors.filter { it in resultingDescriptors }
|
||||
val resultingDescriptors = results.getResultingCalls().map { it.getResultingDescriptor() }
|
||||
val resultingOriginals = resultingDescriptors.mapTo(HashSet<DeclarationDescriptor>()) { it.getOriginal() }
|
||||
val filtered = descriptors.filter { candidateDescriptor ->
|
||||
candidateDescriptor.getOriginal() in resultingOriginals /* optimization */
|
||||
&& resultingDescriptors.any { descriptorsEqualWithSubstitution(it, candidateDescriptor) }
|
||||
}
|
||||
return if (filtered.isNotEmpty()) filtered else descriptors /* something went wrong, none of our declarations among resolve candidates, let's not filter anything */
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.util
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.equalTypesOrNulls
|
||||
|
||||
public 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
|
||||
|
||||
val typeChecker = JetTypeChecker.withAxioms(object: JetTypeChecker.TypeConstructorEquality {
|
||||
override fun equals(a: TypeConstructor, b: TypeConstructor): Boolean {
|
||||
val typeParam1 = a.getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
val typeParam2 = b.getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
if (typeParam1 != null
|
||||
&& typeParam2 != null
|
||||
&& typeParam1.getContainingDeclaration() == descriptor1
|
||||
&& typeParam2.getContainingDeclaration() == descriptor2) {
|
||||
return typeParam1.getIndex() == typeParam2.getIndex()
|
||||
}
|
||||
|
||||
return a == b
|
||||
}
|
||||
})
|
||||
|
||||
if (!typeChecker.equalTypesOrNulls(descriptor1.getReturnType(), descriptor2.getReturnType())) return false
|
||||
|
||||
val parameters1 = descriptor1.getValueParameters()
|
||||
val parameters2 = descriptor2.getValueParameters()
|
||||
if (parameters1.size() != parameters2.size()) return false
|
||||
for ((param1, param2) in parameters1.zip(parameters2)) {
|
||||
if (!typeChecker.equalTypes(param1.getType(), param2.getType())) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -140,42 +140,6 @@ enum class CallableWeight {
|
||||
|
||||
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
|
||||
|
||||
val typeChecker = JetTypeChecker.withAxioms(object: JetTypeChecker.TypeConstructorEquality {
|
||||
override fun equals(a: TypeConstructor, b: TypeConstructor): Boolean {
|
||||
val typeParam1 = a.getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
val typeParam2 = b.getDeclarationDescriptor() as? TypeParameterDescriptor
|
||||
if (typeParam1 != null
|
||||
&& typeParam2 != null
|
||||
&& typeParam1.getContainingDeclaration() == descriptor1
|
||||
&& typeParam2.getContainingDeclaration() == descriptor2) {
|
||||
return typeParam1.getIndex() == typeParam2.getIndex()
|
||||
}
|
||||
|
||||
return a == b
|
||||
}
|
||||
})
|
||||
|
||||
if (!typeChecker.equalTypesOrNulls(descriptor1.getReturnType(), descriptor2.getReturnType())) return false
|
||||
|
||||
val parameters1 = descriptor1.getValueParameters()
|
||||
val parameters2 = descriptor2.getValueParameters()
|
||||
if (parameters1.size() != parameters2.size()) return false
|
||||
for ((param1, param2) in parameters1.zip(parameters2)) {
|
||||
if (!typeChecker.equalTypes(param1.getType(), param2.getType())) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun InsertionContext.isAfterDot(): Boolean {
|
||||
var offset = getStartOffset()
|
||||
val chars = getDocument().getCharsSequence()
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
|
||||
import javax.swing.Icon
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ 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.util.descriptorsEqualWithSubstitution
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import java.util.HashSet
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
interface I<T> {
|
||||
fun xxx(): T
|
||||
}
|
||||
|
||||
fun foo(i1: I<Int>, i2: I<String>) {
|
||||
with (i1) {
|
||||
with (i2) {
|
||||
xx<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "xxx", itemText: "xxx", tailText: "()", typeText: "String" }
|
||||
// NOTHING_ELSE
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fun <T> List<T>.xxx(){}
|
||||
fun <T> Iterable<T>.xxx(){}
|
||||
|
||||
fun foo() {
|
||||
listOf(1).xx<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "xxx", itemText: "xxx", tailText: "() for List<T> in <root>", typeText: "Unit" }
|
||||
// NOTHING_ELSE
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
fun <T> List<T>.xxx(t: T){}
|
||||
fun <T> Iterable<T>.xxx(t: T){}
|
||||
|
||||
fun foo() {
|
||||
listOf(1).xx<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "xxx", itemText: "xxx", tailText: "(t: Int) for List<T> in <root>", typeText: "Unit" }
|
||||
// NOTHING_ELSE
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package dependency
|
||||
|
||||
interface Iterable<T>
|
||||
interface List<T> : Iterable<T>
|
||||
|
||||
fun <T> List<T>.xxx(t: T){}
|
||||
fun <T> Iterable<T>.xxx(t: T){}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
fun foo(list: dependency.List<Int>) {
|
||||
list.xx<caret>
|
||||
}
|
||||
|
||||
// EXIST: { lookupString: "xxx", itemText: "xxx", tailText: "(t: Int) for List<T> in dependency", typeText: "Unit" }
|
||||
// NOTHING_ELSE
|
||||
+18
@@ -1650,6 +1650,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferCloserReceiverGeneric.kt")
|
||||
public void testPreferCloserReceiverGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferCloserReceiverGeneric.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMemberExtension.kt")
|
||||
public void testPreferMemberExtension() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMemberExtension.kt");
|
||||
@@ -1673,6 +1679,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtension.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMoreSpecificExtensionGeneric.kt")
|
||||
public void testPreferMoreSpecificExtensionGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtensionGeneric.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMoreSpecificExtensionGenericWithParam.kt")
|
||||
public void testPreferMoreSpecificExtensionGenericWithParam() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtensionGenericWithParam.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/typeArgsOrNot")
|
||||
|
||||
+18
@@ -1650,6 +1650,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferCloserReceiverGeneric.kt")
|
||||
public void testPreferCloserReceiverGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferCloserReceiverGeneric.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMemberExtension.kt")
|
||||
public void testPreferMemberExtension() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMemberExtension.kt");
|
||||
@@ -1673,6 +1679,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtension.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMoreSpecificExtensionGeneric.kt")
|
||||
public void testPreferMoreSpecificExtensionGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtensionGeneric.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMoreSpecificExtensionGenericWithParam.kt")
|
||||
public void testPreferMoreSpecificExtensionGenericWithParam() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/PreferMoreSpecificExtensionGenericWithParam.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/typeArgsOrNot")
|
||||
|
||||
+6
@@ -137,6 +137,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MoreSpecificExtensionGeneric")
|
||||
public void testMoreSpecificExtensionGeneric() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/MoreSpecificExtensionGeneric/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MoreSpecificExtensionInDifferentPackage")
|
||||
public void testMoreSpecificExtensionInDifferentPackage() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/MoreSpecificExtensionInDifferentPackage/");
|
||||
|
||||
Reference in New Issue
Block a user