[FIR] Add diagnostic for primary constructor not called

This commit is contained in:
Nick
2020-07-31 18:01:52 +03:00
committed by Mikhail Glukhikh
parent bb0e1b7390
commit 0f213e58db
12 changed files with 44 additions and 23 deletions
@@ -1,5 +1,5 @@
annotation class A() { annotation class A() {
<!ANNOTATION_CLASS_MEMBER!>constructor(s: Nothing?) {}<!> <!ANNOTATION_CLASS_MEMBER!><!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: Nothing?)<!> {}<!>
<!ANNOTATION_CLASS_MEMBER!>init {}<!> <!ANNOTATION_CLASS_MEMBER!>init {}<!>
<!ANNOTATION_CLASS_MEMBER!>fun foo() {}<!> <!ANNOTATION_CLASS_MEMBER!>fun foo() {}<!>
<!ANNOTATION_CLASS_MEMBER!>val bar: Nothing?<!> <!ANNOTATION_CLASS_MEMBER!>val bar: Nothing?<!>
@@ -42,16 +42,16 @@ class J<T> {
} }
class F(s: String) { class F(s: String) {
constructor(i: Boolean) {} <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(i: Boolean)<!> {}
constructor(i: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(3) {} constructor(i: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(3) {}
} }
class G(x: Int) { class G(x: Int) {
constructor() {} <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!> {}
} }
class H(x: Int) { class H(x: Int) {
constructor() <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!>
} }
class K(x: Int) { class K(x: Int) {
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.declarations.FirConstructor
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker() { object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker() {
@@ -22,11 +24,27 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
} }
val cyclicConstructors = mutableSetOf<FirConstructor>() val cyclicConstructors = mutableSetOf<FirConstructor>()
var hasPrimaryConstructor = false
for (it in declaration.declarations) { for (it in declaration.declarations) {
if (it is FirConstructor && !it.isPrimary) { if (it is FirConstructor) {
it.findCycle(cyclicConstructors)?.let { visited -> if (!it.isPrimary) {
cyclicConstructors += visited it.findCycle(cyclicConstructors)?.let { visited ->
cyclicConstructors += visited
}
} else {
hasPrimaryConstructor = true
}
}
}
if (hasPrimaryConstructor) {
for (it in declaration.declarations) {
if (
it is FirConstructor && !it.isPrimary && it !in cyclicConstructors &&
it.delegatedConstructor?.constructedTypeRef?.getType() != it.returnTypeRef.getType()
) {
reporter.reportPrimaryConstructorDelegationCallExpected(it.delegatedConstructor?.source)
} }
} }
} }
@@ -60,7 +78,16 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
?.resolvedSymbol ?.resolvedSymbol
?.fir.safeAs() ?.fir.safeAs()
private fun FirTypeRef.getType() = when (this) {
is FirResolvedTypeRef -> type
else -> null
}
private fun DiagnosticReporter.reportCyclicConstructorDelegationCall(source: FirSourceElement?) { private fun DiagnosticReporter.reportCyclicConstructorDelegationCall(source: FirSourceElement?) {
source?.let { report(FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL.on(it)) } source?.let { report(FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL.on(it)) }
} }
private fun DiagnosticReporter.reportPrimaryConstructorDelegationCallExpected(source: FirSourceElement?) {
source?.let { report(FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.on(it)) }
}
} }
@@ -65,6 +65,7 @@ object FirErrors {
val NOT_AN_ANNOTATION_CLASS by error1<FirSourceElement, PsiElement, String>() val NOT_AN_ANNOTATION_CLASS by error1<FirSourceElement, PsiElement, String>()
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>() val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>()
// Exposed visibility group // Exposed visibility group
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<FirSourceElement, PsiElement, FirEffectiveVisibility, FirMemberDeclaration, FirEffectiveVisibility>() val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<FirSourceElement, PsiElement, FirEffectiveVisibility, FirMemberDeclaration, FirEffectiveVisibility>()
@@ -2,7 +2,7 @@
fun test(vararg x1: Int, vararg x2: Int) { fun test(vararg x1: Int, vararg x2: Int) {
fun test2(vararg x1: Int, vararg x2: Int) { fun test2(vararg x1: Int, vararg x2: Int) {
class LocalClass(vararg x1: Int, vararg x2: Int) { class LocalClass(vararg x1: Int, vararg x2: Int) {
constructor(vararg x1: Int, vararg x2: Int, xx: Int) {} <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(vararg x1: Int, vararg x2: Int, xx: Int)<!> {}
} }
fun test3(vararg x1: Int, vararg x2: Int) {} fun test3(vararg x1: Int, vararg x2: Int) {}
} }
@@ -20,7 +20,7 @@ abstract class C(vararg x1: Int, vararg x2: Int, b: Boolean) {
abstract fun test2(vararg x1: Int, vararg x2: Int) abstract fun test2(vararg x1: Int, vararg x2: Int)
class CC(vararg x1: Int, vararg x2: Int, b: Boolean) { class CC(vararg x1: Int, vararg x2: Int, b: Boolean) {
constructor(vararg x1: Int, vararg x2: Int) {} <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(vararg x1: Int, vararg x2: Int)<!> {}
fun test(vararg x1: Int, vararg x2: Int) {} fun test(vararg x1: Int, vararg x2: Int) {}
} }
} }
@@ -29,7 +29,7 @@ object O {
fun test(vararg x1: Int, vararg x2: Int) {} fun test(vararg x1: Int, vararg x2: Int) {}
class CC(vararg x1: Int, vararg x2: Int, b: Boolean) { class CC(vararg x1: Int, vararg x2: Int, b: Boolean) {
constructor(vararg x1: Int, vararg x2: Int) {} <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(vararg x1: Int, vararg x2: Int)<!> {}
fun test(vararg x1: Int, vararg x2: Int) {} fun test(vararg x1: Int, vararg x2: Int) {}
} }
} }
@@ -7,7 +7,7 @@ expect enum class En(x: Int) {
E2(42), E2(42),
; ;
constructor(s: String) <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!>
} }
expect enum class En2 { expect enum class En2 {
@@ -9,7 +9,7 @@ expect class Foo(
"no" "no"
} }
constructor(s: String) { <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!> {
"no" "no"
} }
@@ -4,7 +4,7 @@
// FILE: common.kt // FILE: common.kt
expect class Foo(zzz: Int) { expect class Foo(zzz: Int) {
constructor(aaa: Boolean) <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(aaa: Boolean)<!>
fun f1(xxx: String): String fun f1(xxx: String): String
} }
@@ -14,7 +14,7 @@ enum class B(x: Int) {
constructor(x: Int, y: Int): this(x+y) constructor(x: Int, y: Int): this(x+y)
constructor(x: Double): this(x.toInt(), 1) constructor(x: Double): this(x.toInt(), 1)
constructor(x: String): super(x, 1) constructor(x: String): <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>super<!>(x, 1)
} }
enum class C { enum class C {
@@ -1,8 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class A(x: Int) {
constructor()
}
open class B(x: Int)
class C(x: Int) : B(x) {
constructor(): super(1)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
class A(x: Int) { class A(x: Int) {
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!> <!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!>
@@ -2,6 +2,6 @@
open class A(p1: String) open class A(p1: String)
class B() : A("") { class B() : A("") {
<!INAPPLICABLE_CANDIDATE!>constructor(s: String)<!> { <!INAPPLICABLE_CANDIDATE, PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!> {
} }
} }