Specialize diagnostic message for nested classes not allowed
This commit is contained in:
@@ -930,8 +930,8 @@ public interface Errors {
|
||||
DiagnosticFactory2<KtExpression, ClassDescriptor, String> NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<KtClass> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||
DiagnosticFactory0<KtObjectDeclaration> NESTED_OBJECT_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||
DiagnosticFactory1<KtClassOrObject, String> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, DECLARATION_NAME);
|
||||
DiagnosticFactory1<KtClassOrObject, String> NESTED_CLASS_NOT_ALLOWED_SINCE_1_3 = DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
|
||||
//Inline and inlinable parameters
|
||||
DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> NON_PUBLIC_CALL_FROM_PUBLIC_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
|
||||
|
||||
+2
-2
@@ -423,8 +423,8 @@ public class DefaultErrorMessages {
|
||||
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}''", NAME);
|
||||
MAP.put(NESTED_CLASS_NOT_ALLOWED, "Nested class is not allowed here, use 'inner' keyword to make the class inner");
|
||||
MAP.put(NESTED_OBJECT_NOT_ALLOWED, "Objects inside inner classes are prohibited");
|
||||
MAP.put(NESTED_CLASS_NOT_ALLOWED, "{0} is not allowed here", STRING);
|
||||
MAP.put(NESTED_CLASS_NOT_ALLOWED_SINCE_1_3, "{0} will not be allowed here in Kotlin 1.3 and later, please migrate your code", STRING);
|
||||
|
||||
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);
|
||||
|
||||
@@ -40,12 +40,33 @@ import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||
import static org.jetbrains.kotlin.lexer.KtTokens.*;
|
||||
|
||||
public class ModifiersChecker {
|
||||
private static boolean isIllegalNestedClass(@NotNull DeclarationDescriptor descriptor) {
|
||||
if (!(descriptor instanceof ClassDescriptor)) return false;
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
if (!(containingDeclaration instanceof ClassDescriptor)) return false;
|
||||
ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
|
||||
return containingClass.isInner() || DescriptorUtils.isLocal(containingClass);
|
||||
private enum DetailedClassKind {
|
||||
ENUM_CLASS("Enum class"),
|
||||
ENUM_ENTRY("Enum entry"),
|
||||
ANNOTATION_CLASS("Annotation class"),
|
||||
INTERFACE("Interface"),
|
||||
COMPANION_OBJECT("Companion object"),
|
||||
ANONYMOUS_OBJECT("Anonymous object"),
|
||||
OBJECT("Object"),
|
||||
CLASS("Class");
|
||||
|
||||
public final String withCapitalFirstLetter;
|
||||
|
||||
DetailedClassKind(String withCapitalFirstLetter) {
|
||||
this.withCapitalFirstLetter = withCapitalFirstLetter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DetailedClassKind getClassKind(@NotNull ClassDescriptor descriptor) {
|
||||
if (DescriptorUtils.isEnumEntry(descriptor)) return ENUM_ENTRY;
|
||||
if (DescriptorUtils.isEnumClass(descriptor)) return ENUM_CLASS;
|
||||
if (DescriptorUtils.isAnnotationClass(descriptor)) return ANNOTATION_CLASS;
|
||||
if (DescriptorUtils.isInterface(descriptor)) return INTERFACE;
|
||||
if (DescriptorUtils.isCompanionObject(descriptor)) return COMPANION_OBJECT;
|
||||
if (DescriptorUtils.isAnonymousObject(descriptor)) return ANONYMOUS_OBJECT;
|
||||
if (DescriptorUtils.isObject(descriptor)) return OBJECT;
|
||||
return CLASS;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -157,18 +178,32 @@ public class ModifiersChecker {
|
||||
|
||||
public void checkModifiersForDeclaration(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
|
||||
checkNestedClassAllowed(modifierListOwner, descriptor);
|
||||
checkObjectInsideInnerClass(modifierListOwner, descriptor);
|
||||
checkTypeParametersModifiers(modifierListOwner);
|
||||
checkModifierListCommon(modifierListOwner, descriptor);
|
||||
checkIllegalHeader(modifierListOwner, descriptor);
|
||||
}
|
||||
|
||||
private void checkObjectInsideInnerClass(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
|
||||
if (modifierListOwner instanceof KtObjectDeclaration) {
|
||||
KtObjectDeclaration ktObject = (KtObjectDeclaration) modifierListOwner;
|
||||
if (!ktObject.isLocal() && isIllegalNestedClass(descriptor)) {
|
||||
trace.report(NESTED_OBJECT_NOT_ALLOWED.on(ktObject));
|
||||
}
|
||||
private void checkNestedClassAllowed(@NotNull KtDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
|
||||
if (!(declaration instanceof KtClassOrObject)) return;
|
||||
KtClassOrObject ktClassOrObject = (KtClassOrObject) declaration;
|
||||
if (!(descriptor instanceof ClassDescriptor)) return;
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
if (!(containingDeclaration instanceof ClassDescriptor)) return;
|
||||
ClassDescriptor containingClass = (ClassDescriptor) containingDeclaration;
|
||||
|
||||
DetailedClassKind kind = DetailedClassKind.getClassKind(classDescriptor);
|
||||
|
||||
if (kind == DetailedClassKind.ANONYMOUS_OBJECT || kind == DetailedClassKind.ENUM_ENTRY) return;
|
||||
|
||||
// Local enums / objects / companion objects are handled in different checks
|
||||
if ((kind == DetailedClassKind.ENUM_CLASS || kind == DetailedClassKind.OBJECT || kind == DetailedClassKind.COMPANION_OBJECT) &&
|
||||
DescriptorUtils.isLocal(classDescriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!classDescriptor.isInner() && (containingClass.isInner() || DescriptorUtils.isLocal(containingClass))) {
|
||||
trace.report(NESTED_CLASS_NOT_ALLOWED.on(ktClassOrObject, kind.withCapitalFirstLetter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,17 +241,6 @@ public class ModifiersChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNestedClassAllowed(@NotNull KtModifierListOwner modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
|
||||
if (modifierListOwner.hasModifier(INNER_KEYWORD)) return;
|
||||
if (modifierListOwner instanceof KtClass && !(modifierListOwner instanceof KtEnumEntry)) {
|
||||
KtClass aClass = (KtClass) modifierListOwner;
|
||||
boolean localEnumError = aClass.isLocal() && aClass.isEnum();
|
||||
if (!localEnumError && isIllegalNestedClass(descriptor)) {
|
||||
trace.report(NESTED_CLASS_NOT_ALLOWED.on(aClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<KtModifierKeywordToken, PsiElement> getTokensCorrespondingToModifiers(
|
||||
@NotNull KtModifierList modifierList,
|
||||
|
||||
Reference in New Issue
Block a user