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);