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 be9af1d9ded..0712f3dfadb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -522,6 +522,7 @@ public interface Errors { DiagnosticFactory1 NO_CLASS_OBJECT = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR); + SimpleDiagnosticFactory NESTED_CLASS_NOT_ALLOWED = SimpleDiagnosticFactory.create(ERROR); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 a675fd343ec..7af3705ba19 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 @@ -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(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_FUNCTION_AMBIGUITY, "hasNext() is ambiguous for iterator() of type ''{0}''", RENDER_TYPE); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ModifiersChecker.java index ce15a1bb20d..1755a7d256e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ModifiersChecker.java @@ -21,10 +21,12 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Pair; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; 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.JetModifierListOwner; import org.jetbrains.jet.lexer.JetKeywordToken; @@ -103,6 +105,14 @@ public class ModifiersChecker { 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) { @@ -114,6 +124,14 @@ public class ModifiersChecker { 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 availableModifiers, Collection... availableCombinations) { if (modifierList == null) return; Collection presentModifiers = Sets.newLinkedHashSet(); diff --git a/compiler/testData/codegen/dataClasses/copy/copyInNestedDataClass.kt b/compiler/testData/codegen/dataClasses/copy/copyInNestedDataClass.kt index bb0bb4ec3ce..6d40d4263e6 100644 --- a/compiler/testData/codegen/dataClasses/copy/copyInNestedDataClass.kt +++ b/compiler/testData/codegen/dataClasses/copy/copyInNestedDataClass.kt @@ -1,8 +1,8 @@ class Bar(val name: String) class Baz { - class Foo() { - data class NestedFoo(val bar: Bar) + inner class Foo() { + inner data class NestedFoo(val bar: Bar) fun foo(): String { return NestedFoo(Bar("FAIL")).copy(bar = Bar("OK")).bar.name diff --git a/compiler/testData/codegen/dataClasses/copy/copyInObjectNestedDataClass.kt b/compiler/testData/codegen/dataClasses/copy/copyInObjectNestedDataClass.kt index ccb3e90fbcb..3a6794efec9 100644 --- a/compiler/testData/codegen/dataClasses/copy/copyInObjectNestedDataClass.kt +++ b/compiler/testData/codegen/dataClasses/copy/copyInObjectNestedDataClass.kt @@ -6,7 +6,7 @@ abstract class Foo { fun box(): String { return object: Foo() { - data class NestedFoo(val bar: Bar) + inner data class NestedFoo(val bar: Bar) override fun foo(): String { return NestedFoo(Bar("Fail")).copy(bar = Bar("OK")).bar.name diff --git a/compiler/testData/codegen/regressions/kt2607.kt b/compiler/testData/codegen/regressions/kt2607.kt index 9bc3b5c2b35..85cf6e66d2e 100644 --- a/compiler/testData/codegen/regressions/kt2607.kt +++ b/compiler/testData/codegen/regressions/kt2607.kt @@ -1,7 +1,7 @@ fun box() : String { val o = object { - class C { + inner class C { fun foo() = "OK" } } diff --git a/compiler/testData/diagnostics/tests/inner/nestedClassNotAllowed.kt b/compiler/testData/diagnostics/tests/inner/nestedClassNotAllowed.kt new file mode 100644 index 00000000000..cf84e336468 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inner/nestedClassNotAllowed.kt @@ -0,0 +1,25 @@ +class A { + inner class B { + class C + } + + fun foo() { + class B { + class C + } + } +} + +fun foo() { + class B { + class C + } +} + + +enum class E { + E1 { + // Not allowed in Java, but no reason to disallow in Kotlin + class D + } +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index c2f4951dcee..fec5bc2aade 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2459,6 +2459,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage 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") public void testNestedVsInnerAccessOuterMember() throws Exception { doTest("compiler/testData/diagnostics/tests/inner/nestedVsInnerAccessOuterMember.kt");