diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index b74be84d3d7..29de27575d6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -602,7 +602,7 @@ public interface Errors { DiagnosticFactory1 TYPE_PARAMETER_IS_NOT_AN_EXPRESSION = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 TYPE_PARAMETER_ON_LHS_OF_DOT = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory1.create(ERROR); + DiagnosticFactory2 NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index ed7af872641..57ce94d2bed 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -262,7 +262,7 @@ public class DefaultErrorMessages { 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(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE, "Nested {0} accessed via instance reference", RENDER_CLASS_OR_OBJECT_NAME); - MAP.put(NESTED_CLASS_SHOULD_BE_QUALIFIED, "Nested {0} should be qualified", RENDER_CLASS_OR_OBJECT_NAME); + MAP.put(NESTED_CLASS_SHOULD_BE_QUALIFIED, "Nested {0} should be qualified as ''{1}''", RENDER_CLASS_OR_OBJECT_NAME, TO_STRING); 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"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java index a6cc013f722..6ce178b24ef 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/AbstractTracingStrategy.java @@ -25,6 +25,8 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.calls.inference.*; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; +import org.jetbrains.jet.lang.resolve.descriptorUtil.DescriptorUtilPackage; +import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; @@ -39,6 +41,7 @@ import java.util.List; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.resolve.BindingContext.AMBIGUOUS_REFERENCE_TARGET; +import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqNameFromTopLevelClass; import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType; public abstract class AbstractTracingStrategy implements TracingStrategy { @@ -134,7 +137,15 @@ public abstract class AbstractTracingStrategy implements TracingStrategy { @NotNull ExplicitReceiverKind explicitReceiverKind ) { if (explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) { - trace.report(NESTED_CLASS_SHOULD_BE_QUALIFIED.on(reference, classDescriptor)); + String qualifiedName; + FqName fqName = getFqNameFromTopLevelClass(DescriptorUtilPackage.getImportableDescriptor(classDescriptor)); + if (reference.getParent() instanceof JetCallableReferenceExpression) { + qualifiedName = fqName.parent() + "::" + classDescriptor.getName(); + } + else { + qualifiedName = fqName.asString(); + } + trace.report(NESTED_CLASS_SHOULD_BE_QUALIFIED.on(reference, classDescriptor, qualifiedName)); } else { trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(reference, classDescriptor)); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/Qualifier.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/Qualifier.kt index 234c952f814..7d9053b4b25 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/Qualifier.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/Qualifier.kt @@ -34,13 +34,13 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor import org.jetbrains.jet.lang.diagnostics.Errors.* import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor -import org.jetbrains.jet.lang.descriptors.impl.PackageFragmentDescriptorImpl import org.jetbrains.jet.lang.types.expressions.ExpressionTypingContext import org.jetbrains.jet.lang.psi.psiUtil.getTopmostParentQualifiedExpressionForSelector import org.jetbrains.jet.lang.resolve.descriptorUtil.getClassObjectReferenceTarget import org.jetbrains.jet.lang.psi.JetExpression import org.jetbrains.jet.lang.resolve.bindingContextUtil.recordScopeAndDataFlowInfo import kotlin.properties.Delegates +import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor public trait Qualifier { @@ -168,7 +168,7 @@ private fun QualifierReceiver.resolveReferenceTarget(selector: DeclarationDescri else -> selector?.getContainingDeclaration() } - if (packageView != null && (containingDeclaration is PackageFragmentDescriptorImpl || containingDeclaration is PackageViewDescriptor) + if (packageView != null && (containingDeclaration is PackageFragmentDescriptor || containingDeclaration is PackageViewDescriptor) && getFqName(packageView) == getFqName(containingDeclaration)) { return packageView } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java index 35edbc96c36..e07f12a7f83 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.java @@ -102,14 +102,8 @@ public class DescriptorUtils { @NotNull private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) { - DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); - - if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.CLASS_OBJECT) { - DeclarationDescriptor classOfClassObject = containingDeclaration.getContainingDeclaration(); - assert classOfClassObject != null; - return getFqName(classOfClassObject).child(descriptor.getName()); - } - + DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor); + assert containingDeclaration != null : "Not package/module descriptor doesn't have containing declaration: " + descriptor; return getFqName(containingDeclaration).child(descriptor.getName()); } @@ -117,6 +111,22 @@ public class DescriptorUtils { return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor; } + @Nullable + private static DeclarationDescriptor getContainingDeclarationSkippingClassObjects(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); + return isClassObject(containingDeclaration) ? containingDeclaration.getContainingDeclaration() : containingDeclaration; + } + + @NotNull + public static FqName getFqNameFromTopLevelClass(@NotNull DeclarationDescriptor descriptor) { + DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor); + Name name = descriptor.getName(); + if (!(containingDeclaration instanceof ClassDescriptor)) { + return FqName.topLevel(name); + } + return getFqNameFromTopLevelClass(containingDeclaration).child(name); + } + // WARNING! Don't use this method in JVM backend, use JvmCodegenUtil.isCallInsideSameModuleAsDeclared() instead. // The latter handles compilation against compiled part of our module correctly. public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) { @@ -202,7 +212,7 @@ public class DescriptorUtils { return descriptor instanceof AnonymousFunctionDescriptor; } - public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) { + public static boolean isClassObject(@Nullable DeclarationDescriptor descriptor) { return isKindOf(descriptor, ClassKind.CLASS_OBJECT); } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.kt index a816626db6c..f9e3f28a8f7 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/DescriptorUtils.kt @@ -20,6 +20,9 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor import org.jetbrains.jet.lang.descriptors.ClassKind.ENUM_ENTRY import org.jetbrains.jet.lang.descriptors.ClassKind.ENUM_CLASS import org.jetbrains.jet.lang.descriptors.ClassKind.OBJECT +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor +import org.jetbrains.jet.lang.resolve.DescriptorUtils public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor { @@ -27,4 +30,7 @@ public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor { return if (classObjectDescriptor == null || hasSyntheticClassObject()) this else classObjectDescriptor } -public fun ClassDescriptor.hasSyntheticClassObject(): Boolean = getKind() in setOf(ENUM_ENTRY, ENUM_CLASS, OBJECT) \ No newline at end of file +public fun ClassDescriptor.hasSyntheticClassObject(): Boolean = getKind() in setOf(ENUM_ENTRY, ENUM_CLASS, OBJECT) + +public fun DeclarationDescriptor.getImportableDescriptor(): DeclarationDescriptor = + if (this is ConstructorDescriptor || DescriptorUtils.isClassObject(this)) getContainingDeclaration()!! else this diff --git a/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt index 8260232041c..d7225f072a1 100644 --- a/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/imports/ImportsUtils.kt @@ -27,9 +27,7 @@ import org.jetbrains.jet.lang.psi.JetSimpleNameExpression import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression import com.intellij.psi.PsiElement import org.jetbrains.jet.lang.types.JetType - -private fun DeclarationDescriptor.getImportableDescriptor() = - if (this is ConstructorDescriptor || DescriptorUtils.isClassObject(this)) getContainingDeclaration()!! else this +import org.jetbrains.jet.lang.resolve.descriptorUtil.getImportableDescriptor public val DeclarationDescriptor.importableFqName: FqName? get() { diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified.kt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified.kt new file mode 100644 index 00000000000..3fc0a24e000 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified.kt @@ -0,0 +1,38 @@ +// !DIAGNOSTICS_NUMBER: 5 +// !DIAGNOSTICS: NESTED_CLASS_SHOULD_BE_QUALIFIED + +package p + +class A { + class B { + class Nested + } +} + +fun A.B.test() { + Nested() + ::Nested +} + +class C { + class object { + class D { + class Nested + } + } +} + +fun C.D.text() { + Nested() + ::Nested +} + +class E { + class F { + class object + } +} + +fun E.test() { + F +} \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified1.txt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified1.txt new file mode 100644 index 00000000000..06a18775834 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified1.txt @@ -0,0 +1,2 @@ + +Nested class 'Nested' should be qualified as 'A.B.Nested' \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified2.txt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified2.txt new file mode 100644 index 00000000000..2cccc6b3938 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified2.txt @@ -0,0 +1,2 @@ + +Nested class 'Nested' should be qualified as 'A.B::Nested' \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified3.txt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified3.txt new file mode 100644 index 00000000000..5f6d147ef98 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified3.txt @@ -0,0 +1,2 @@ + +Nested class 'Nested' should be qualified as 'C.D.Nested' \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified4.txt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified4.txt new file mode 100644 index 00000000000..2fae93db633 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified4.txt @@ -0,0 +1,2 @@ + +Nested class 'Nested' should be qualified as 'C.D::Nested' \ No newline at end of file diff --git a/idea/testData/diagnosticMessage/nestedClassShouldBeQualified5.txt b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified5.txt new file mode 100644 index 00000000000..d662eec3e52 --- /dev/null +++ b/idea/testData/diagnosticMessage/nestedClassShouldBeQualified5.txt @@ -0,0 +1,2 @@ + +Nested class object of 'F' should be qualified as 'E.F' \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTestGenerated.java index 7e4a024a7d5..00e4e133a3f 100644 --- a/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/highlighter/DiagnosticMessageTestGenerated.java @@ -103,6 +103,11 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes doTest("idea/testData/diagnosticMessage/nestedClassAcessedViaInstanceReference.kt"); } + @TestMetadata("nestedClassShouldBeQualified.kt") + public void testNestedClassShouldBeQualified() throws Exception { + doTest("idea/testData/diagnosticMessage/nestedClassShouldBeQualified.kt"); + } + @TestMetadata("noneApplicable.kt") public void testNoneApplicable() throws Exception { doTest("idea/testData/diagnosticMessage/noneApplicable.kt");