[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_MEMBER!>constructor(s: Nothing?) {}<!>
<!ANNOTATION_CLASS_MEMBER!><!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: Nothing?)<!> {}<!>
<!ANNOTATION_CLASS_MEMBER!>init {}<!>
<!ANNOTATION_CLASS_MEMBER!>fun foo() {}<!>
<!ANNOTATION_CLASS_MEMBER!>val bar: Nothing?<!>
@@ -42,16 +42,16 @@ class J<T> {
}
class F(s: String) {
constructor(i: Boolean) {}
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(i: Boolean)<!> {}
constructor(i: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(3) {}
}
class G(x: Int) {
constructor() {}
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!> {}
}
class H(x: Int) {
constructor()
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!>
}
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.FirRegularClass
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
object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker() {
@@ -22,11 +24,27 @@ object FirCyclicConstructorDelegationCallChecker : FirMemberDeclarationChecker()
}
val cyclicConstructors = mutableSetOf<FirConstructor>()
var hasPrimaryConstructor = false
for (it in declaration.declarations) {
if (it is FirConstructor && !it.isPrimary) {
it.findCycle(cyclicConstructors)?.let { visited ->
cyclicConstructors += visited
if (it is FirConstructor) {
if (!it.isPrimary) {
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
?.fir.safeAs()
private fun FirTypeRef.getType() = when (this) {
is FirResolvedTypeRef -> type
else -> null
}
private fun DiagnosticReporter.reportCyclicConstructorDelegationCall(source: FirSourceElement?) {
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 CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>()
// Exposed visibility group
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 test2(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) {}
}
@@ -20,7 +20,7 @@ abstract class C(vararg x1: Int, vararg x2: Int, b: Boolean) {
abstract fun test2(vararg x1: Int, vararg x2: Int)
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) {}
}
}
@@ -29,7 +29,7 @@ object O {
fun test(vararg x1: Int, vararg x2: Int) {}
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) {}
}
}
@@ -7,7 +7,7 @@ expect enum class En(x: Int) {
E2(42),
;
constructor(s: String)
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!>
}
expect enum class En2 {
@@ -9,7 +9,7 @@ expect class Foo(
"no"
}
constructor(s: String) {
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!> {
"no"
}
@@ -4,7 +4,7 @@
// FILE: common.kt
expect class Foo(zzz: Int) {
constructor(aaa: Boolean)
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(aaa: Boolean)<!>
fun f1(xxx: String): String
}
@@ -14,7 +14,7 @@ enum class B(x: Int) {
constructor(x: Int, y: Int): this(x+y)
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 {
@@ -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
class A(x: Int) {
<!PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor()<!>
@@ -2,6 +2,6 @@
open class A(p1: String)
class B() : A("") {
<!INAPPLICABLE_CANDIDATE!>constructor(s: String)<!> {
<!INAPPLICABLE_CANDIDATE, PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED!>constructor(s: String)<!> {
}
}