Inaccessible outer class member is now an error

#KT-1174 In Progress
This commit is contained in:
Alexander Udalov
2012-12-11 22:30:25 +04:00
parent e76abcb625
commit 5d92453532
35 changed files with 358 additions and 25 deletions
@@ -48,7 +48,6 @@ public class ConstructorDescriptorImpl extends FunctionDescriptorImpl implements
return initialize(typeParameters, unsubstitutedValueParameters, visibility, false);
}
//isStatic - for java only
public ConstructorDescriptorImpl initialize(@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull List<ValueParameterDescriptor> unsubstitutedValueParameters, Visibility visibility, boolean isStatic) {
super.initialize(null, isStatic ? NO_RECEIVER_PARAMETER : getExpectedThisObject(getContainingDeclaration()), typeParameters, unsubstitutedValueParameters, null, Modality.FINAL, visibility);
return this;
@@ -521,6 +521,8 @@ public interface Errors {
SimpleDiagnosticFactory<JetRootNamespaceExpression> NAMESPACE_IS_NOT_AN_EXPRESSION = SimpleDiagnosticFactory.create(ERROR);
DiagnosticFactory1<JetSimpleNameExpression, ClassifierDescriptor> NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This field is needed to make the Initializer class load (interfaces cannot have static initializers)
@@ -229,6 +229,8 @@ public class DefaultErrorMessages {
MAP.put(NO_CLASS_OBJECT, "Please specify constructor invocation; classifier ''{0}'' does not have a class object", NAME);
MAP.put(NO_GENERICS_IN_SUPERTYPE_SPECIFIER, "Generic arguments of the base type must be specified");
MAP.put(INACCESSIBLE_OUTER_CLASS_EXPRESSION, "Expression is inaccessible from a nested class ''{0}'', use ''inner'' keyword to make the class inner", NAME);
MAP.put(HAS_NEXT_MISSING, "hasNext() cannot be called on iterator() of type ''{0}''", RENDER_TYPE);
MAP.put(HAS_NEXT_FUNCTION_AMBIGUITY, "hasNext() is ambiguous for iterator() of type ''{0}''", RENDER_TYPE);
MAP.put(HAS_NEXT_FUNCTION_NONE_APPLICABLE, "None of the hasNext() functions is applicable for iterator() of type ''{0}''", RENDER_TYPE);
@@ -48,8 +48,7 @@ import java.util.*;
import static org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.CONSTRUCTOR;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getDefaultConstructorVisibility;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getExpectedThisObjectIfNeeded;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.resolve.ModifiersChecker.*;
import static org.jetbrains.jet.lexer.JetTokens.OVERRIDE_KEYWORD;
@@ -1157,7 +1156,8 @@ public class DescriptorResolver {
constructorDescriptor,
parameterScope,
valueParameters, trace),
resolveVisibilityFromModifiers(modifierList, getDefaultConstructorVisibility(classDescriptor)));
resolveVisibilityFromModifiers(modifierList, getDefaultConstructorVisibility(classDescriptor)),
DescriptorUtils.isConstructorOfStaticNestedClass(constructorDescriptor));
}
@Nullable
@@ -1351,4 +1351,35 @@ public class DescriptorResolver {
}
};
}
public static boolean checkHasOuterClassInstance(
@NotNull JetScope scope,
@NotNull BindingTrace trace,
@NotNull PsiElement reportErrorsOn,
@NotNull ClassDescriptor target
) {
ClassDescriptor thisClass = getContainingClass(scope);
if (thisClass == null) return true;
if (!isAncestor(target, thisClass, true)) return true;
if (!hasOuterClassInstance(thisClass, target)) {
trace.report(INACCESSIBLE_OUTER_CLASS_EXPRESSION.on(reportErrorsOn, thisClass));
return false;
}
return true;
}
private static boolean hasOuterClassInstance(@NotNull ClassDescriptor thisClass, @NotNull ClassDescriptor outerClass) {
DeclarationDescriptor descriptor = thisClass;
while (true) {
assert descriptor != null : "outerClass must be an ancestor of thisClass: " + thisClass + " " + outerClass;
if (descriptor instanceof ClassDescriptor && isSubclass((ClassDescriptor) descriptor, outerClass)) {
return true;
}
if (isStaticNestedClass(descriptor)) {
return false;
}
descriptor = descriptor.getContainingDeclaration();
}
}
}
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.DescriptorSubstitutor;
import org.jetbrains.jet.lang.types.JetType;
@@ -426,4 +427,25 @@ public class DescriptorUtils {
}
return parameterTypes;
}
public static boolean isConstructorOfStaticNestedClass(@Nullable CallableDescriptor descriptor) {
return descriptor instanceof ConstructorDescriptor && isStaticNestedClass(descriptor.getContainingDeclaration());
}
/**
* @return true if descriptor is a class inside another class and does not have access to the outer class
*/
public static boolean isStaticNestedClass(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor containing = descriptor.getContainingDeclaration();
return descriptor instanceof ClassDescriptor &&
containing instanceof ClassDescriptor &&
!((ClassDescriptor) descriptor).isInner() &&
!((ClassDescriptor) containing).getKind().isObject();
}
@Nullable
public static ClassDescriptor getContainingClass(@NotNull JetScope scope) {
DeclarationDescriptor containingDeclaration = scope.getContainingDeclaration();
return getParentOfType(containingDeclaration, ClassDescriptor.class, false);
}
}
@@ -80,6 +80,11 @@ public class CandidateResolver {
return;
}
if (!checkOuterClassMemberIsAccessible(context)) {
candidateCall.addStatus(OTHER_ERROR);
return;
}
if (!Visibilities.isVisible(candidate, context.scope.getContainingDeclaration())) {
candidateCall.addStatus(OTHER_ERROR);
context.tracing.invisibleMember(context.trace, candidate);
@@ -162,6 +167,24 @@ public class CandidateResolver {
AutoCastUtils.recordAutoCastIfNecessary(candidateCall.getThisObject(), candidateCall.getTrace());
}
private static boolean checkOuterClassMemberIsAccessible(@NotNull CallResolutionContext<?, ?> context) {
// In "this@Outer.foo()" the error will be reported on "this@Outer" instead
if (context.call.getExplicitReceiver().exists()) return true;
ClassDescriptor candidateThis = getDeclaringClass(context.candidateCall.getCandidateDescriptor());
if (candidateThis == null || candidateThis.getKind().isObject()) return true;
return DescriptorResolver.checkHasOuterClassInstance(context.scope, context.trace, context.call.getCallElement(), candidateThis);
}
@Nullable
private static ClassDescriptor getDeclaringClass(@NotNull CallableDescriptor candidate) {
ReceiverParameterDescriptor expectedThis = candidate.getExpectedThisObject();
if (expectedThis == null) return null;
DeclarationDescriptor descriptor = expectedThis.getContainingDeclaration();
return descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null;
}
public <D extends CallableDescriptor> void completeTypeInferenceDependentOnExpectedTypeForCall(
CallResolutionContext<D, D> context
) {
@@ -175,6 +175,10 @@ public abstract class TaskPrioritizer {
for (ReceiverValue thisObject : thisObjects) {
for (ReceiverValue receiverParameter : receiverParameters) {
for (D extension : descriptors) {
if (DescriptorUtils.isConstructorOfStaticNestedClass(extension)) {
// We don't want static nested classes' constructors to be resolved with expectedThisObject
continue;
}
ResolutionCandidate<D> candidate = ResolutionCandidate.create(extension);
candidate.setThisObject(thisObject);
candidate.setReceiverArgument(receiverParameter);
@@ -1,8 +1,8 @@
class Outer() {
open class InnerBase() {
open inner class InnerBase() {
}
class InnerDerived(): InnerBase() {
inner class InnerDerived(): InnerBase() {
}
public val foo: InnerBase? = InnerDerived()
@@ -4,7 +4,7 @@ import java.io.*
class World() {
public val items: ArrayList<Item> = ArrayList<Item>()
class Item() {
inner class Item() {
{
items.add(this)
}
@@ -1,5 +1,5 @@
class Outer(val foo: StringBuilder) {
class Inner() {
inner class Inner() {
fun len() : Int {
return foo.length()
}
@@ -20,7 +20,7 @@ class C {
return "OK"
}
private class Inner() {
private inner class Inner() {
fun innerFun() {
"".ext()
f()
@@ -17,7 +17,7 @@ class C{
return v
}
private class Inner() {
private inner class Inner() {
fun innerFun() {
v = v + 1
}
@@ -1,10 +1,10 @@
class Outer() {
val s = "xyzzy"
open class InnerBase(public val name: String) {
open inner class InnerBase(public val name: String) {
}
class InnerDerived(): InnerBase(s) {
inner class InnerDerived(): InnerBase(s) {
}
val x = InnerDerived()
@@ -1,5 +1,5 @@
class A {
class B(val a: String = "a", val b: Int = 55, val c: String = "c")
inner class B(val a: String = "a", val b: Int = 55, val c: String = "c")
}
fun box(): String {
@@ -1,5 +1,5 @@
class A {
class B(val a: Double = 1.0, val b: Int = 55, val c: String = "c")
inner class B(val a: Double = 1.0, val b: Int = 55, val c: String = "c")
}
fun box(): String {
@@ -1,6 +1,6 @@
fun box(): String {
val o = object {
class A(val value: String = "OK")
inner class A(val value: String = "OK")
}
return o.A().value
@@ -1,5 +1,5 @@
class A {
public class Foo(val a: Int = 1) {}
public inner class Foo(val a: Int = 1) {}
fun foo() {
Foo()
@@ -1,5 +1,5 @@
private class A {
public class Foo(val a: Int = 1) {}
public inner class Foo(val a: Int = 1) {}
}
// CLASS: A$Foo
@@ -4,14 +4,14 @@ fun box() = IssueState.DEFAULT.ToString() + IssueState.FIXED.ToString()
enum class IssueState {
DEFAULT {
class D {
inner class D {
val k = ToString()
}
}
FIXED {
override fun ToString() = "K"
object D {
inner class D {
val k = ToString()
}
}
@@ -16,7 +16,7 @@ public object SomeObject {
}
public class SomeClass() {
class Inner {
inner class Inner {
val copy = list
}
@@ -6,7 +6,7 @@ open class N() : M() {
override var y = 200
open class C() {
open inner class C() {
fun test5() = y
fun test6() : Int {
super<M>@N.y += 200
@@ -229,7 +229,7 @@ class Outer() {
$b = 1
}
class Inner() {
inner class Inner() {
{
<!VAL_REASSIGNMENT!>a<!>++
b++
@@ -0,0 +1,32 @@
class Outer1 {
class Nested
class C1 { val b = Nested() }
class C2(val b: Any = Nested())
inner class C3 { val b = Nested() }
inner class C4(val b: Any = Nested())
inner class Inner
class C5 { val b = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>Inner()<!> }
class C6(val b: Any = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>Inner()<!>)
inner class C7 { val b = Inner() }
inner class C8(val b: Any = Inner())
}
class Outer2 {
class Nested {
fun foo() = Outer2()
fun bar() = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>Inner()<!>
}
inner class Inner {
fun foo() = Outer2()
fun bar() = Nested()
}
fun foo() {
Nested()
Inner()
}
}
@@ -0,0 +1,12 @@
enum class E {
E1 {
override fun foo() = outerFun() + super.outerFun()
}
E2 {
override fun foo() = E1.foo()
}
abstract fun foo(): Int
fun outerFun() = 42
}
@@ -0,0 +1,28 @@
class Outer {
class Nested
inner class Inner
fun Inner.foo() {
Outer()
Nested()
Inner()
}
fun Nested.bar() {
Outer()
Nested()
Inner()
}
fun Outer.baz() {
Outer()
Nested()
Inner()
}
}
fun Outer.foo() {
Outer()
<!UNRESOLVED_REFERENCE!>Nested<!>()
Inner()
}
@@ -0,0 +1,18 @@
class Outer {
fun foo(): Int {
if (outerState > 0) return outerState
class Local {
val localState = outerState
inner class LocalInner {
val o = outerState
val l = localState
}
}
return Local().localState
}
val outerState = 42
}
@@ -0,0 +1,11 @@
class Outer {
class Nested {
fun foo() {
class Local {
val state = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>outerState<!>
}
}
}
val outerState = 42
}
@@ -0,0 +1,17 @@
class Outer {
open class OpenNested
class FinalNested
open inner class OpenInner
class FinalInner
class Nested1 : OpenNested()
class Nested2 : <!FINAL_SUPERTYPE!>FinalNested<!>()
class Nested3 : <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>OpenInner()<!>
class Nested4 : <!FINAL_SUPERTYPE!>FinalInner<!>()
inner class Inner1 : OpenNested()
inner class Inner2 : <!FINAL_SUPERTYPE!>FinalNested<!>()
inner class Inner3 : OpenInner()
inner class Inner4 : <!FINAL_SUPERTYPE!>FinalInner<!>()
}
@@ -0,0 +1,8 @@
open class Outer {
class Nested : Outer() {
fun bar() = foo()
fun baz() = super.foo()
}
fun foo() = 42
}
@@ -0,0 +1,12 @@
open class Outer<T> {
class Nested<U> : Outer<U>() {
fun bar(): U = foo()
fun baz(): U = super.foo()
}
class Nested2 : Outer<String>() {
fun bar(): String = foo()
fun baz(): String = super.foo()
}
fun foo(): T = null!!
}
@@ -0,0 +1,19 @@
class Outer {
fun function() = 42
val property = ""
class Nested {
fun f() = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>function()<!>
fun g() = <!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>property<!>
}
inner class Inner {
fun innerFun() = function()
val innerProp = property
inner class InnerInner {
fun f() = innerFun()
fun g() = innerProp
}
}
}
@@ -0,0 +1,34 @@
open class Outer {
private class PrivateNested
private inner class PrivateInner
protected class ProtectedNested
protected inner class ProtectedInner
public class PublicNested
public inner class PublicInner
}
class Derived : Outer() {
fun foo() {
Outer.<!INVISIBLE_MEMBER!>PrivateNested<!>()
super.<!INVISIBLE_MEMBER!>PrivateInner<!>()
Outer.ProtectedNested()
super.ProtectedInner()
Outer.PublicNested()
super.PublicInner()
}
}
fun foo() {
Outer.<!INVISIBLE_MEMBER!>PrivateNested<!>()
Outer().<!INVISIBLE_MEMBER!>PrivateInner<!>()
Outer.<!INVISIBLE_MEMBER!>ProtectedNested<!>()
Outer().<!INVISIBLE_MEMBER!>ProtectedInner<!>()
Outer.PublicNested()
Outer().PublicInner()
}
@@ -19,7 +19,7 @@ val A.foo1 : Int get() = ii
class C {
class D {}
inner class D {}
}
val C.foo : C.D = <!UNRESOLVED_REFERENCE!>D<!>()
@@ -9,7 +9,7 @@ abstract class Foo {
class Bar : Foo() {
protected val i: Int = 1
class Baz {
inner class Baz {
val copy = color // INVISIBLE_MEMBER: Cannot access 'color' in 'Bar'
val j = i
}
@@ -30,7 +30,7 @@ import org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve;
@InnerTestClasses({JetDiagnosticsTestGenerated.Tests.class, JetDiagnosticsTestGenerated.Script.class})
public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEagerResolve {
@TestMetadata("compiler/testData/diagnostics/tests")
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.ThisAndSuper.class, Tests.Tuples.class, Tests.Varargs.class})
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.ThisAndSuper.class, Tests.Tuples.class, Tests.Varargs.class})
public static class Tests extends AbstractDiagnosticsTestWithEagerResolve {
@TestMetadata("Abstract.kt")
public void testAbstract() throws Exception {
@@ -2398,6 +2398,64 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
@TestMetadata("compiler/testData/diagnostics/tests/inner")
public static class Inner extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInInner() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/inner"), "kt", true);
}
@TestMetadata("constructorAccess.kt")
public void testConstructorAccess() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/constructorAccess.kt");
}
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/enumEntries.kt");
}
@TestMetadata("extensionFun.kt")
public void testExtensionFun() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/extensionFun.kt");
}
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/localClass.kt");
}
@TestMetadata("localClassInsideNested.kt")
public void testLocalClassInsideNested() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/localClassInsideNested.kt");
}
@TestMetadata("modality.kt")
public void testModality() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/modality.kt");
}
@TestMetadata("nestedClassExtendsOuter.kt")
public void testNestedClassExtendsOuter() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuter.kt");
}
@TestMetadata("nestedClassExtendsOuterGeneric.kt")
public void testNestedClassExtendsOuterGeneric() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuterGeneric.kt");
}
@TestMetadata("nestedVsInnerAccessOuterMember.kt")
public void testNestedVsInnerAccessOuterMember() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/nestedVsInnerAccessOuterMember.kt");
}
@TestMetadata("visibility.kt")
public void testVisibility() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/visibility.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/j+k")
public static class J_k extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInJ_k() throws Exception {
@@ -4109,6 +4167,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
suite.addTest(IncompleteCode.innerSuite());
suite.addTest(Inference.innerSuite());
suite.addTestSuite(Infos.class);
suite.addTestSuite(Inner.class);
suite.addTestSuite(J_k.class);
suite.addTest(Jdk_annotations.innerSuite());
suite.addTestSuite(Library.class);