Disallow nested classes within inner & local classes
#KT-1174 In Progress
This commit is contained in:
@@ -522,6 +522,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory1<JetSimpleNameExpression, ClassifierDescriptor> NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetSimpleNameExpression, ClassifierDescriptor> NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
||||||
|
SimpleDiagnosticFactory<PsiElement> NESTED_CLASS_NOT_ALLOWED = SimpleDiagnosticFactory.create(ERROR);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
+1
@@ -230,6 +230,7 @@ public class DefaultErrorMessages {
|
|||||||
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(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(HAS_NEXT_MISSING, "hasNext() cannot be called on iterator() of type ''{0}''", RENDER_TYPE);
|
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_AMBIGUITY, "hasNext() is ambiguous for iterator() of type ''{0}''", RENDER_TYPE);
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ import com.google.common.collect.Maps;
|
|||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetClass;
|
||||||
import org.jetbrains.jet.lang.psi.JetModifierList;
|
import org.jetbrains.jet.lang.psi.JetModifierList;
|
||||||
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
|
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
|
||||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||||
@@ -103,6 +105,14 @@ public class ModifiersChecker {
|
|||||||
checkIllegalInThisContextModifiers(modifierList, Collections.singletonList(INNER_KEYWORD));
|
checkIllegalInThisContextModifiers(modifierList, Collections.singletonList(INNER_KEYWORD));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
if (modifierListOwner instanceof JetClass && isIllegalNestedClass(descriptor)) {
|
||||||
|
PsiElement name = ((JetClass) modifierListOwner).getNameIdentifier();
|
||||||
|
if (name != null) {
|
||||||
|
trace.report(Errors.NESTED_CLASS_NOT_ALLOWED.on(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
|
private static boolean isIllegalInner(@NotNull DeclarationDescriptor descriptor) {
|
||||||
@@ -114,6 +124,14 @@ public class ModifiersChecker {
|
|||||||
return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
|
return ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.TRAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() || containingClass.getContainingDeclaration() instanceof FunctionDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetKeywordToken> availableModifiers, Collection<JetToken>... availableCombinations) {
|
private void checkCompatibility(@Nullable JetModifierList modifierList, Collection<JetKeywordToken> availableModifiers, Collection<JetToken>... availableCombinations) {
|
||||||
if (modifierList == null) return;
|
if (modifierList == null) return;
|
||||||
Collection<JetKeywordToken> presentModifiers = Sets.newLinkedHashSet();
|
Collection<JetKeywordToken> presentModifiers = Sets.newLinkedHashSet();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
class Bar(val name: String)
|
class Bar(val name: String)
|
||||||
|
|
||||||
class Baz {
|
class Baz {
|
||||||
class Foo() {
|
inner class Foo() {
|
||||||
data class NestedFoo(val bar: Bar)
|
inner data class NestedFoo(val bar: Bar)
|
||||||
|
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
return NestedFoo(Bar("FAIL")).copy(bar = Bar("OK")).bar.name
|
return NestedFoo(Bar("FAIL")).copy(bar = Bar("OK")).bar.name
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ abstract class Foo {
|
|||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
return object: Foo() {
|
return object: Foo() {
|
||||||
data class NestedFoo(val bar: Bar)
|
inner data class NestedFoo(val bar: Bar)
|
||||||
|
|
||||||
override fun foo(): String {
|
override fun foo(): String {
|
||||||
return NestedFoo(Bar("Fail")).copy(bar = Bar("OK")).bar.name
|
return NestedFoo(Bar("Fail")).copy(bar = Bar("OK")).bar.name
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
fun box() : String {
|
fun box() : String {
|
||||||
val o = object {
|
val o = object {
|
||||||
|
|
||||||
class C {
|
inner class C {
|
||||||
fun foo() = "OK"
|
fun foo() = "OK"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
class A {
|
||||||
|
inner class B {
|
||||||
|
class <!NESTED_CLASS_NOT_ALLOWED!>C<!>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
class B {
|
||||||
|
class <!NESTED_CLASS_NOT_ALLOWED!>C<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
class B {
|
||||||
|
class <!NESTED_CLASS_NOT_ALLOWED!>C<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class E {
|
||||||
|
E1 {
|
||||||
|
// Not allowed in Java, but no reason to disallow in Kotlin
|
||||||
|
class D
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2459,6 +2459,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuterGeneric.kt");
|
doTest("compiler/testData/diagnostics/tests/inner/nestedClassExtendsOuterGeneric.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedClassNotAllowed.kt")
|
||||||
|
public void testNestedClassNotAllowed() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/inner/nestedClassNotAllowed.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nestedVsInnerAccessOuterMember.kt")
|
@TestMetadata("nestedVsInnerAccessOuterMember.kt")
|
||||||
public void testNestedVsInnerAccessOuterMember() throws Exception {
|
public void testNestedVsInnerAccessOuterMember() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/inner/nestedVsInnerAccessOuterMember.kt");
|
doTest("compiler/testData/diagnostics/tests/inner/nestedVsInnerAccessOuterMember.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user