Code completion: do not list instance members from context with no instance

#KT-6388 Fixed
 #KT-4422 Fixed
 #KT-5516 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-12-19 21:42:15 +03:00
parent d33e60b876
commit 311a3a75c8
22 changed files with 307 additions and 18 deletions
@@ -27,7 +27,7 @@ public class InnerClassesScopeWrapper(override val workerScope: JetScope) : Abst
override fun getDescriptors(kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean): List<ClassDescriptor> {
val restrictedFilter = kindFilter.restrictedToKinds(DescriptorKindFilter.CLASSIFIERS_MASK) ?: return listOf()
val restrictedFilter = kindFilter.restrictedToKindsOrNull(DescriptorKindFilter.CLASSIFIERS_MASK) ?: return listOf()
return workerScope.getDescriptors(restrictedFilter, nameFilter).filterIsInstance<ClassDescriptor>()
}
@@ -91,6 +91,7 @@ public fun JetScope.getDescriptorsFiltered(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
if (kindFilter.kindMask == 0) return listOf()
return getDescriptors(kindFilter, nameFilter).filter { kindFilter.accepts(it) && nameFilter(it.getName()) }
}
@@ -113,7 +114,10 @@ public class DescriptorKindFilter(
public fun withKinds(kinds: Int): DescriptorKindFilter
= DescriptorKindFilter(kindMask or kinds, excludes)
public fun restrictedToKinds(kinds: Int): DescriptorKindFilter? {
public fun restrictedToKinds(kinds: Int): DescriptorKindFilter
= DescriptorKindFilter(kindMask and kinds, excludes)
public fun restrictedToKindsOrNull(kinds: Int): DescriptorKindFilter? {
val mask = kindMask and kinds
if (mask == 0) return null
return DescriptorKindFilter(mask, excludes)
@@ -38,6 +38,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.plugin.util.CallType
import org.jetbrains.jet.plugin.util.substituteExtensionIfCallable
import org.jetbrains.jet.plugin.util.getImplicitReceiversWithInstance
public class ReferenceVariantsHelper(
private val context: BindingContext,
@@ -72,13 +73,11 @@ public class ReferenceVariantsHelper(
val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
if (parent is JetImportDirective || parent is JetPackageDirective) {
val restrictedFilter = kindFilter.restrictedToKinds(DescriptorKindFilter.PACKAGES_MASK) ?: return listOf()
return resolutionScope.getDescriptorsFiltered(restrictedFilter, nameFilter)
return resolutionScope.getDescriptorsFiltered(kindFilter.restrictedToKinds(DescriptorKindFilter.PACKAGES_MASK), nameFilter)
}
if (parent is JetUserType) {
val restrictedFilter = kindFilter.restrictedToKinds(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK) ?: return listOf()
return resolutionScope.getDescriptorsFiltered(restrictedFilter, nameFilter)
return resolutionScope.getDescriptorsFiltered(kindFilter.restrictedToKinds(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK), nameFilter)
}
val pair = getExplicitReceiverData(expression)
@@ -112,7 +111,8 @@ public class ReferenceVariantsHelper(
else {
val descriptorsSet = HashSet<DeclarationDescriptor>()
val receivers = resolutionScope.getImplicitReceiversHierarchy()
// process instance members that can be called via implicit receiver's instances
val receivers = resolutionScope.getImplicitReceiversWithInstance()
receivers.flatMapTo(descriptorsSet) {
it.getType().getMemberScope().getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter)
}
@@ -120,7 +120,10 @@ public class ReferenceVariantsHelper(
val dataFlowInfo = context.getDataFlowInfo(expression)
val receiverValues = receivers.map { it.getValue() }
// process extensions and non-instance members
for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) {
if (descriptor is CallableDescriptor && descriptor.getDispatchReceiverParameter() != null) continue // should already be processed via implicit receivers
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.NORMAL))
}
@@ -164,7 +167,7 @@ public class ReferenceVariantsHelper(
}
else {
val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return ReceiversData.Empty
return ReceiversData(resolutionScope.getImplicitReceiversHierarchy().map { it.getValue() }, CallType.NORMAL)
return ReceiversData(resolutionScope.getImplicitReceiversWithInstance().map { it.getValue() }, CallType.NORMAL)
}
}
@@ -185,9 +188,23 @@ public class ReferenceVariantsHelper(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
) {
if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) {
for (callable in resolutionScope.getDescriptorsFiltered(kindFilter.exclude(DescriptorKindExclude.NonExtensions), nameFilter)) {
addAll((callable as CallableDescriptor).substituteExtensionIfCallable(receiver, callType, context, dataFlowInfo))
if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return
val extensionsFilter = kindFilter.exclude(DescriptorKindExclude.NonExtensions)
fun processExtension(descriptor: DeclarationDescriptor) {
addAll((descriptor as CallableDescriptor).substituteExtensionIfCallable(receiver, callType, context, dataFlowInfo))
}
// process member extensions from implicit receivers separately to filter out ones from implicit receivers with no instance
for (implicitReceiver in resolutionScope.getImplicitReceiversWithInstance()) {
for (extension in implicitReceiver.getType().getMemberScope().getDescriptorsFiltered(extensionsFilter, nameFilter)) {
processExtension(extension)
}
}
for (extension in resolutionScope.getDescriptorsFiltered(extensionsFilter, nameFilter)) {
if ((extension as CallableDescriptor).getDispatchReceiverParameter() == null) { // otherwise it should already be processed via implicit receivers
processExtension(extension)
}
}
}
@@ -56,7 +56,7 @@ public fun CallableDescriptor.substituteExtensionIfCallable(receivers: Collectio
}
public fun CallableDescriptor.substituteExtensionIfCallableWithImplicitReceiver(scope: JetScope, context: BindingContext, dataFlowInfo: DataFlowInfo): Collection<CallableDescriptor>
= substituteExtensionIfCallable(scope.getImplicitReceiversHierarchy().map { it.getValue() }, context, dataFlowInfo, CallType.NORMAL)
= substituteExtensionIfCallable(scope.getImplicitReceiversWithInstance().map { it.getValue() }, context, dataFlowInfo, CallType.NORMAL)
public fun CallableDescriptor.substituteExtensionIfCallable(
receiver: ReceiverValue,
@@ -0,0 +1,43 @@
/*
* 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.util
import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import java.util.LinkedHashSet
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import java.util.HashSet
public fun JetScope.getImplicitReceiversWithInstance(): List<ReceiverParameterDescriptor> {
// we use a set to workaround a bug with receiver for class object present twice in the result of getImplicitReceiversHierarchy()
val receivers = LinkedHashSet(getImplicitReceiversHierarchy())
val withInstance = HashSet<DeclarationDescriptor>()
var current: DeclarationDescriptor? = getContainingDeclaration()
while (current != null) {
withInstance.add(current)
val classDescriptor = current as? ClassDescriptor
if (classDescriptor != null && !classDescriptor.isInner()) break
current = current!!.getContainingDeclaration()
}
return receivers.filter {
val owner = it.getContainingDeclaration()
owner is ClassDescriptor && owner.getKind().isSingleton() || owner in withInstance
}
}
@@ -37,6 +37,7 @@ import org.jetbrains.jet.plugin.util.substituteExtensionIfCallable
import org.jetbrains.jet.plugin.util.CallType
import org.jetbrains.jet.plugin.codeInsight.ReferenceVariantsHelper
import org.jetbrains.jet.utils.addToStdlib.singletonOrEmptyList
import org.jetbrains.jet.plugin.util.getImplicitReceiversWithInstance
public class KotlinIndicesHelper(
private val project: Project,
@@ -123,7 +124,7 @@ public class KotlinIndicesHelper(
else {
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return
for (receiver in resolutionScope.getImplicitReceiversHierarchy()) {
for (receiver in resolutionScope.getImplicitReceiversWithInstance()) {
matchingNames.flatMapTo(this) {
findSuitableExtensions(it, index, receiver.getValue(), dataFlowInfo, CallType.NORMAL, resolutionScope, moduleDescriptor, bindingContext)
}
@@ -31,14 +31,13 @@ import org.jetbrains.jet.plugin.completion.ExpectedInfo
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression
import org.jetbrains.jet.plugin.util.FuzzyType
import org.jetbrains.jet.plugin.util.getImplicitReceiversWithInstance
class ThisItems(val bindingContext: BindingContext) {
public fun addToCollection(collection: MutableCollection<LookupElement>, context: JetExpression, expectedInfos: Collection<ExpectedInfo>) {
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
for (i in 0..receivers.size - 1) {
val receiver = receivers[i]
for ((i, receiver) in scope.getImplicitReceiversWithInstance().withIndex()) {
val thisType = receiver.getType()
val fuzzyType = FuzzyType(thisType, listOf())
val classifier = { (expectedInfo: ExpectedInfo) -> fuzzyType.classifyExpectedInfo(expectedInfo) }
@@ -0,0 +1,25 @@
class C {
val field = 0
class NestedClass
inner class InnerClass
object AnObject
class object {
val classObjectField = 0
class ClassObjectClass
fun foo(){
<caret>
}
}
}
fun C.extensionForC(){}
// ABSENT: field
// EXIST: NestedClass
// EXIST: InnerClass
// EXIST: AnObject
// EXIST: classObjectField
// EXIST: ClassObjectClass
// ABSENT: extensionForC
@@ -0,0 +1,18 @@
class C {
val field = 0
class NestedClass
inner class InnerClass
inner class N {
fun foo(){
<caret>
}
}
}
fun C.extensionForC(){}
// EXIST: field
// EXIST: NestedClass
// EXIST: InnerClass
// EXIST: extensionForC
@@ -0,0 +1,23 @@
class C {
val field = 0
class NestedClass
inner class InnerClass
class N {
fun foo(){
<caret>
}
}
class object {
fun fromClassObject(){}
}
}
fun C.extensionForC(){}
// ABSENT: field
// EXIST: NestedClass
// EXIST: InnerClass
// EXIST: fromClassObject
// ABSENT: extensionForC
@@ -0,0 +1,11 @@
object O {
fun foo() {}
class Nested {
fun bar() {
<caret>
}
}
}
// EXIST: foo
@@ -1,7 +1,7 @@
package a.b
class Some {
class Nested {
inner class Inner {
fun foo() {
"".<caret>
}
@@ -17,5 +17,5 @@ val String.extProp: Int get() = 1
// EXIST: { lookupString: "extFun", itemText: "extFun", tailText: "() for String in a.b", typeText: "Unit" }
// EXIST: { lookupString: "extProp", itemText: "extProp", tailText: " for String in a.b", typeText: "Int" }
// EXIST: { lookupString: "memberExtFun", itemText: "memberExtFun", tailText: "() for String in Some.Nested", typeText: "Unit" }
// EXIST: { lookupString: "memberExtFun", itemText: "memberExtFun", tailText: "() for String in Some.Inner", typeText: "Unit" }
// EXIST: { lookupString: "memberExtProp", itemText: "memberExtProp", tailText: " for String in Some", typeText: "Int" }
@@ -0,0 +1,11 @@
class X {
fun String.f() {}
}
fun fn() {
with (X()) {
"sss".<caret>
}
}
// EXIST: f
@@ -0,0 +1,11 @@
class C {
fun String.memberExtForString(){}
class object {
fun foo() {
"".<caret>
}
}
}
// ABSENT: memberExtForString
@@ -0,0 +1,6 @@
package second
import first.OuterClass
fun OuterClass.outerExtension() {
}
@@ -0,0 +1,12 @@
package first
class OuterClass {
class Nested {
fun foo() {
out<caret>
}
}
}
// INVOCATION_COUNT: 2
// ABSENT: outerExtension
@@ -0,0 +1,9 @@
class C {
class object {
fun foo(): C {
return <caret>
}
}
}
// ABSENT: this@C
@@ -0,0 +1,9 @@
class Outer {
class Nested {
fun foo(): Outer {
return <caret>
}
}
}
// ABSENT: this@Outer
@@ -400,6 +400,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("InClassObject.kt")
public void testInClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InClassObject.kt");
doTest(fileName);
}
@TestMetadata("InClassPropertyAccessor.kt")
public void testInClassPropertyAccessor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InClassPropertyAccessor.kt");
@@ -472,6 +478,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("InInnerClass.kt")
public void testInInnerClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InInnerClass.kt");
doTest(fileName);
}
@TestMetadata("InLocalObjectDeclaration.kt")
public void testInLocalObjectDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InLocalObjectDeclaration.kt");
@@ -496,6 +508,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("InNestedClass.kt")
public void testInNestedClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InNestedClass.kt");
doTest(fileName);
}
@TestMetadata("InObjectInDelegationSpecifier.kt")
public void testInObjectInDelegationSpecifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InObjectInDelegationSpecifier.kt");
@@ -724,6 +742,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("ObjectMembersFromNested.kt")
public void testObjectMembersFromNested() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ObjectMembersFromNested.kt");
doTest(fileName);
}
@TestMetadata("ObjectRedeclaration1.kt")
public void testObjectRedeclaration1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ObjectRedeclaration1.kt");
@@ -918,12 +942,24 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("MemberExtensionFromWith.kt")
public void testMemberExtensionFromWith() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/MemberExtensionFromWith.kt");
doTest(fileName);
}
@TestMetadata("NoDuplicatedMethodForSmartCast.kt")
public void testNoDuplicatedMethodForSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/NoDuplicatedMethodForSmartCast.kt");
doTest(fileName);
}
@TestMetadata("NoExtensionFromOuter.kt")
public void testNoExtensionFromOuter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/NoExtensionFromOuter.kt");
doTest(fileName);
}
@TestMetadata("WrongExplicitReceiver.kt")
public void testWrongExplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/WrongExplicitReceiver.kt");
@@ -400,6 +400,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("InClassObject.kt")
public void testInClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InClassObject.kt");
doTest(fileName);
}
@TestMetadata("InClassPropertyAccessor.kt")
public void testInClassPropertyAccessor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InClassPropertyAccessor.kt");
@@ -472,6 +478,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("InInnerClass.kt")
public void testInInnerClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InInnerClass.kt");
doTest(fileName);
}
@TestMetadata("InLocalObjectDeclaration.kt")
public void testInLocalObjectDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InLocalObjectDeclaration.kt");
@@ -496,6 +508,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("InNestedClass.kt")
public void testInNestedClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InNestedClass.kt");
doTest(fileName);
}
@TestMetadata("InObjectInDelegationSpecifier.kt")
public void testInObjectInDelegationSpecifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/InObjectInDelegationSpecifier.kt");
@@ -724,6 +742,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("ObjectMembersFromNested.kt")
public void testObjectMembersFromNested() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ObjectMembersFromNested.kt");
doTest(fileName);
}
@TestMetadata("ObjectRedeclaration1.kt")
public void testObjectRedeclaration1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/ObjectRedeclaration1.kt");
@@ -918,12 +942,24 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("MemberExtensionFromWith.kt")
public void testMemberExtensionFromWith() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/MemberExtensionFromWith.kt");
doTest(fileName);
}
@TestMetadata("NoDuplicatedMethodForSmartCast.kt")
public void testNoDuplicatedMethodForSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/NoDuplicatedMethodForSmartCast.kt");
doTest(fileName);
}
@TestMetadata("NoExtensionFromOuter.kt")
public void testNoExtensionFromOuter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/NoExtensionFromOuter.kt");
doTest(fileName);
}
@TestMetadata("WrongExplicitReceiver.kt")
public void testWrongExplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/common/extensions/WrongExplicitReceiver.kt");
@@ -372,6 +372,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName);
}
@TestMetadata("InClassObject.kt")
public void testInClassObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/InClassObject.kt");
doTest(fileName);
}
@TestMetadata("InElvisOperator1.kt")
public void testInElvisOperator1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/InElvisOperator1.kt");
@@ -408,6 +414,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName);
}
@TestMetadata("InNestedClass.kt")
public void testInNestedClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/InNestedClass.kt");
doTest(fileName);
}
@TestMetadata("InaccessibleConstructor.kt")
public void testInaccessibleConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/InaccessibleConstructor.kt");
@@ -120,6 +120,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
doTest(fileName);
}
@TestMetadata("NoExtForOuterFromNested")
public void testNoExtForOuterFromNested() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NoExtForOuterFromNested/");
doTest(fileName);
}
@TestMetadata("NoExtensionMethodDuplication")
public void testNoExtensionMethodDuplication() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NoExtensionMethodDuplication/");