From a05b37c6521873fe29e9addadf4fe54e257f0899 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 11 Oct 2023 13:06:05 +0200 Subject: [PATCH] [K2] Support reporting of SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS ...on regular classes and enum entries. ^KT-59979 Fixed --- .../api/fir/diagnostics/KtFirDiagnostics.kt | 2 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 2 +- .../diagnostics/FirDiagnosticsList.kt | 2 +- .../model/RegularDiagnosticData.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 2 +- .../fir/analysis/checkers/SourceNavigator.kt | 18 +++++++++++ .../FirExpectConsistencyChecker.kt | 18 +++++++++++ .../LightTreePositioningStrategies.kt | 30 +++++++++++++++++++ .../diagnostics/PositioningStrategies.kt | 15 ++++++++++ .../SourceElementPositioningStrategies.kt | 5 ++++ .../k2/basic/nonExternalEquals.kt | 2 +- .../enum/constructorInHeaderEnum.fir.kt | 4 +-- .../headerClass/superClass.fir.kt | 4 +-- 13 files changed, 96 insertions(+), 9 deletions(-) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index aa19ec51f47..b9f829ab998 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2512,7 +2512,7 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ExpectedLateinitProperty::class } - interface SupertypeInitializedInExpectedClass : KtFirDiagnostic { + interface SupertypeInitializedInExpectedClass : KtFirDiagnostic { override val diagnosticClass get() = SupertypeInitializedInExpectedClass::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index c8d3dfe64bb..1428f409ffe 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3027,7 +3027,7 @@ internal class ExpectedLateinitPropertyImpl( internal class SupertypeInitializedInExpectedClassImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, -) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.SupertypeInitializedInExpectedClass +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.SupertypeInitializedInExpectedClass internal class ExpectedPrivateDeclarationImpl( firDiagnostic: KtPsiDiagnostic, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 261c31db319..72e490815b1 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1229,7 +1229,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { // TODO: need to cover `by` as well as delegate expression val EXPECTED_DELEGATED_PROPERTY by error() val EXPECTED_LATEINIT_PROPERTY by error(PositioningStrategy.LATEINIT_MODIFIER) - val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error() + val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error(PositioningStrategy.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC) val EXPECTED_PRIVATE_DECLARATION by error(PositioningStrategy.VISIBILITY_MODIFIER) val EXPECTED_EXTERNAL_DECLARATION by error(PositioningStrategy.EXTERNAL_MODIFIER) val EXPECTED_TAILREC_FUNCTION by error(PositioningStrategy.TAILREC_MODIFIER) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index 38e9b2fb952..01d078f77e2 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -117,6 +117,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { CALL_ELEMENT_WITH_DOT, EXPECT_ACTUAL_MODIFIER, TYPEALIAS_TYPE_REFERENCE, + SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC, ; val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}" diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index b01299c5ddd..ea9f0aaad3e 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -642,7 +642,7 @@ object FirErrors { val EXPECTED_PROPERTY_INITIALIZER by error0() val EXPECTED_DELEGATED_PROPERTY by error0() val EXPECTED_LATEINIT_PROPERTY by error0(SourceElementPositioningStrategies.LATEINIT_MODIFIER) - val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error0() + val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error0(SourceElementPositioningStrategies.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC) val EXPECTED_PRIVATE_DECLARATION by error0(SourceElementPositioningStrategies.VISIBILITY_MODIFIER) val EXPECTED_EXTERNAL_DECLARATION by error0(SourceElementPositioningStrategies.EXTERNAL_MODIFIER) val EXPECTED_TAILREC_FUNCTION by error0(SourceElementPositioningStrategies.TAILREC_MODIFIER) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt index 9446bf88502..f7d910ac7f8 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt @@ -56,6 +56,13 @@ interface SourceNavigator { */ fun FirEnumEntry.hasBody(): Boolean? + /** + * Returns whether this [FirEnumEntry] has an initializer in source, or `null` if the entry does not have a source. + * + * Reason of implementing this in [SourceNavigator] and not in FIR is same as in [hasBody] method. + */ + fun FirEnumEntry.hasInitializer(): Boolean? + companion object { private val lightTreeInstance = LightTreeSourceNavigator() @@ -129,6 +136,12 @@ private open class LightTreeSourceNavigator : SourceNavigator { val childNodes = source.lighterASTNode.getChildren(source.treeStructure) return childNodes.any { it.tokenType == KtNodeTypes.CLASS_BODY } } + + override fun FirEnumEntry.hasInitializer(): Boolean? { + val source = source ?: return null + val childNodes = source.lighterASTNode.getChildren(source.treeStructure) + return childNodes.any { it.tokenType == KtNodeTypes.INITIALIZER_LIST } + } } //by default psi tree can reuse light tree manipulations @@ -176,4 +189,9 @@ private object PsiSourceNavigator : LightTreeSourceNavigator() { val enumEntryPsi = source?.psi as? KtEnumEntry ?: return null return enumEntryPsi.body != null } + + override fun FirEnumEntry.hasInitializer(): Boolean? { + val enumEntryPsi = source?.psi as? KtEnumEntry ?: return null + return enumEntryPsi.initializerList != null + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectConsistencyChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectConsistencyChecker.kt index 781d20c8adb..09a5dd80fa3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectConsistencyChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectConsistencyChecker.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall +import org.jetbrains.kotlin.fir.types.FirTypeRef // See old FE's [DeclarationsChecker] object FirExpectConsistencyChecker : FirBasicDeclarationChecker() { @@ -43,6 +44,9 @@ object FirExpectConsistencyChecker : FirBasicDeclarationChecker() { getConstructorDelegationCall(declaration)?.let { delegatedConstructor -> reporter.reportOn(delegatedConstructor.source, FirErrors.EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL, context) } + for (superTypeRef in getClassSuperTypeReferencesWithInitializers(declaration)) { + reporter.reportOn(superTypeRef.source, FirErrors.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, context) + } for (propertyParameter in getConstructorProhibitedPropertyParameters(declaration, lastClass)) { reporter.reportOn(propertyParameter.source, FirErrors.EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, context) } @@ -52,6 +56,9 @@ object FirExpectConsistencyChecker : FirBasicDeclarationChecker() { if (isProhibitedEnumEntryWithBody(declaration)) { reporter.reportOn(source, FirErrors.EXPECTED_ENUM_ENTRY_WITH_BODY, context) } + if (isProhibitedEnumEntryWithInitializer(declaration)) { + reporter.reportOn(source, FirErrors.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, context) + } if (isProhibitedPrivateDeclaration(declaration)) { reporter.reportOn(source, FirErrors.EXPECTED_PRIVATE_DECLARATION, context) @@ -87,6 +94,13 @@ object FirExpectConsistencyChecker : FirBasicDeclarationChecker() { return null } + private fun getClassSuperTypeReferencesWithInitializers(declaration: FirMemberDeclaration): List { + if (declaration !is FirRegularClass) return emptyList() + return declaration.withNavigator { + declaration.superTypeRefs.filter { it.isInConstructorCallee() } + } + } + private fun isProhibitedPrivateDeclaration(declaration: FirMemberDeclaration): Boolean { return declaration !is FirConstructor && declaration !is FirPropertyAccessor && Visibilities.isPrivate(declaration.visibility) } @@ -102,4 +116,8 @@ object FirExpectConsistencyChecker : FirBasicDeclarationChecker() { private fun isProhibitedEnumEntryWithBody(declaration: FirMemberDeclaration): Boolean { return declaration is FirEnumEntry && declaration.withNavigator { declaration.hasBody() == true } } + + private fun isProhibitedEnumEntryWithInitializer(declaration: FirMemberDeclaration): Boolean { + return declaration is FirEnumEntry && declaration.withNavigator { declaration.hasInitializer() == true } + } } diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt index 6a5285439f8..2ec29f3966d 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/LightTreePositioningStrategies.kt @@ -1233,6 +1233,29 @@ object LightTreePositioningStrategies { return markElement(nodeToMark, startOffset, endOffset, tree, node) } } + + val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + val nodeToMark = when (node.tokenType) { + KtNodeTypes.ENUM_ENTRY -> { + tree.findChildByType(node, KtNodeTypes.INITIALIZER_LIST) ?: node + } + KtNodeTypes.TYPE_REFERENCE -> { + val valueArgList = node.getParentIfTypeIs(KtNodeTypes.CONSTRUCTOR_CALLEE, tree) + ?.getParentIfTypeIs(KtNodeTypes.SUPER_TYPE_CALL_ENTRY, tree) + ?.let { tree.findChildByType(it, KtNodeTypes.VALUE_ARGUMENT_LIST) } + valueArgList ?: node + } + else -> node + } + return markElement(nodeToMark, startOffset, endOffset, tree, node) + } + } } fun KtSourceElement.hasValOrVar(): Boolean = @@ -1657,3 +1680,10 @@ private fun FlyweightCapableTreeStructure.lastChild(node: Lighte getChildren(node, childrenRef) return childrenRef.get().lastOrNull { it != null } } + +private fun LighterASTNode.getParentIfTypeIs( + tokenType: IElementType, + tree: FlyweightCapableTreeStructure, +): LighterASTNode? { + return tree.getParent(this)?.takeIf { it.tokenType == tokenType } +} diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 569dbdbdbe5..9442cdf09af 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -1046,6 +1046,21 @@ object PositioningStrategies { } } + @JvmField + val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC = object : PositioningStrategy() { + override fun mark(element: KtElement): List { + val elementToMark = when (element) { + is KtEnumEntry -> element.initializerList ?: element + is KtTypeReference -> { + val superTypeCallEntry = (element.parent as? KtConstructorCalleeExpression)?.parent as? KtSuperTypeCallEntry + superTypeCallEntry?.valueArgumentList ?: element + } + else -> element + } + return markElement(elementToMark) + } + } + /** * @param locateReferencedName whether to remove any nested parentheses while locating the reference element. This is useful for * diagnostics on super and unresolved references. For example, with the following, only the part inside the parentheses should be diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt index 79f0ac553e9..ca48e9ce437 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/SourceElementPositioningStrategies.kt @@ -394,4 +394,9 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.TYPEALIAS_TYPE_REFERENCE, PositioningStrategies.TYPEALIAS_TYPE_REFERENCE, ) + + val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC = SourceElementPositioningStrategy( + LightTreePositioningStrategies.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC, + PositioningStrategies.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS_DIAGNOSTIC, + ) } diff --git a/compiler/testData/codegen/box/multiplatform/k2/basic/nonExternalEquals.kt b/compiler/testData/codegen/box/multiplatform/k2/basic/nonExternalEquals.kt index 3bc6c61509b..50dcca01a0e 100644 --- a/compiler/testData/codegen/box/multiplatform/k2/basic/nonExternalEquals.kt +++ b/compiler/testData/codegen/box/multiplatform/k2/basic/nonExternalEquals.kt @@ -7,7 +7,7 @@ open class Base { open operator fun plus(b: Base) = Base() } -expect open class Derived constructor() : Base() { +expect open class Derived constructor() : Base { } // MODULE: main()()(common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt index 2899a73f525..6797b88127c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt @@ -3,12 +3,12 @@ expect enum class En(x: Int) { E1, - E2(42), + E2(42), ; constructor(s: String) } expect enum class En2 { - E1() + E1() } diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt index 86abe6ecf67..3619298b69c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/superClass.fir.kt @@ -7,9 +7,9 @@ interface J expect class Foo : I, C, J -expect class Bar : C() +expect class Bar : C() -expect class WithExplicitPrimaryConstructor() : C() +expect class WithExplicitPrimaryConstructor() : C() // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt