Improved error message for 'nested class should be qualified'
This commit is contained in:
@@ -602,7 +602,7 @@ public interface Errors {
|
|||||||
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<JetExpression, ClassDescriptor> NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory1<JetExpression, ClassDescriptor> NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory2<JetExpression, ClassDescriptor, String> NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory2.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory0<JetClass> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
DiagnosticFactory0<JetClass> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||||
|
|||||||
+1
-1
@@ -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(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(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(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");
|
||||||
|
|||||||
+12
-1
@@ -25,6 +25,8 @@ 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.*;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
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.name.Name;
|
||||||
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;
|
||||||
@@ -39,6 +41,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
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.BindingContext.AMBIGUOUS_REFERENCE_TARGET;
|
||||||
|
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqNameFromTopLevelClass;
|
||||||
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
||||||
|
|
||||||
public abstract class AbstractTracingStrategy implements TracingStrategy {
|
public abstract class AbstractTracingStrategy implements TracingStrategy {
|
||||||
@@ -134,7 +137,15 @@ public abstract class AbstractTracingStrategy implements TracingStrategy {
|
|||||||
@NotNull ExplicitReceiverKind explicitReceiverKind
|
@NotNull ExplicitReceiverKind explicitReceiverKind
|
||||||
) {
|
) {
|
||||||
if (explicitReceiverKind == ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) {
|
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 {
|
else {
|
||||||
trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(reference, classDescriptor));
|
trace.report(NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE.on(reference, classDescriptor));
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
|||||||
import org.jetbrains.jet.lang.diagnostics.Errors.*
|
import org.jetbrains.jet.lang.diagnostics.Errors.*
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
|
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.types.expressions.ExpressionTypingContext
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.getTopmostParentQualifiedExpressionForSelector
|
import org.jetbrains.jet.lang.psi.psiUtil.getTopmostParentQualifiedExpressionForSelector
|
||||||
import org.jetbrains.jet.lang.resolve.descriptorUtil.getClassObjectReferenceTarget
|
import org.jetbrains.jet.lang.resolve.descriptorUtil.getClassObjectReferenceTarget
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression
|
import org.jetbrains.jet.lang.psi.JetExpression
|
||||||
import org.jetbrains.jet.lang.resolve.bindingContextUtil.recordScopeAndDataFlowInfo
|
import org.jetbrains.jet.lang.resolve.bindingContextUtil.recordScopeAndDataFlowInfo
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor
|
||||||
|
|
||||||
public trait Qualifier {
|
public trait Qualifier {
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ private fun QualifierReceiver.resolveReferenceTarget(selector: DeclarationDescri
|
|||||||
else -> selector?.getContainingDeclaration()
|
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)) {
|
&& getFqName(packageView) == getFqName(containingDeclaration)) {
|
||||||
return packageView
|
return packageView
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,14 +102,8 @@ public class DescriptorUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
|
private static FqNameUnsafe getFqNameUnsafe(@NotNull DeclarationDescriptor descriptor) {
|
||||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
DeclarationDescriptor containingDeclaration = getContainingDeclarationSkippingClassObjects(descriptor);
|
||||||
|
assert containingDeclaration != null : "Not package/module descriptor doesn't have containing declaration: " + descriptor;
|
||||||
if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.CLASS_OBJECT) {
|
|
||||||
DeclarationDescriptor classOfClassObject = containingDeclaration.getContainingDeclaration();
|
|
||||||
assert classOfClassObject != null;
|
|
||||||
return getFqName(classOfClassObject).child(descriptor.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return getFqName(containingDeclaration).child(descriptor.getName());
|
return getFqName(containingDeclaration).child(descriptor.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +111,22 @@ public class DescriptorUtils {
|
|||||||
return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor;
|
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.
|
// WARNING! Don't use this method in JVM backend, use JvmCodegenUtil.isCallInsideSameModuleAsDeclared() instead.
|
||||||
// The latter handles compilation against compiled part of our module correctly.
|
// The latter handles compilation against compiled part of our module correctly.
|
||||||
public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
|
public static boolean areInSameModule(@NotNull DeclarationDescriptor first, @NotNull DeclarationDescriptor second) {
|
||||||
@@ -202,7 +212,7 @@ public class DescriptorUtils {
|
|||||||
return descriptor instanceof AnonymousFunctionDescriptor;
|
return descriptor instanceof AnonymousFunctionDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isClassObject(@NotNull DeclarationDescriptor descriptor) {
|
public static boolean isClassObject(@Nullable DeclarationDescriptor descriptor) {
|
||||||
return isKindOf(descriptor, ClassKind.CLASS_OBJECT);
|
return isKindOf(descriptor, ClassKind.CLASS_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_ENTRY
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassKind.ENUM_CLASS
|
import org.jetbrains.jet.lang.descriptors.ClassKind.ENUM_CLASS
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassKind.OBJECT
|
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 {
|
public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor {
|
||||||
@@ -28,3 +31,6 @@ public fun ClassDescriptor.getClassObjectReferenceTarget(): ClassDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fun ClassDescriptor.hasSyntheticClassObject(): Boolean = getKind() in setOf(ENUM_ENTRY, ENUM_CLASS, OBJECT)
|
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
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
|
|||||||
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.jet.lang.types.JetType
|
import org.jetbrains.jet.lang.types.JetType
|
||||||
|
import org.jetbrains.jet.lang.resolve.descriptorUtil.getImportableDescriptor
|
||||||
private fun DeclarationDescriptor.getImportableDescriptor() =
|
|
||||||
if (this is ConstructorDescriptor || DescriptorUtils.isClassObject(this)) getContainingDeclaration()!! else this
|
|
||||||
|
|
||||||
public val DeclarationDescriptor.importableFqName: FqName?
|
public val DeclarationDescriptor.importableFqName: FqName?
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!-- nestedClassShouldBeQualified1 -->
|
||||||
|
Nested class 'Nested' should be qualified as 'A.B.Nested'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!-- nestedClassShouldBeQualified2 -->
|
||||||
|
Nested class 'Nested' should be qualified as 'A.B::Nested'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!-- nestedClassShouldBeQualified3 -->
|
||||||
|
Nested class 'Nested' should be qualified as 'C.D.Nested'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!-- nestedClassShouldBeQualified4 -->
|
||||||
|
Nested class 'Nested' should be qualified as 'C.D::Nested'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!-- nestedClassShouldBeQualified5 -->
|
||||||
|
Nested class object of 'F' should be qualified as 'E.F'
|
||||||
@@ -103,6 +103,11 @@ public class DiagnosticMessageTestGenerated extends AbstractDiagnosticMessageTes
|
|||||||
doTest("idea/testData/diagnosticMessage/nestedClassAcessedViaInstanceReference.kt");
|
doTest("idea/testData/diagnosticMessage/nestedClassAcessedViaInstanceReference.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedClassShouldBeQualified.kt")
|
||||||
|
public void testNestedClassShouldBeQualified() throws Exception {
|
||||||
|
doTest("idea/testData/diagnosticMessage/nestedClassShouldBeQualified.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");
|
||||||
|
|||||||
Reference in New Issue
Block a user