From a7fc32c8da441b32ffaf6b2e1189d62273e5106c Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 28 Feb 2017 15:28:20 +0300 Subject: [PATCH] Add diagnostic on calling inner classes constructors without receiver Otherwise there will be just an unresolved reference that doesn't give any useful information #KT-8959 Fixed --- .../calls/tower/ErrorCandidateFactory.kt | 7 ++- .../InnerClassAccessThroughEnum.kt | 4 +- .../tests/inner/constructorAccess.kt | 16 +++---- .../inner/innerConstructorsFromQualifiers.kt | 36 ++++++++++++++++ ...rsFromQualifiersWithIrrelevantCandidate.kt | 43 +++++++++++++++++++ .../tests/inner/innerErrorForClassObjects.kt | 4 +- .../tests/inner/innerErrorForObjects.kt | 4 +- .../constructNestedClass.kt | 8 ++-- .../diagnostics/tests/regressions/kt4827.kt | 4 +- .../tests/scopes/classHeader/constructors.kt | 2 +- .../tests/scopes/classHeader/delegation.kt | 2 +- .../classHeader/superConstructorArguments.kt | 2 +- .../tests/scopes/inheritance/innerClasses.kt | 10 ++--- .../scopes/inheritance/nestedFromJava.kt | 8 ++-- .../inheritance/nestedFromJavaAfterKotlin.kt | 4 +- .../typeAliasConstructorWrongClass.kt | 4 +- .../checkers/DiagnosticsTestGenerated.java | 12 ++++++ 17 files changed, 133 insertions(+), 37 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiers.kt create mode 100644 compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiersWithIrrelevantCandidate.kt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ErrorCandidateFactory.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ErrorCandidateFactory.kt index 6b642d5e0d3..0c33e90b052 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ErrorCandidateFactory.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ErrorCandidateFactory.kt @@ -31,6 +31,7 @@ enum class WrongResolutionToClassifier(val message: (Name) -> String) { INTERFACE_AS_VALUE({ "Interface $it does not have companion object" }), INTERFACE_AS_FUNCTION({ "Interface $it does not have constructors" }), CLASS_AS_VALUE({ "Class $it does not have companion object" }), + INNER_CLASS_CONSTRUCTOR_NO_RECEIVER({ "Constructor of inner class $it can be called only with receiver of containing class" }), OBJECT_AS_FUNCTION({ "Function 'invoke()' is not found in object $it" }) } @@ -92,7 +93,11 @@ private fun ErrorCandidateContext.asClassifierCall(asFunction: Boolean) { when (classifier.kind) { ClassKind.INTERFACE -> if (asFunction) INTERFACE_AS_FUNCTION else INTERFACE_AS_VALUE ClassKind.OBJECT -> if (asFunction) OBJECT_AS_FUNCTION else return - ClassKind.CLASS -> if (asFunction) return else CLASS_AS_VALUE + ClassKind.CLASS -> when { + asFunction && explicitReceiver is QualifierReceiver? && classifier.isInner -> INNER_CLASS_CONSTRUCTOR_NO_RECEIVER + !asFunction -> CLASS_AS_VALUE + else -> return + } else -> return } } diff --git a/compiler/testData/diagnostics/tests/classObjects/InnerClassAccessThroughEnum.kt b/compiler/testData/diagnostics/tests/classObjects/InnerClassAccessThroughEnum.kt index 3c8b8888cfe..9716e5b09cc 100644 --- a/compiler/testData/diagnostics/tests/classObjects/InnerClassAccessThroughEnum.kt +++ b/compiler/testData/diagnostics/tests/classObjects/InnerClassAccessThroughEnum.kt @@ -41,8 +41,8 @@ fun f() { C.O C.O.InO C.A() - C.B() + C.B() C.E3.O_O C.E3.G() -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inner/constructorAccess.kt b/compiler/testData/diagnostics/tests/inner/constructorAccess.kt index f31a5f1e4d7..961c29b2585 100644 --- a/compiler/testData/diagnostics/tests/inner/constructorAccess.kt +++ b/compiler/testData/diagnostics/tests/inner/constructorAccess.kt @@ -1,15 +1,15 @@ class Outer1 { class Nested - + class C1 { val b = Nested() } class C2(val b: Any = Nested()) inner class C3 { val b = Nested() } inner class C4(val b: Any = Nested()) - + inner class Inner - - class C5 { val b = Inner() } - class C6(val b: Any = Inner()) + + class C5 { val b = Inner() } + class C6(val b: Any = Inner()) inner class C7 { val b = Inner() } inner class C8(val b: Any = Inner()) } @@ -18,15 +18,15 @@ class Outer1 { class Outer2 { class Nested { fun foo() = Outer2() - fun bar() = Inner() + fun bar() = Inner() } inner class Inner { fun foo() = Outer2() fun bar() = Nested() } - + fun foo() { Nested() Inner() } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiers.kt b/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiers.kt new file mode 100644 index 00000000000..5a2c9ff2cbd --- /dev/null +++ b/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiers.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// SKIP_TXT +// FILE: Outer.kt +package abc +class Outer { + inner class Inner() { + constructor(x: Int) : this() {} + } + + companion object { + + fun baz() { + Inner() + Inner(1) + } + } +} + +fun foo() { + Outer.Inner() + Outer.Inner(1) +} + +// FILE: imported.kt +import abc.Outer +import abc.Outer.Inner + +fun bar() { + Inner() + Inner(1) + + with(Outer()) { + Inner() + Inner(1) + } +} diff --git a/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiersWithIrrelevantCandidate.kt b/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiersWithIrrelevantCandidate.kt new file mode 100644 index 00000000000..5c504d553c4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiersWithIrrelevantCandidate.kt @@ -0,0 +1,43 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// SKIP_TXT +// FILE: Outer.kt +package abc +class Outer { + inner class Inner() { + constructor(x: Int) : this() {} + } + + companion object { + fun Inner(x: String) {} + + fun baz() { + // Diagnostic here could be better (why can't I call the constructor above?) + Inner() + Inner(1) + Inner("") + } + } +} + +fun foo() { + Outer.Inner() + Outer.Inner(1) + Outer.Inner("") +} + +// FILE: imported.kt +import abc.Outer +import abc.Outer.Inner +import abc.Outer.Companion.Inner + +fun bar() { + Inner() + Inner(1) + Inner("") + + with(Outer()) { + Inner() + Inner(1) + Inner("") + } +} diff --git a/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt b/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt index 955710761ab..cf5fb902ea0 100644 --- a/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt +++ b/compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt @@ -10,7 +10,7 @@ class Test { } fun more(): InnerClass { - val b = InnerClass() + val b = InnerClass() val testVal = inClass foo() @@ -23,4 +23,4 @@ class Test { fun foo() {} open inner class InnerClass -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inner/innerErrorForObjects.kt b/compiler/testData/diagnostics/tests/inner/innerErrorForObjects.kt index ad861aecc98..e2752bf0eba 100644 --- a/compiler/testData/diagnostics/tests/inner/innerErrorForObjects.kt +++ b/compiler/testData/diagnostics/tests/inner/innerErrorForObjects.kt @@ -10,7 +10,7 @@ class Test { } fun more(): InnerClass { - val b = InnerClass() + val b = InnerClass() val testVal = inClass foo() @@ -24,4 +24,4 @@ class Test { } open inner class InnerClass -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inner/qualifiedExpression/constructNestedClass.kt b/compiler/testData/diagnostics/tests/inner/qualifiedExpression/constructNestedClass.kt index 7b4b0e32373..8a67ae48bf4 100644 --- a/compiler/testData/diagnostics/tests/inner/qualifiedExpression/constructNestedClass.kt +++ b/compiler/testData/diagnostics/tests/inner/qualifiedExpression/constructNestedClass.kt @@ -2,7 +2,7 @@ class Outer { class Nested { class NestedNested } - + inner class Inner { inner class InnerInner } @@ -11,7 +11,7 @@ class Outer { fun f1() = Outer() fun f2() = Outer.Nested() fun f3() = Outer.Nested.NestedNested() -fun f4() = Outer.Inner() -fun f5() = Outer.Inner.InnerInner() +fun f4() = Outer.Inner() +fun f5() = Outer.Inner.InnerInner() fun f6() = Outer().Inner() -fun f7() = Outer().Inner().InnerInner() \ No newline at end of file +fun f7() = Outer().Inner().InnerInner() diff --git a/compiler/testData/diagnostics/tests/regressions/kt4827.kt b/compiler/testData/diagnostics/tests/regressions/kt4827.kt index 0c0f96a428c..76cfccefc69 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt4827.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt4827.kt @@ -12,5 +12,5 @@ class C { fun f() { TestInterface() - C.I() -} \ No newline at end of file + C.I() +} diff --git a/compiler/testData/diagnostics/tests/scopes/classHeader/constructors.kt b/compiler/testData/diagnostics/tests/scopes/classHeader/constructors.kt index 27b65294ae5..b554c0708fe 100644 --- a/compiler/testData/diagnostics/tests/scopes/classHeader/constructors.kt +++ b/compiler/testData/diagnostics/tests/scopes/classHeader/constructors.kt @@ -4,7 +4,7 @@ class A( n: Nested = foo(), n2: Nested = Nested(), inn: Inner = null!!, - inn2: Inner = Inner(), + inn2: Inner = Inner(), i: Interface = null!!, c: Int = CONST, cc: Int = Companion.CONST, diff --git a/compiler/testData/diagnostics/tests/scopes/classHeader/delegation.kt b/compiler/testData/diagnostics/tests/scopes/classHeader/delegation.kt index f89c11c2cff..813443209c9 100644 --- a/compiler/testData/diagnostics/tests/scopes/classHeader/delegation.kt +++ b/compiler/testData/diagnostics/tests/scopes/classHeader/delegation.kt @@ -17,7 +17,7 @@ open class S( class A : I by S( foo(), Nested(), - Inner(), + Inner(), CONST, Companion.CONST, Nested.CONST, diff --git a/compiler/testData/diagnostics/tests/scopes/classHeader/superConstructorArguments.kt b/compiler/testData/diagnostics/tests/scopes/classHeader/superConstructorArguments.kt index e8d6d6d37d2..ec88083051b 100644 --- a/compiler/testData/diagnostics/tests/scopes/classHeader/superConstructorArguments.kt +++ b/compiler/testData/diagnostics/tests/scopes/classHeader/superConstructorArguments.kt @@ -15,7 +15,7 @@ open class S( class A : S ( foo(), Nested(), - Inner(), + Inner(), CONST, Companion.CONST, Nested.CONST, diff --git a/compiler/testData/diagnostics/tests/scopes/inheritance/innerClasses.kt b/compiler/testData/diagnostics/tests/scopes/inheritance/innerClasses.kt index 721d41ebe6b..85559597aa2 100644 --- a/compiler/testData/diagnostics/tests/scopes/inheritance/innerClasses.kt +++ b/compiler/testData/diagnostics/tests/scopes/inheritance/innerClasses.kt @@ -33,10 +33,10 @@ class E: A() { object Z { init { - B().foo() - B().bar() + B().foo() + B().bar() - D() + D() C() } } @@ -58,7 +58,7 @@ class F: A() { companion object { init { B().fas() - D().f() + D().f() } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJava.kt b/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJava.kt index 2eebc48b594..dd7d7498570 100644 --- a/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJava.kt +++ b/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJava.kt @@ -46,7 +46,7 @@ class Y: B() { init { B_() - B.B_() + B.B_() Y.B_() B_S() @@ -59,7 +59,7 @@ class Y: B() { val b_s: B_S = null!! init { - B_() + B_() B_S() } } @@ -83,8 +83,8 @@ class Z: C() { init { A_S() - B_() + B_() B_S() } } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJavaAfterKotlin.kt b/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJavaAfterKotlin.kt index 643f3673ed2..e4e00ca3e28 100644 --- a/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJavaAfterKotlin.kt +++ b/compiler/testData/diagnostics/tests/scopes/inheritance/nestedFromJavaAfterKotlin.kt @@ -36,7 +36,7 @@ public class F extends D { class X: D() { init { B_() - B.B_() + B.B_() C.B_() D.B_() X.B_() @@ -79,4 +79,4 @@ class Y: F() { F.E_S() Y.E_S() } -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt index e8278e5fe0d..1859412c047 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.kt @@ -27,8 +27,8 @@ class Outer { typealias Test5 = Outer.Inner val test5 = Test5() -val test5a = Outer.Inner() +val test5a = Outer.Inner() val test5b = Outer.TestInner() val test5c = Outer().TestInner() val test5d = Outer().Inner() -val test5e = Outer().Test5() \ No newline at end of file +val test5e = Outer().Test5() diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index a9614505d1a..86b067fb2b1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11347,6 +11347,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("innerConstructorsFromQualifiers.kt") + public void testInnerConstructorsFromQualifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiers.kt"); + doTest(fileName); + } + + @TestMetadata("innerConstructorsFromQualifiersWithIrrelevantCandidate.kt") + public void testInnerConstructorsFromQualifiersWithIrrelevantCandidate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inner/innerConstructorsFromQualifiersWithIrrelevantCandidate.kt"); + doTest(fileName); + } + @TestMetadata("innerErrorForClassObjects.kt") public void testInnerErrorForClassObjects() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inner/innerErrorForClassObjects.kt");