[FIR] Add diagnostic for missing primary constructor

This commit is contained in:
Nick
2020-08-03 15:07:54 +03:00
committed by Mikhail Glukhikh
parent 0f213e58db
commit b76f757d47
9 changed files with 148 additions and 3 deletions
@@ -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)
}<!>
@@ -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))
}
}
@@ -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");
@@ -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");
@@ -21,6 +21,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirInfixFunctionDeclarationChecker,
FirExposedVisibilityDeclarationChecker,
FirCyclicConstructorDelegationCallChecker,
FirSupertypeInitializedWithoutPrimaryConstructor,
)
override val constructorCheckers: List<FirConstructorChecker> = listOf(
@@ -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>()
@@ -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
@@ -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)
}
}<!>