[FIR] Add diagnostic for missing primary constructor
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
class A
|
||||
class B : A
|
||||
|
||||
class C(x: Int)
|
||||
<!INAPPLICABLE_CANDIDATE!>class D : C<!>
|
||||
class E : C(10)
|
||||
class F() : C(10)
|
||||
|
||||
<!SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>class G : C(10) {
|
||||
constructor() : super(1)
|
||||
}<!>
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
FILE: supertypeInitializedWithoutPrimaryConstructor.kt
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class C : R|kotlin/Any| {
|
||||
public constructor(x: R|kotlin/Int|): R|C| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class D : R|C| {
|
||||
public constructor(): R|D| {
|
||||
super<R|C|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class E : R|C| {
|
||||
public constructor(): R|E| {
|
||||
super<R|C|>(Int(10))
|
||||
}
|
||||
|
||||
}
|
||||
public final class F : R|C| {
|
||||
public constructor(): R|F| {
|
||||
super<R|C|>(Int(10))
|
||||
}
|
||||
|
||||
}
|
||||
public final class G : R|C| {
|
||||
public constructor(): R|G| {
|
||||
super<R|C|>(Int(1))
|
||||
}
|
||||
|
||||
}
|
||||
Generated
+5
@@ -986,6 +986,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("supertypeInitializedWithoutPrimaryConstructor.kt")
|
||||
public void testSupertypeInitializedWithoutPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeArgumentsNotAllowed.kt")
|
||||
public void testTypeArgumentsNotAllowed() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/typeArgumentsNotAllowed.kt");
|
||||
|
||||
+5
@@ -986,6 +986,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("supertypeInitializedWithoutPrimaryConstructor.kt")
|
||||
public void testSupertypeInitializedWithoutPrimaryConstructor() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("typeArgumentsNotAllowed.kt")
|
||||
public void testTypeArgumentsNotAllowed() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/typeArgumentsNotAllowed.kt");
|
||||
|
||||
+1
@@ -21,6 +21,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirInfixFunctionDeclarationChecker,
|
||||
FirExposedVisibilityDeclarationChecker,
|
||||
FirCyclicConstructorDelegationCallChecker,
|
||||
FirSupertypeInitializedWithoutPrimaryConstructor,
|
||||
)
|
||||
|
||||
override val constructorCheckers: List<FirConstructorChecker> = listOf(
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import com.intellij.lang.LighterASTNode
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiErrorElement
|
||||
import com.intellij.util.diff.FlyweightCapableTreeStructure
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirLightSourceElement
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
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.lightNode
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
|
||||
|
||||
object FirSupertypeInitializedWithoutPrimaryConstructor : FirMemberDeclarationChecker() {
|
||||
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration !is FirRegularClass || declaration.classKind == ClassKind.INTERFACE) {
|
||||
return
|
||||
}
|
||||
|
||||
val hasSupertypeWithConstructor = declaration.source?.anySupertypeHasConstructorParentheses() == true
|
||||
val hasPrimaryConstructor = declaration.declarations.any { it is FirConstructor && it.isPrimary }
|
||||
|
||||
if (hasSupertypeWithConstructor && !hasPrimaryConstructor) {
|
||||
reporter.report(declaration.source)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirSourceElement.anySupertypeHasConstructorParentheses(): Boolean {
|
||||
val localPsi = psi
|
||||
val localLightNode = lightNode
|
||||
|
||||
if (localPsi != null && localPsi !is PsiErrorElement) {
|
||||
return localPsi.anySupertypeHasConstructorParentheses()
|
||||
} else if (localLightNode != null && this is FirLightSourceElement) {
|
||||
return localLightNode.anySupertypeHasConstructorParentheses(tree)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun PsiElement.anySupertypeHasConstructorParentheses(): Boolean {
|
||||
return children.isNotEmpty() && children[0] !is PsiErrorElement && children[0].children.any { it is KtSuperTypeCallEntry }
|
||||
}
|
||||
|
||||
private fun LighterASTNode.anySupertypeHasConstructorParentheses(tree: FlyweightCapableTreeStructure<LighterASTNode>): Boolean {
|
||||
val superTypes = getChildren(tree).find { it.tokenType == KtNodeTypes.SUPER_TYPE_LIST }
|
||||
?: return false
|
||||
|
||||
return superTypes.getChildren(tree).any { it.tokenType == KtNodeTypes.SUPER_TYPE_CALL_ENTRY }
|
||||
}
|
||||
|
||||
private fun LighterASTNode.getChildren(tree: FlyweightCapableTreeStructure<LighterASTNode>): List<LighterASTNode> {
|
||||
val children = Ref<Array<LighterASTNode?>>()
|
||||
val count = tree.getChildren(this, children)
|
||||
return if (count > 0) children.get().filterNotNull() else emptyList()
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.report(source: FirSourceElement?) {
|
||||
source?.let { report(FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR.on(it)) }
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ object FirErrors {
|
||||
|
||||
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>()
|
||||
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>()
|
||||
val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
|
||||
|
||||
// Exposed visibility group
|
||||
val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3<FirSourceElement, PsiElement, FirEffectiveVisibility, FirMemberDeclaration, FirEffectiveVisibility>()
|
||||
|
||||
+6
-1
@@ -1350,7 +1350,12 @@ class DeclarationsConverter(
|
||||
var delegateNumber = 0
|
||||
delegationSpecifiers.forEachChildren {
|
||||
when (it.tokenType) {
|
||||
SUPER_TYPE_ENTRY -> superTypeRefs += convertType(it)
|
||||
SUPER_TYPE_ENTRY -> {
|
||||
superTypeRefs += convertType(it)
|
||||
if (delegateConstructorSource == null) {
|
||||
delegateConstructorSource = it.toFirSourceElement()
|
||||
}
|
||||
}
|
||||
SUPER_TYPE_CALL_ENTRY -> convertConstructorInvocation(it).apply {
|
||||
delegatedSuperTypeRef = first
|
||||
superTypeRefs += first
|
||||
|
||||
Vendored
+2
-2
@@ -1,5 +1,5 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
open class B(x: Int)
|
||||
class A : B(1) {
|
||||
<!SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>class A : B(1) {
|
||||
constructor(): super(1)
|
||||
}
|
||||
}<!>
|
||||
|
||||
Reference in New Issue
Block a user