diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index 32eb4f4eb19..bb84530891e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -101,6 +101,9 @@ public interface BindingContext { */ WritableSlice QUALIFIER = new BasicWritableSlice(DO_NOTHING); + WritableSlice THIS_TYPE_FOR_SUPER_EXPRESSION = + new BasicWritableSlice(DO_NOTHING); + WritableSlice REFERENCE_TARGET = new BasicWritableSlice(DO_NOTHING); // if 'A' really means 'A.Companion' then this slice stores class descriptor for A, REFERENCE_TARGET stores descriptor Companion in this case diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/receivers/ExpressionReceiver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/receivers/ExpressionReceiver.kt index fe1b77a00ee..47289d2b54e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/receivers/ExpressionReceiver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/receivers/ExpressionReceiver.kt @@ -17,10 +17,7 @@ package org.jetbrains.kotlin.resolve.scopes.receivers import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtReferenceExpression -import org.jetbrains.kotlin.psi.KtThisExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.KotlinType @@ -40,6 +37,12 @@ interface ExpressionReceiver : ReceiverValue { type: KotlinType ) : ExpressionReceiverImpl(expression, type), ThisClassReceiver + private class SuperExpressionReceiver( + override val thisType: KotlinType, + expression: KtExpression, + type: KotlinType + ) : ExpressionReceiverImpl(expression, type), SuperCallReceiverValue + fun create( expression: KtExpression, type: KotlinType, @@ -59,6 +62,13 @@ interface ExpressionReceiver : ReceiverValue { return ThisExpressionClassReceiver(descriptor.original as ClassDescriptor, expression, type) } } + else if (expression is KtSuperExpression) { + // if there is no THIS_TYPE_FOR_SUPER_EXPRESSION in binding context, we fall through into more restrictive option + // i.e. just return common ExpressionReceiverImpl + bindingContext[BindingContext.THIS_TYPE_FOR_SUPER_EXPRESSION, expression]?.let { + thisType -> return SuperExpressionReceiver(thisType, expression, type) + } + } return ExpressionReceiverImpl(expression, type) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 7ef488e7602..ada98256188 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -502,6 +502,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { context.trace.recordType(expression.getInstanceReference(), result); context.trace.record(BindingContext.REFERENCE_TARGET, expression.getInstanceReference(), result.getConstructor().getDeclarationDescriptor()); + context.trace.record(THIS_TYPE_FOR_SUPER_EXPRESSION, expression, thisType); } BindingContextUtilsKt.recordScope(context.trace, context.scope, superTypeQualifier); diff --git a/compiler/testData/diagnostics/tests/scopes/kt9430.kt b/compiler/testData/diagnostics/tests/scopes/kt9430.kt index f68468e270c..cb743b1d27d 100644 --- a/compiler/testData/diagnostics/tests/scopes/kt9430.kt +++ b/compiler/testData/diagnostics/tests/scopes/kt9430.kt @@ -6,7 +6,7 @@ class B: A() class C: A() { fun bar() { - A().foo() + A().foo() B().foo() } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.kt new file mode 100644 index 00000000000..8adefea99ab --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.kt @@ -0,0 +1,18 @@ +open class A { + protected fun foo() {} + + init { + B.foo() // Ok, receiver (B.Companion) is subtype of A + (B.Companion).foo() + } +} + +class B { + companion object : A() +} + +class C: A() { + init { + B.foo() // Error: receiver is not suitable + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.txt new file mode 100644 index 00000000000..3ffa80b4927 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.txt @@ -0,0 +1,32 @@ +package + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final fun foo(): kotlin.Unit + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion : A { + private constructor Companion() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final class C : A { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected final override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.kt new file mode 100644 index 00000000000..ef6809e9dba --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.kt @@ -0,0 +1,24 @@ +// FILE: p1/BaseClass.java + +package p1; + +public class BaseClass { + protected class ProtSubClass { + public ProtSubClass() {} + } +} + +// FILE: k1/main.kt +package k1 + +import p1.BaseClass + +class Foo : BaseClass() { + + fun foo() { + ProtSubClass() + super.ProtSubClass() + } + + private val v1: BaseClass.ProtSubClass = ProtSubClass() +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.txt new file mode 100644 index 00000000000..9471f0589be --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.txt @@ -0,0 +1,13 @@ +package + +package k1 { + + public final class Foo : p1.BaseClass { + public constructor Foo() + private final val v1: p1.BaseClass.ProtSubClass + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt new file mode 100644 index 00000000000..e434a606751 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt @@ -0,0 +1,58 @@ +// FILE: bar/JavaClass.java + +package bar; + +public class JavaClass { + protected void foo() {} + protected static void bar1() {} + protected static void bar2() {} + + protected String field = ""; + protected static String CONST1 = ""; + protected static String CONST2 = ""; + +} + +// FILE: foo/JavaClassSamePackage.java +package foo + +public class JavaClassSamePackage extends bar.JavaClass { + protected static void bar2() {} + protected static String CONST2 = ""; +} + +// FILE: foo/main.kt +package foo + +import bar.JavaClass + +class KotlinClass : JavaClass() { + fun baz() { + foo() // OK + } +} + +class KotlinClass2 : JavaClass() { + override fun foo() {} + + val field: String = "abc" +} + +fun test(a: KotlinClass, b: KotlinClass2) { + a.foo() // Error, protected_and_package declared in different package + b.foo() // Error, protected visibility in same package (but could be protected_and_package) + + a.field + + JavaClass.bar1() + JavaClass.CONST1 + + KotlinClass.bar1() // Currently it's unresolved, but it should be prohibited even in case it would be resolved + KotlinClass.CONST1 + + JavaClassSamePackage.bar1() + JavaClassSamePackage.bar2() + + JavaClassSamePackage.CONST1 + JavaClassSamePackage.CONST2 +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.txt new file mode 100644 index 00000000000..006dbddeee0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.txt @@ -0,0 +1,40 @@ +package + +package foo { + public fun test(/*0*/ a: foo.KotlinClass, /*1*/ b: foo.KotlinClass2): kotlin.Unit + + public open class JavaClassSamePackage : bar.JavaClass { + public constructor JavaClassSamePackage() + protected/*protected and package*/ final override /*1*/ /*fake_override*/ var field: kotlin.String! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + protected/*protected static*/ final override /*1*/ /*fake_override*/ var CONST1: kotlin.String! + protected/*protected static*/ final var CONST2: kotlin.String! + protected/*protected static*/ open override /*1*/ /*fake_override*/ fun bar1(): kotlin.Unit + protected/*protected static*/ open fun bar2(): kotlin.Unit + } + + public final class KotlinClass : bar.JavaClass { + public constructor KotlinClass() + protected/*protected and package*/ final override /*1*/ /*fake_override*/ var field: kotlin.String! + public final fun baz(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected/*protected and package*/ open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public final class KotlinClass2 : bar.JavaClass { + public constructor KotlinClass2() + public final val field: kotlin.String = "abc" + protected/*protected and package*/ final override /*1*/ /*fake_override*/ var field: kotlin.String! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected open override /*1*/ fun foo(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.kt new file mode 100644 index 00000000000..3025b9081f9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.kt @@ -0,0 +1,22 @@ +// !CHECK_TYPE +// FILE: module1/AbstractModule.java +package module1; + +public abstract class AbstractModule { + protected S bind(Class clazz) { return null; } +} + +// FILE: module2/main.kt +package module2 + +import module1.* + +fun javaClass(): Class = null!! + +public class AppServiceModule : AbstractModule() { + inline fun AbstractModule.bind() { + val x = bind(javaClass()) + + x checkType { _() } // check that Class receiver is used instead of extension one + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.txt new file mode 100644 index 00000000000..cf2d4596da7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.txt @@ -0,0 +1,14 @@ +package + +package module2 { + public fun javaClass(): java.lang.Class + + public final class AppServiceModule : module1.AbstractModule { + public constructor AppServiceModule() + protected/*protected and package*/ open override /*1*/ /*fake_override*/ fun bind(/*0*/ clazz: java.lang.Class!): kotlin.String! + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + public final inline fun module1.AbstractModule.bind(): kotlin.Unit + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt new file mode 100644 index 00000000000..985b6b63a61 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt @@ -0,0 +1,15 @@ +open class A { + open protected fun foo() { } + open protected fun foobaz() { } + + fun bar(x: B) { + x.foo() // OK, foo declared in A + x.baz() // Declared in B + x.foobaz() // Declared in B + } +} + +class B : A() { + protected fun baz() {} + override fun foobaz() {} +} diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.txt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.txt new file mode 100644 index 00000000000..21543c157e8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.txt @@ -0,0 +1,22 @@ +package + +public open class A { + public constructor A() + public final fun bar(/*0*/ x: B): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected open fun foo(): kotlin.Unit + protected open fun foobaz(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class B : A { + public constructor B() + public final override /*1*/ /*fake_override*/ fun bar(/*0*/ x: B): kotlin.Unit + protected final fun baz(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + protected open override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit + protected open override /*1*/ fun foobaz(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/scopes/visibility.kt b/compiler/testData/diagnostics/tests/scopes/visibility.kt index 249b34cf221..26e62c7733c 100644 --- a/compiler/testData/diagnostics/tests/scopes/visibility.kt +++ b/compiler/testData/diagnostics/tests/scopes/visibility.kt @@ -72,7 +72,7 @@ class E : C() { class F : C() { fun test8(c: C) { - doSmth(c.i) + doSmth(c.i) } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index bd100827125..0135f5469dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -15455,6 +15455,45 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { } } } + + @TestMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ProtectedVisibility extends AbstractDiagnosticsTest { + public void testAllFilesPresentInProtectedVisibility() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/protectedVisibility"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("complexCompanion.kt") + public void testComplexCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.kt"); + doTest(fileName); + } + + @TestMetadata("innerClassInJava.kt") + public void testInnerClassInJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/innerClassInJava.kt"); + doTest(fileName); + } + + @TestMetadata("javaInheritedInKotlin.kt") + public void testJavaInheritedInKotlin() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/javaInheritedInKotlin.kt"); + doTest(fileName); + } + + @TestMetadata("kt7971.kt") + public void testKt7971() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.kt"); + doTest(fileName); + } + + @TestMetadata("protectedCallOnSubClass.kt") + public void testProtectedCallOnSubClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/scopes/protectedVisibility/protectedCallOnSubClass.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/tests/script") diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaVisibilities.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaVisibilities.java index 0c399ea79cd..6337be2695f 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaVisibilities.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JavaVisibilities.java @@ -66,21 +66,7 @@ public class JavaVisibilities { public static final Visibility PROTECTED_STATIC_VISIBILITY = new Visibility("protected_static", true) { @Override public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { - if (areInSamePackage(what, from)) { - return true; - } - - ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false); - if (fromClass == null) return false; - - DeclarationDescriptor containingDeclaration = what.getContainingDeclaration(); - assert containingDeclaration instanceof ClassDescriptor : "Only class members can have protected_static visibility"; - ClassDescriptor whatClass = (ClassDescriptor) containingDeclaration; - - if (DescriptorUtils.isSubclass(fromClass, whatClass)) { - return true; - } - return isVisible(receiver, what, fromClass.getContainingDeclaration()); + return isVisibleForProtectedAndPackage(receiver, what, from); } @Override @@ -104,20 +90,7 @@ public class JavaVisibilities { public static final Visibility PROTECTED_AND_PACKAGE = new Visibility("protected_and_package", true) { @Override public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { - if (areInSamePackage(what, from)) { - return true; - } - - ClassDescriptor whatClass = DescriptorUtils.getParentOfType(what, ClassDescriptor.class, false); - if (whatClass == null) return false; - - ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false); - if (fromClass == null) return false; - - if (DescriptorUtils.isSubclass(fromClass, whatClass)) { - return true; - } - return isVisible(receiver, what, fromClass.getContainingDeclaration()); + return isVisibleForProtectedAndPackage(receiver, what, from); } @Override @@ -146,6 +119,18 @@ public class JavaVisibilities { } }; + private static boolean isVisibleForProtectedAndPackage( + @Nullable ReceiverValue receiver, + @NotNull DeclarationDescriptorWithVisibility what, + @NotNull DeclarationDescriptor from + ) { + if (areInSamePackage(DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(what), from)) { + return true; + } + + return Visibilities.PROTECTED.isVisible(receiver, what, from); + } + private static boolean areInSamePackage(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) { PackageFragmentDescriptor whatPackage = DescriptorUtils.getParentOfType(first, PackageFragmentDescriptor.class, false); PackageFragmentDescriptor fromPackage = DescriptorUtils.getParentOfType(second, PackageFragmentDescriptor.class, false); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java index f323d445b37..dd30332f3e0 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.resolve.scopes.receivers.SuperCallReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.util.ModuleVisibilityHelper; @@ -121,20 +122,57 @@ public class Visibilities { } @Override - public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { - ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(what, ClassDescriptor.class); - if (DescriptorUtils.isCompanionObject(classDescriptor)) { - classDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class); - } - if (classDescriptor == null) return false; - + public boolean isVisible( + @Nullable ReceiverValue receiver, + @NotNull DeclarationDescriptorWithVisibility what, + @NotNull DeclarationDescriptor from + ) { + ClassDescriptor givenDescriptorContainingClass = DescriptorUtils.getParentOfType(what, ClassDescriptor.class); ClassDescriptor fromClass = DescriptorUtils.getParentOfType(from, ClassDescriptor.class, false); if (fromClass == null) return false; - if (DescriptorUtils.isSubclass(fromClass, classDescriptor)) { + + if (givenDescriptorContainingClass != null && DescriptorUtils.isCompanionObject(givenDescriptorContainingClass)) { + // Access to protected members inside companion is allowed to all subclasses + // Receiver type does not matter because objects are final + // NB: protected fake overrides in companion from super class should also be allowed + ClassDescriptor companionOwner = DescriptorUtils.getParentOfType(givenDescriptorContainingClass, ClassDescriptor.class); + if (companionOwner != null && DescriptorUtils.isSubclass(fromClass, companionOwner)) return true; + } + + // The rest part of method checks visibility similarly to Java does for protected (see JLS ยง6.6.2) + + // Protected fake overrides can have only one protected overridden (as protected is not allowed for interface members) + DeclarationDescriptorWithVisibility whatDeclaration = DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(what); + + ClassDescriptor classDescriptor = DescriptorUtils.getParentOfType(whatDeclaration, ClassDescriptor.class); + if (classDescriptor == null) return false; + + if (DescriptorUtils.isSubclass(fromClass, classDescriptor) + && doesReceiverFitForProtectedVisibility(receiver, whatDeclaration, fromClass)) { return true; } + return isVisible(receiver, what, fromClass.getContainingDeclaration()); } + + private boolean doesReceiverFitForProtectedVisibility( + @Nullable ReceiverValue receiver, + @NotNull DeclarationDescriptorWithVisibility whatDeclaration, + @NotNull ClassDescriptor fromClass + ) { + // Do not check receiver for non-callable declarations + if (!(whatDeclaration instanceof CallableMemberDescriptor)) return true; + + // See Visibility.isVisible contract + if (receiver == ALWAYS_SUITABLE_RECEIVER) return true; + if (receiver == IRRELEVANT_RECEIVER || receiver == null) return false; + + KotlinType actualReceiverType = receiver instanceof SuperCallReceiverValue + ? ((SuperCallReceiverValue) receiver).getThisType() + : receiver.getType(); + + return DescriptorUtils.isSubtypeOfClass(actualReceiverType, fromClass); + } }; public static final Visibility INTERNAL = new Visibility("internal", false) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index d94efb7032d..8de36885919 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -262,7 +262,7 @@ public class DescriptorUtils { return false; } - private static boolean isSubtypeOfClass(@NotNull KotlinType type, @NotNull DeclarationDescriptor superClass) { + public static boolean isSubtypeOfClass(@NotNull KotlinType type, @NotNull DeclarationDescriptor superClass) { if (isSameClass(type, superClass)) return true; for (KotlinType superType : type.getConstructor().getSupertypes()) { if (isSubtypeOfClass(superType, superClass)) { @@ -432,6 +432,15 @@ public class DescriptorUtils { return descriptor; } + public static D unwrapFakeOverrideToAnyDeclaration(@NotNull D descriptor) { + if (descriptor instanceof CallableMemberDescriptor) { + //noinspection unchecked + return (D) unwrapFakeOverride((CallableMemberDescriptor) descriptor); + } + + return descriptor; + } + public static boolean shouldRecordInitializerForProperty(@NotNull VariableDescriptor variable, @NotNull KotlinType type) { if (variable.isVar() || type.isError()) return false; diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/SuperCallReceiverValue.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/SuperCallReceiverValue.kt new file mode 100644 index 00000000000..ba036ec8879 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/receivers/SuperCallReceiverValue.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2016 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.resolve.scopes.receivers + +import org.jetbrains.kotlin.types.KotlinType + +interface SuperCallReceiverValue : ReceiverValue { + // This type is an actual receiver type used for invoke super-descriptor while ReceiverValue.type is some specific super-type of it + val thisType: KotlinType +}