From b185316a6e428b8402cbc2161750409ea5873f51 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Fri, 15 Jan 2016 18:41:02 +0300 Subject: [PATCH] Create new HidesMembers annotation --- .../kotlin/resolve/calls/tower/TowerLevels.kt | 26 ++++++++++ .../resolve/calls/tower/TowerResolver.kt | 6 +++ .../testsWithStdLib/resolve/hidesMembers.kt | 52 +++++++++++++++++++ .../testsWithStdLib/resolve/hidesMembers.txt | 22 ++++++++ .../testsWithStdLib/resolve/hidesMembers2.kt | 42 +++++++++++++++ .../testsWithStdLib/resolve/hidesMembers2.txt | 22 ++++++++ .../DiagnosticsTestWithStdLibGenerated.java | 12 +++++ .../kotlin/resolve/annotationsForResolve.kt | 11 ++-- .../stdlib/src/kotlin/internal/Annotations.kt | 8 +++ 9 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt index eb3ed0b005e..69fc9f1f9a3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerLevels.kt @@ -21,13 +21,17 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject +import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassValueDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.hasHidesMembersAnnotation import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution import org.jetbrains.kotlin.resolve.scopes.* import org.jetbrains.kotlin.resolve.scopes.receivers.CastImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions +import org.jetbrains.kotlin.resolve.scopes.utils.collectVariables import org.jetbrains.kotlin.resolve.selectMostSpecificInEachOverridableGroup import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType @@ -184,6 +188,28 @@ internal class SyntheticScopeBasedTowerLevel( } } +internal class HidesMembersTowerLevel(scopeTower: ScopeTower): AbstractScopeTowerLevel(scopeTower) { + override fun getVariables(name: Name, extensionReceiver: ReceiverValue?) + = getCandidates(name, extensionReceiver, LexicalScope::collectVariables) + + override fun getFunctions(name: Name, extensionReceiver: ReceiverValue?) + = getCandidates(name, extensionReceiver, LexicalScope::collectFunctions) + + private fun getCandidates( + name: Name, + extensionReceiver: ReceiverValue?, + collectCandidates: LexicalScope.(Name, LookupLocation) -> Collection + ): Collection> { + if (extensionReceiver == null || name !in HIDES_MEMBERS_NAME_LIST) return emptyList() + + return scopeTower.lexicalScope.collectCandidates(name, location).filter { + it.extensionReceiverParameter != null && it.hasHidesMembersAnnotation() + }.map { + createCandidateDescriptor(it, dispatchReceiver = null) + } + } +} + private fun KotlinType.getClassifierFromMeAndSuperclasses(name: Name, location: LookupLocation): ClassifierDescriptor? { var superclass: KotlinType? = this while (superclass != null) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt index 4f22ffa99fe..77bb5ecf9ef 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/TowerResolver.kt @@ -103,8 +103,11 @@ class TowerResolver { map { ScopeBasedTowerLevel(this@createTowerDataList, it) } val nonLocalLevels = createNonLocalLevels() + val hidesMembersLevel = HidesMembersTowerLevel(this) val syntheticLevel = SyntheticScopeBasedTowerLevel(this, syntheticScopes) + // hides members extensions for explicit receiver + + TowerData.TowerLevel(hidesMembersLevel) // possibly there is explicit member + TowerData.Empty // synthetic member for explicit receiver @@ -124,6 +127,9 @@ class TowerResolver { val implicitReceiver = scope.implicitReceiver?.value if (implicitReceiver != null) { + // hides members extensions + + TowerData.BothTowerLevelAndImplicitReceiver(hidesMembersLevel, implicitReceiver) + // members of implicit receiver or member extension for explicit receiver + TowerData.TowerLevel(ReceiverScopeTowerLevel(this, implicitReceiver)) diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.kt b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.kt new file mode 100644 index 00000000000..a7a2c731369 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.kt @@ -0,0 +1,52 @@ +// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE + +// FILE: 2.kt +package b + +import a.A + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.HidesMembers +fun A.forEach(i: Int) = i + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.HidesMembers +fun A.forEach(s: String) {} + + +// FILE: 1.kt +package a + +import b.* + +class A { + fun forEach() = this + fun forEach(i: Int) = this + fun forEach(i: String) = this +} + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.HidesMembers +fun A.forEach() = "" + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.HidesMembers +fun A.forEach(s: String) {} + +fun test(a: A) { + a.forEach() checkType { _() } + + a.forEach(1) checkType { _() } + + a.forEach("") + + with(a) { + forEach() checkType { _() } + + forEach(1) checkType { _() } + + forEach("") + } +} + diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.txt b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.txt new file mode 100644 index 00000000000..c9967650c93 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.txt @@ -0,0 +1,22 @@ +package + +package a { + public fun test(/*0*/ a: a.A): kotlin.Unit + @kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public fun a.A.forEach(): kotlin.String + @kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public fun a.A.forEach(/*0*/ s: kotlin.String): kotlin.Unit + + public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun forEach(): a.A + public final fun forEach(/*0*/ i: kotlin.Int): a.A + public final fun forEach(/*0*/ i: kotlin.String): a.A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +package b { + @kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public fun a.A.forEach(/*0*/ i: kotlin.Int): kotlin.Int + @kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public fun a.A.forEach(/*0*/ s: kotlin.String): kotlin.Unit +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.kt b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.kt new file mode 100644 index 00000000000..f32a50b10df --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.kt @@ -0,0 +1,42 @@ +// !CHECK_TYPE +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE +class A { + fun forEach() = this + fun forEach(i: Int) = this +} + +@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@kotlin.internal.HidesMembers +fun A.forEach(i: Int) = i + +class B { + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.HidesMembers + fun A.forEach() = this@B + + fun test(a: A) { + a.forEach() checkType { _() } // todo + + with(a) { + forEach() checkType { _() } // todo + } + } +} + +fun test2(a: A) { + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.HidesMembers + fun A.forEach() = "" + + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.HidesMembers + fun A.forEach(i: Int) = "" + + a.forEach() checkType { _() } + a.forEach(1) + + with(a) { + forEach() checkType { _() } + forEach(1) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.txt b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.txt new file mode 100644 index 00000000000..73f9d3540a2 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.txt @@ -0,0 +1,22 @@ +package + +public fun test2(/*0*/ a: A): kotlin.Unit +@kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public fun A.forEach(/*0*/ i: kotlin.Int): kotlin.Int + +public final class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun forEach(): A + public final fun forEach(/*0*/ i: kotlin.Int): A + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(/*0*/ a: A): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + @kotlin.Suppress(names = {"INVISIBLE_MEMBER", "INVISIBLE_REFERENCE"}) @kotlin.internal.HidesMembers() public final fun A.forEach(): B +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 9b15fc3467e..86f82725ae1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1021,6 +1021,18 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/resolve"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("hidesMembers.kt") + public void testHidesMembers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers.kt"); + doTest(fileName); + } + + @TestMetadata("hidesMembers2.kt") + public void testHidesMembers2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/resolve/hidesMembers2.kt"); + doTest(fileName); + } + @TestMetadata("javaPackageMembers.kt") public void testJavaPackageMembers() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/resolve/javaPackageMembers.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt index 5f4a408ae73..f80f13295dd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt @@ -22,10 +22,17 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType private val NO_INFER_ANNOTATION_FQ_NAME = FqName("kotlin.internal.NoInfer") private val EXACT_ANNOTATION_FQ_NAME = FqName("kotlin.internal.Exact") +private val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.internal.LowPriorityInOverloadResolution") +private val HIDES_MEMBERS_ANNOTATION_FQ_NAME = FqName("kotlin.internal.HidesMembers") +private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes") + +// @HidesMembers annotation only has effect for members with these names +val HIDES_MEMBERS_NAME_LIST = setOf(Name.identifier("forEach")) fun KotlinType.hasNoInferAnnotation(): Boolean = annotations.hasAnnotation(NO_INFER_ANNOTATION_FQ_NAME) @@ -36,11 +43,9 @@ fun Annotations.hasInternalAnnotationForResolve(): Boolean = fun FqName.isInternalAnnotationForResolve() = this == NO_INFER_ANNOTATION_FQ_NAME || this == EXACT_ANNOTATION_FQ_NAME -private val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.internal.LowPriorityInOverloadResolution") - fun CallableDescriptor.hasLowPriorityInOverloadResolution(): Boolean = annotations.hasAnnotation(LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME) -private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes") +fun CallableDescriptor.hasHidesMembersAnnotation(): Boolean = annotations.hasAnnotation(HIDES_MEMBERS_ANNOTATION_FQ_NAME) fun TypeParameterDescriptor.hasOnlyInputTypesAnnotation(): Boolean = annotations.hasAnnotation(ONLY_INPUT_TYPES_FQ_NAME) diff --git a/libraries/stdlib/src/kotlin/internal/Annotations.kt b/libraries/stdlib/src/kotlin/internal/Annotations.kt index 1ff19c7ce47..ed31c96b3f2 100644 --- a/libraries/stdlib/src/kotlin/internal/Annotations.kt +++ b/libraries/stdlib/src/kotlin/internal/Annotations.kt @@ -37,6 +37,14 @@ internal annotation class Exact @Retention(AnnotationRetention.BINARY) internal annotation class LowPriorityInOverloadResolution +/** + * Specifies that the corresponding member has the highest priority in overload resolution. Effectively this means that + * an extension annotated with this annotation will win in overload resolution over a member with the same signature. + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +internal annotation class HidesMembers + /** * The value of this type parameter should be mentioned in input types (argument types, receiver type or expected type). */