Report 'nested class accessed via instance reference' error

This commit is contained in:
Svetlana Isakova
2014-08-01 14:02:27 +04:00
parent d150a26f8a
commit 7f33ad90a6
28 changed files with 224 additions and 41 deletions
@@ -591,6 +591,7 @@ public interface Errors {
DiagnosticFactory1<JetSimpleNameExpression, ClassifierDescriptor> NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR); DiagnosticFactory1<JetSimpleNameExpression, ClassifierDescriptor> NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetSimpleNameExpression, TypeParameterDescriptor> TYPE_PARAMETER_IS_NOT_AN_EXPRESSION = DiagnosticFactory1.create(ERROR); DiagnosticFactory1<JetSimpleNameExpression, TypeParameterDescriptor> TYPE_PARAMETER_IS_NOT_AN_EXPRESSION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetSimpleNameExpression, TypeParameterDescriptor> TYPE_PARAMETER_ON_LHS_OF_DOT = DiagnosticFactory1.create(ERROR); DiagnosticFactory1<JetSimpleNameExpression, TypeParameterDescriptor> TYPE_PARAMETER_ON_LHS_OF_DOT = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<JetExpression, ClassDescriptor> NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR); DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR); DiagnosticFactory0<PsiElement> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
@@ -259,6 +259,7 @@ public class DefaultErrorMessages {
MAP.put(TYPE_PARAMETER_IS_NOT_AN_EXPRESSION, "Type parameter ''{0}'' is not an expression", NAME); MAP.put(TYPE_PARAMETER_IS_NOT_AN_EXPRESSION, "Type parameter ''{0}'' is not an expression", NAME);
MAP.put(TYPE_PARAMETER_ON_LHS_OF_DOT, "Type parameter ''{0}'' cannot have or inherit a class object, so it cannot be on the left hand side of dot", NAME); MAP.put(TYPE_PARAMETER_ON_LHS_OF_DOT, "Type parameter ''{0}'' cannot have or inherit a class object, so it cannot be on the left hand side of dot", NAME);
MAP.put(NO_GENERICS_IN_SUPERTYPE_SPECIFIER, "Generic arguments of the base type must be specified"); MAP.put(NO_GENERICS_IN_SUPERTYPE_SPECIFIER, "Generic arguments of the base type must be specified");
MAP.put(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE, "Nested {0} accessed via instance reference", RENDER_CLASS_OR_OBJECT_NAME);
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(INACCESSIBLE_OUTER_CLASS_EXPRESSION, "Expression is inaccessible from a nested class ''{0}'', use ''inner'' keyword to make the class inner", NAME);
MAP.put(NESTED_CLASS_NOT_ALLOWED, "Nested class is not allowed here, use ''inner'' keyword to make the class inner"); MAP.put(NESTED_CLASS_NOT_ALLOWED, "Nested class is not allowed here, use ''inner'' keyword to make the class inner");
@@ -17,5 +17,29 @@
package org.jetbrains.jet.lang.diagnostics.rendering package org.jetbrains.jet.lang.diagnostics.rendering
import org.jetbrains.jet.renderer.Renderer import org.jetbrains.jet.renderer.Renderer
import org.jetbrains.jet.lang.descriptors.ClassKind
import org.jetbrains.jet.lang.descriptors.ClassKind.*
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils.isClassObject
public fun <P> renderParameter(parameter: P, renderer: Renderer<P>?): Any = renderer?.render(parameter) ?: parameter public fun <P> renderParameter(parameter: P, renderer: Renderer<P>?): Any = renderer?.render(parameter) ?: parameter
public fun ClassDescriptor.renderKindWithName(): String {
val kind = getKind().getText()
if (isClassObject(this)) {
return "$kind of '${getContainingDeclaration().getName()}'"
}
return "$kind '${getName()}'"
}
public fun ClassKind.getText(): String {
return when (this) {
CLASS -> "class"
TRAIT -> "trait"
ENUM_CLASS -> "enum class"
ENUM_ENTRY -> "enum entry"
ANNOTATION_CLASS -> "annotation class"
OBJECT -> "object"
CLASS_OBJECT -> "class object"
}
}
@@ -112,7 +112,14 @@ public class Renderers {
return "Class" + name; return "Class" + name;
} }
return "Object" + name; return "Object" + name;
}
};
public static final Renderer<ClassDescriptor> RENDER_CLASS_OR_OBJECT_NAME = new Renderer<ClassDescriptor>() {
@NotNull
@Override
public String render(@NotNull ClassDescriptor classifier) {
return RenderingPackage.renderKindWithName(classifier);
} }
}; };
@@ -298,6 +298,7 @@ public class CallExpressionResolver {
JetType selectorReturnType = selectorReturnTypeInfo.getType(); JetType selectorReturnType = selectorReturnTypeInfo.getType();
resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, expression, context); resolveDeferredReceiverInQualifiedExpression(qualifierReceiver, expression, context);
checkNestedClassAccess(expression, context);
//TODO move further //TODO move further
if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) { if (expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
@@ -338,4 +339,24 @@ public class CallExpressionResolver {
? context.trace.get(BindingContext.REFERENCE_TARGET, (JetReferenceExpression) calleeExpression) : null; ? context.trace.get(BindingContext.REFERENCE_TARGET, (JetReferenceExpression) calleeExpression) : null;
ReceiversPackage.resolveAsReceiverInQualifiedExpression(qualifierReceiver, context, selectorDescriptor); ReceiversPackage.resolveAsReceiverInQualifiedExpression(qualifierReceiver, context, selectorDescriptor);
} }
private static void checkNestedClassAccess(
@NotNull JetQualifiedExpression expression,
@NotNull ExpressionTypingContext context
) {
JetExpression selectorExpression = expression.getSelectorExpression();
if (selectorExpression == null) return;
// A.B - if B is a nested class accessed by outer class, 'A' and 'A.B' were marked as qualifiers
// a.B - if B is a nested class accessed by instance reference, 'a.B' was marked as a qualifier, but 'a' was not (it's an expression)
QualifierReceiver expressionQualifier = context.trace.get(BindingContext.QUALIFIER_RECEIVER, expression);
QualifierReceiver receiverQualifier = context.trace.get(BindingContext.QUALIFIER_RECEIVER, expression.getReceiverExpression());
if (receiverQualifier == null && expressionQualifier != null) {
assert expressionQualifier.getClassifier() instanceof ClassDescriptor :
"Only class can (package cannot) be accessed by instance reference: " + expressionQualifier;
context.trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(selectorExpression, (ClassDescriptor)expressionQualifier.getClassifier()));
}
}
} }
@@ -38,6 +38,7 @@ import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus; import org.jetbrains.jet.lang.resolve.calls.results.ResolutionStatus;
import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask; import org.jetbrains.jet.lang.resolve.calls.tasks.ResolutionTask;
import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer; import org.jetbrains.jet.lang.resolve.calls.tasks.TaskPrioritizer;
import org.jetbrains.jet.lang.resolve.calls.util.FakeCallableDescriptorForObject;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.*;
@@ -115,6 +116,9 @@ public class CandidateResolver {
} }
} }
} }
if (!checkThisObject(context)) {
candidateCall.addStatus(OTHER_ERROR);
}
List<JetTypeProjection> jetTypeArguments = context.call.getTypeArguments(); List<JetTypeProjection> jetTypeArguments = context.call.getTypeArguments();
if (jetTypeArguments.isEmpty()) { if (jetTypeArguments.isEmpty()) {
@@ -167,6 +171,31 @@ public class CandidateResolver {
} }
} }
private static boolean checkThisObject(@NotNull CallCandidateResolutionContext<?> context) {
MutableResolvedCall<? extends CallableDescriptor> candidateCall = context.candidateCall;
CallableDescriptor candidateDescriptor = candidateCall.getCandidateDescriptor();
ReceiverValue thisObject = candidateCall.getThisObject();
if (thisObject.exists()) {
ClassDescriptor nestedClass = null;
if (candidateDescriptor instanceof ConstructorDescriptor
&& DescriptorUtils.isStaticNestedClass(candidateDescriptor.getContainingDeclaration())) {
nestedClass = (ClassDescriptor) candidateDescriptor.getContainingDeclaration();
}
else if (candidateDescriptor instanceof FakeCallableDescriptorForObject) {
nestedClass = ((FakeCallableDescriptorForObject) candidateDescriptor).getReferencedDescriptor();
}
if (nestedClass != null) {
context.tracing.nestedClassAccessViaInstanceReference(context.trace, nestedClass);
return false;
}
}
assert (thisObject.exists() == (candidateCall.getResultingDescriptor().getExpectedThisObject() != null))
: "Shouldn't happen because of TaskPrioritizer: " + candidateDescriptor;
return true;
}
private static boolean checkOuterClassMemberIsAccessible(@NotNull CallCandidateResolutionContext<?> context) { private static boolean checkOuterClassMemberIsAccessible(@NotNull CallCandidateResolutionContext<?> context) {
// In "this@Outer.foo()" the error will be reported on "this@Outer" instead // In "this@Outer.foo()" the error will be reported on "this@Outer" instead
if (context.call.getExplicitReceiver().exists() || context.call.getThisObject().exists()) return true; if (context.call.getExplicitReceiver().exists() || context.call.getThisObject().exists()) return true;
@@ -207,9 +207,6 @@ import static org.jetbrains.jet.lang.resolve.calls.ValueArgumentsToParametersMap
processFunctionLiteralArguments(); processFunctionLiteralArguments();
reportUnmappedParameters(); reportUnmappedParameters();
checkReceiverArgument(); checkReceiverArgument();
assert (candidateCall.getThisObject().exists() == (candidateCall.getResultingDescriptor().getExpectedThisObject() != null))
: "Shouldn't happen because of TaskPrioritizer: " + candidateCall.getCandidateDescriptor();
} }
private void processFunctionLiteralArguments() { private void processFunctionLiteralArguments() {
@@ -20,10 +20,7 @@ import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptorWithVisibility;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.calls.inference.*;
@@ -130,6 +127,11 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
trace.report(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS.on(call.getCallElement())); trace.report(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS.on(call.getCallElement()));
} }
@Override
public void nestedClassAccessViaInstanceReference(@NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor) {
trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(reference, classDescriptor));
}
@Override @Override
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) { public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {
ASTNode callOperationNode = call.getCallOperationNode(); ASTNode callOperationNode = call.getCallOperationNode();
@@ -275,12 +275,6 @@ public class TaskPrioritizer {
@NotNull Call call @NotNull Call call
) { ) {
for (D descriptor : descriptors) { for (D descriptor : descriptors) {
if (descriptor instanceof ConstructorDescriptor && DescriptorUtils.isStaticNestedClass(descriptor.getContainingDeclaration())
|| descriptor instanceof FakeCallableDescriptorForObject) {
// We don't want static nested class constructor or class object / object (as callable)
// to be resolved with expectedThisObject
continue;
}
ResolutionCandidate<D> candidate = ResolutionCandidate.create(call, descriptor); ResolutionCandidate<D> candidate = ResolutionCandidate.create(call, descriptor);
candidate.setThisObject(thisObject); candidate.setThisObject(thisObject);
candidate.setReceiverArgument(receiverParameter); candidate.setReceiverArgument(receiverParameter);
@@ -17,10 +17,7 @@
package org.jetbrains.jet.lang.resolve.calls.tasks; package org.jetbrains.jet.lang.resolve.calls.tasks;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptorWithVisibility;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.Call; import org.jetbrains.jet.lang.psi.Call;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralArgument; import org.jetbrains.jet.lang.psi.JetFunctionLiteralArgument;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
@@ -83,6 +80,9 @@ public interface TracingStrategy {
@Override @Override
public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {} public void instantiationOfAbstractClass(@NotNull BindingTrace trace) {}
@Override
public void nestedClassAccessViaInstanceReference(@NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor) {}
@Override @Override
public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {} public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {}
@@ -132,6 +132,8 @@ public interface TracingStrategy {
void instantiationOfAbstractClass(@NotNull BindingTrace trace); void instantiationOfAbstractClass(@NotNull BindingTrace trace);
void nestedClassAccessViaInstanceReference(@NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor);
void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke); void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke);
void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type); void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type);
@@ -591,16 +591,6 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return null; return null;
} }
ReceiverValue receiver = new TransientReceiver(lhsType);
TemporaryTraceAndCache temporaryWithReceiver = TemporaryTraceAndCache.create(
context, "trace to resolve callable reference with receiver", reference);
CallableDescriptor descriptor = resolveCallableNotCheckingArguments(
reference, receiver, context.replaceTraceAndCache(temporaryWithReceiver), result);
if (result[0]) {
temporaryWithReceiver.commit();
return descriptor;
}
JetScope staticScope = getStaticNestedClassesScope((ClassDescriptor) classifier); JetScope staticScope = getStaticNestedClassesScope((ClassDescriptor) classifier);
TemporaryTraceAndCache temporaryForStatic = TemporaryTraceAndCache.create( TemporaryTraceAndCache temporaryForStatic = TemporaryTraceAndCache.create(
context, "trace to resolve callable reference in static scope", reference); context, "trace to resolve callable reference in static scope", reference);
@@ -611,6 +601,16 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return possibleStaticNestedClassConstructor; return possibleStaticNestedClassConstructor;
} }
ReceiverValue receiver = new TransientReceiver(lhsType);
TemporaryTraceAndCache temporaryWithReceiver = TemporaryTraceAndCache.create(
context, "trace to resolve callable reference with receiver", reference);
CallableDescriptor descriptor = resolveCallableNotCheckingArguments(
reference, receiver, context.replaceTraceAndCache(temporaryWithReceiver), result);
if (result[0]) {
temporaryWithReceiver.commit();
return descriptor;
}
return null; return null;
} }
@@ -454,6 +454,13 @@ public class ControlStructureTypingUtils {
throwError(); throwError();
} }
@Override
public void nestedClassAccessViaInstanceReference(
@NotNull BindingTrace trace, @NotNull ClassDescriptor classDescriptor
) {
throwError();
}
@Override @Override
public void unsafeCall( public void unsafeCall(
@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke @NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke
@@ -13,6 +13,6 @@ class C {
} }
fun box(): String { fun box(): String {
val res = C().Obj.o + C().Obj.InnerObj.k() + C().Obj.D().ko val res = C.Obj.o + C.Obj.InnerObj.k() + C.Obj.D().ko
return if (res == "OKKO") "OK" else "Fail: $res" return if (res == "OKKO") "OK" else "Fail: $res"
} }
@@ -1,5 +1,5 @@
fun main(args: Array<String>) { fun main(args: Array<String>) {
val str = aaa.A().E.A val str = aaa.A.E.A
if (str.toString() != "A") { if (str.toString() != "A") {
throw Exception() throw Exception()
} }
@@ -1,5 +1,5 @@
fun main(args: Array<String>) { fun main(args: Array<String>) {
val str = aaa.A().O.s val str = aaa.A.O.s
if (str != "OK") { if (str != "OK") {
throw Exception() throw Exception()
} }
@@ -23,6 +23,6 @@ class Outer {
fun Outer.foo() { fun Outer.foo() {
Outer() Outer()
<!UNRESOLVED_REFERENCE!>Nested<!>() <!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>Nested<!>()
Inner() Inner()
} }
@@ -0,0 +1,54 @@
trait N { fun foo() = 1 }
class WithClassObject {
class object {}
class Nested()
class NestedWithClassObject { class object : N }
enum class NestedEnum { A }
object NestedObj : N { fun invoke() = 1 }
}
class WithoutClassObject {
class Nested()
class NestedWithClassObject { class object : N }
enum class NestedEnum { A }
object NestedObj : N { fun invoke() = 1 }
}
object Obj {
class Nested()
class NestedWithClassObject { class object : N }
enum class NestedEnum { A }
object NestedObj : N { fun invoke() = 1 }
}
fun test(with: WithClassObject, without: WithoutClassObject, obj: Obj) {
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>Nested<!>()
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>()
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>()
with.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>Nested<!>()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>()
without.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>Nested<!>()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedWithClassObject<!>.foo()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedEnum<!>.A
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>()
obj.<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>NestedObj<!>.foo()
}
@@ -0,0 +1,12 @@
//no nested class access via instance reference error
fun test() {
A.f(<!TYPE_MISMATCH!>""<!>)
}
class A() {
class object {
object f {
fun invoke(i: Int) = i
}
}
}
@@ -6,7 +6,7 @@ class A {
} }
fun A.main() { fun A.main() {
::<!UNRESOLVED_REFERENCE!>Nested<!> ::<!NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE!>Nested<!>
val y = A::Nested val y = A::Nested
y : KFunction0<A.Nested> y : KFunction0<A.Nested>
@@ -4944,6 +4944,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/inner/modality.kt"); doTest("compiler/testData/diagnostics/tests/inner/modality.kt");
} }
@TestMetadata("nestedClassAccessedViaInstanceReference.kt")
public void testNestedClassAccessedViaInstanceReference() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/nestedClassAccessedViaInstanceReference.kt");
}
@TestMetadata("nestedClassExtendsOuter.kt") @TestMetadata("nestedClassExtendsOuter.kt")
public void testNestedClassExtendsOuter() throws Exception { public void testNestedClassExtendsOuter() throws Exception {
doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuter.kt"); doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuter.kt");
@@ -5896,6 +5901,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/objects"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/objects"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("invokeOnInnerObject.kt")
public void testInvokeOnInnerObject() throws Exception {
doTest("compiler/testData/diagnostics/tests/objects/invokeOnInnerObject.kt");
}
@TestMetadata("kt2240.kt") @TestMetadata("kt2240.kt")
public void testKt2240() throws Exception { public void testKt2240() throws Exception {
doTest("compiler/testData/diagnostics/tests/objects/kt2240.kt"); doTest("compiler/testData/diagnostics/tests/objects/kt2240.kt");
@@ -6,5 +6,5 @@ object A {
fun test<T>(a: T) { fun test<T>(a: T) {
val c = (a as A) val c = (a as A)
c.<error descr="[FUNCTION_EXPECTED] Expression 'B' of type '[Package-type B]' cannot be invoked as a function. The function invoke() is not found"><error>B</error></error>() c.<error descr="[NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE] Nested class 'B' accessed via instance reference">B</error>()
} }
@@ -0,0 +1,16 @@
// !DIAGNOSTICS_NUMBER: 4
// !DIAGNOSTICS: NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE
class A {
class Nested()
class NestedWithClassObject { class object }
enum class NestedEnum { A }
object NestedObj { fun invoke() = 1 }
}
fun test(a: A) {
a.Nested()
a.NestedWithClassObject
a.NestedEnum
a.NestedObj
}
@@ -0,0 +1,2 @@
<!-- nestedClassAcessedViaInstanceReference1 -->
Nested class 'Nested' accessed via instance reference
@@ -0,0 +1,2 @@
<!-- nestedClassAcessedViaInstanceReference2 -->
Nested class object of 'NestedWithClassObject' accessed via instance reference
@@ -0,0 +1,2 @@
<!-- nestedClassAcessedViaInstanceReference3 -->
Nested enum class 'NestedEnum' accessed via instance reference
@@ -0,0 +1,2 @@
<!-- nestedClassAcessedViaInstanceReference4 -->
Nested object 'NestedObj' accessed via instance reference
@@ -98,6 +98,11 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes
doTest("idea/testData/diagnosticMessage/nameInConstraintIsNotATypeParameter.kt"); doTest("idea/testData/diagnosticMessage/nameInConstraintIsNotATypeParameter.kt");
} }
@TestMetadata("nestedClassAcessedViaInstanceReference.kt")
public void testNestedClassAcessedViaInstanceReference() throws Exception {
doTest("idea/testData/diagnosticMessage/nestedClassAcessedViaInstanceReference.kt");
}
@TestMetadata("noneApplicable.kt") @TestMetadata("noneApplicable.kt")
public void testNoneApplicable() throws Exception { public void testNoneApplicable() throws Exception {
doTest("idea/testData/diagnosticMessage/noneApplicable.kt"); doTest("idea/testData/diagnosticMessage/noneApplicable.kt");
@@ -5,32 +5,25 @@ enum class A {
BAR : A() { BAR : A() {
fun explicitFromEntry() = A.FOO fun explicitFromEntry() = A.FOO
//fun byThisFromEntry() = this.FOO
fun implicitFromEntry() = FOO fun implicitFromEntry() = FOO
} }
fun explicit() = A.FOO fun explicit() = A.FOO
fun byThis() = this.FOO
fun implicit() = FOO fun implicit() = FOO
} }
fun A.extExplicit() = A.FOO fun A.extExplicit() = A.FOO
fun A.extByThis() = this.FOO
//fun A.extImplicit() = FOO //fun A.extImplicit() = FOO
fun box(): String { fun box(): String {
assertEquals(A.FOO, A.FOO.explicit(), "explicit access") assertEquals(A.FOO, A.FOO.explicit(), "explicit access")
assertEquals(A.FOO, A.FOO.byThis(), "access by this")
assertEquals(A.FOO, A.FOO.implicit(), "implicit access") assertEquals(A.FOO, A.FOO.implicit(), "implicit access")
assertEquals(A.FOO, A.FOO.explicit(), "explicit access from BAR") assertEquals(A.FOO, A.FOO.explicit(), "explicit access from BAR")
// TODO uncoment when KT-4692 will be fixed
//assertEquals(A.FOO, A.FOO.byThis(), "access by this from BAR")
assertEquals(A.FOO, A.FOO.implicit(), "implicit access from BAR") assertEquals(A.FOO, A.FOO.implicit(), "implicit access from BAR")
assertEquals(A.FOO, A.FOO.extExplicit(), "explicit access from ext fun") assertEquals(A.FOO, A.FOO.extExplicit(), "explicit access from ext fun")
assertEquals(A.FOO, A.FOO.extByThis(), "access by this from ext fun") // TODO uncoment when KT-5605 will be fixed
// TODO uncoment when KT-4692 will be fixed
//assertEquals(A.FOO, A.FOO.extImplicit(), "implicit access from ext fun") //assertEquals(A.FOO, A.FOO.extImplicit(), "implicit access from ext fun")
return "OK" return "OK"