From 56bec6997cd5effa77d1b8c79bc5ede4f8e57702 Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Tue, 9 Mar 2021 15:25:05 -0800 Subject: [PATCH] FIR checker: report SUPERTYPE_NOT_INITIALIZED Combined this and the checker of SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR together. Also fixed SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR incorrectly repoted as warning instead of error. --- .../resolve/defaultJavaImportHiding.kt | 2 +- .../diagnostics/conflictingOverloads.kt | 2 +- .../resolve/diagnostics/notASupertype.kt | 2 +- .../propertyTypeMismatchOnOverride.kt | 2 +- .../returnTypeMismatchOnOverride.kt | 2 +- ...ypeInitializedWithoutPrimaryConstructor.kt | 4 +- .../negative/missingSealedInheritor.kt | 2 +- .../resolve/expresssions/checkArguments.kt | 2 +- .../resolve/expresssions/genericDecorator.kt | 2 +- .../inlineClasses/inlineClassDeclaration.kt | 2 +- .../resolve/multifile/sealedStarImport.kt | 2 +- .../resolve/visibility/exposedSupertype.kt | 6 +- .../visibility/exposedTypeParameters.kt | 2 +- .../diagnostics/FirDiagnosticsList.kt | 6 +- .../fir/analysis/diagnostics/FirErrors.kt | 6 +- .../kotlin/fir/analysis/FirSourceChild.kt | 40 ------- .../kotlin/fir/analysis/FirSourceUtils.kt | 91 ++++++++++++++++ .../FirPrimaryConstructorSuperTypeChecker.kt | 100 ++++++++++++++++++ ...ypeInitializedWithoutPrimaryConstructor.kt | 34 ------ .../diagnostics/FirDefaultErrorMessages.kt | 4 + .../fir/checkers/CommonDeclarationCheckers.kt | 2 +- .../diagnostics/tests/Constructors.fir.kt | 10 +- .../tests/SupertypeListChecks.fir.kt | 6 +- .../definiteReturn/simpleClass.fir.kt | 4 +- .../baseExpectClassWithoutConstructor.fir.kt | 2 +- .../tests/multiplatform/sealedTypeAlias.kt | 2 +- .../diagnostics/tests/objects/Objects.fir.kt | 4 +- .../diagnostics/KtFirDataClassConverters.kt | 7 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 8 ++ 30 files changed, 256 insertions(+), 107 deletions(-) delete mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceChild.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt delete mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypeInitializedWithoutPrimaryConstructor.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt b/compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt index e8292c347e9..e137027f386 100644 --- a/compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt +++ b/compiler/fir/analysis-tests/testData/resolve/defaultJavaImportHiding.kt @@ -21,7 +21,7 @@ class LinkedList : java.util.LinkedList() package util -class HashSet : java.util.HashSet +class HashSet : java.util.HashSet // FILE: main.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt index 737159ad2f5..9b4875e8c12 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/conflictingOverloads.kt @@ -16,7 +16,7 @@ fun test(z: Int, c: Char) {} } -class B : A { +class B : A { override fun rest(s: String) {} fun rest(s: String) {} diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt index 7d5354dc497..228db84d95d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt @@ -2,7 +2,7 @@ class A { fun f() {} } -class B : A { +class B : A { fun g() { super.f() super.f() diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt index 848b59e325a..ed71e8f5054 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt @@ -2,7 +2,7 @@ open class A { open var test: Number = 10 } -open class B : A { +open class B : A { override var test: Double = 20.0 } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt index 12775ff02b0..187ce893956 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt @@ -2,7 +2,7 @@ open class A { open fun test(): Number = 10 } -open class B : A { +open class B : A { override fun test(): Double = 20.0 fun test(x: Int) = x } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt index 40a8a7036d4..9fc2d8a4684 100644 --- a/compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/supertypeInitializedWithoutPrimaryConstructor.kt @@ -1,8 +1,8 @@ class A -class B : A +class B : A class C(x: Int) -class D : C +class D : C class E : C(10) class F() : C(10) diff --git a/compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt b/compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt index 9300b82cb1f..08cab15d1db 100644 --- a/compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt +++ b/compiler/fir/analysis-tests/testData/resolve/exhaustiveness/negative/missingSealedInheritor.kt @@ -2,7 +2,7 @@ sealed class Base -class A : Base +class A : Base // FILE: b.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/checkArguments.kt b/compiler/fir/analysis-tests/testData/resolve/expresssions/checkArguments.kt index 05dd484ed41..3f1c030937d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/expresssions/checkArguments.kt +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/checkArguments.kt @@ -1,6 +1,6 @@ class A open class B -class C : B +class C : B fun bar(a: A) = a diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/genericDecorator.kt b/compiler/fir/analysis-tests/testData/resolve/expresssions/genericDecorator.kt index 4bfa21deb37..fef9fba118d 100644 --- a/compiler/fir/analysis-tests/testData/resolve/expresssions/genericDecorator.kt +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/genericDecorator.kt @@ -14,6 +14,6 @@ public abstract class Decorator extends LookupElement { // FILE: test.kt -class MyDecorator : Decorator { +class MyDecorator : Decorator { override fun getLookupString(): String = delegate.lookupString } diff --git a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt index 7d2b7097a78..5a91276654f 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt +++ b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassDeclaration.kt @@ -14,6 +14,6 @@ class A { inline class CloneableClass2(val x: Int): java.lang.Cloneable open class Test -inline class ExtendTest(val x: Int): Test +inline class ExtendTest(val x: Int): Test inline class ImplementByDelegation(val x: Int) : Comparable by x diff --git a/compiler/fir/analysis-tests/testData/resolve/multifile/sealedStarImport.kt b/compiler/fir/analysis-tests/testData/resolve/multifile/sealedStarImport.kt index 4387c1c29b3..ca6c4c06191 100644 --- a/compiler/fir/analysis-tests/testData/resolve/multifile/sealedStarImport.kt +++ b/compiler/fir/analysis-tests/testData/resolve/multifile/sealedStarImport.kt @@ -5,7 +5,7 @@ package test sealed class Test { object O : Test() - class Extra(val x: Int): Test + class Extra(val x: Int): Test } // FILE: main.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/visibility/exposedSupertype.kt b/compiler/fir/analysis-tests/testData/resolve/visibility/exposedSupertype.kt index 168e52b8dfc..a9b9742ad19 100644 --- a/compiler/fir/analysis-tests/testData/resolve/visibility/exposedSupertype.kt +++ b/compiler/fir/analysis-tests/testData/resolve/visibility/exposedSupertype.kt @@ -28,7 +28,7 @@ private class C { } } -class D : A { +class D : A { class Test1 : A.AProtectedI { } @@ -42,7 +42,7 @@ class Test2 : A.APublicI, C { +class Test3 : C.CPublicI, C { } @@ -54,7 +54,7 @@ class Test5 : C.CPublicI, C.CPublic { +class Test6 : E, C.CPublic { } diff --git a/compiler/fir/analysis-tests/testData/resolve/visibility/exposedTypeParameters.kt b/compiler/fir/analysis-tests/testData/resolve/visibility/exposedTypeParameters.kt index eda6bd86bc6..ef94befdb5e 100644 --- a/compiler/fir/analysis-tests/testData/resolve/visibility/exposedTypeParameters.kt +++ b/compiler/fir/analysis-tests/testData/resolve/visibility/exposedTypeParameters.kt @@ -23,7 +23,7 @@ internal class Test4 // valid, B is internal private class Test5 -public class Container : C { +public class Container : C { // valid, D is protected in C protected class Test6 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 304502b2261..60ef05fd9c6 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 @@ -102,7 +102,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED by error() val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning() val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning(PositioningStrategy.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) - val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning() + val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by error() val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by warning() val PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS by error(PositioningStrategy.DECLARATION_NAME) val EXPLICIT_DELEGATION_CALL_REQUIRED by warning(PositioningStrategy.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) @@ -168,6 +168,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() { } } + val CLASSES_AND_INTERFACES by object : DiagnosticGroup("Classes and interfaces") { + val SUPERTYPE_NOT_INITIALIZED by error() + } + val INLINE_CLASSES by object : DiagnosticGroup("Inline classes") { val INLINE_CLASS_NOT_TOP_LEVEL by error(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) val INLINE_CLASS_NOT_FINAL by error(PositioningStrategy.MODALITY_MODIFIER) 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 51104193e1a..03f6ad6fa05 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 @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtSuperTypeEntry import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -105,7 +106,7 @@ object FirErrors { val NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED by error0() val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0() val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0(SourceElementPositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) - val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0() + val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by error0() val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by warning0() val PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS by error0(SourceElementPositioningStrategies.DECLARATION_NAME) val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0(SourceElementPositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) @@ -149,6 +150,9 @@ object FirErrors { val REDUNDANT_OPEN_IN_INTERFACE by warning0(SourceElementPositioningStrategies.OPEN_MODIFIER) val WRONG_MODIFIER_TARGET by error2() + // Classes and interfaces + val SUPERTYPE_NOT_INITIALIZED by error0() + // Inline classes val INLINE_CLASS_NOT_TOP_LEVEL by error0(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) val INLINE_CLASS_NOT_FINAL by error0(SourceElementPositioningStrategies.MODALITY_MODIFIER) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceChild.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceChild.kt deleted file mode 100644 index ba99f79d20f..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceChild.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2021 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 - -import com.intellij.psi.tree.IElementType -import com.intellij.psi.tree.TokenSet -import org.jetbrains.kotlin.fir.* - -fun FirSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1): FirSourceElement? { - return getChild(setOf(type), index, depth) -} - -fun FirSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1): FirSourceElement? { - return getChild(types.types.toSet(), index, depth) -} - -fun FirSourceElement.getChild(types: Set, index: Int = 0, depth: Int = -1): FirSourceElement? { - return when (this) { - is FirPsiSourceElement<*> -> { - getChild(types, index, depth) - } - is FirLightSourceElement -> { - getChild(types, index, depth) - } - else -> null - } -} - -private fun FirPsiSourceElement<*>.getChild(types: Set, index: Int, depth: Int): FirSourceElement? { - val visitor = PsiElementFinderByType(types, index, depth) - return visitor.find(psi)?.toFirPsiSourceElement() -} - -private fun FirLightSourceElement.getChild(types: Set, index: Int, depth: Int): FirSourceElement? { - val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth) - return visitor.find(lighterASTNode)?.toFirLightSourceElement(treeStructure) -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt new file mode 100644 index 00000000000..8dc8df478b6 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/FirSourceUtils.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2021 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 + +import com.intellij.lang.LighterASTNode +import com.intellij.psi.PsiElement +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.fir.* + +fun FirSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1): FirSourceElement? { + return getChild(setOf(type), index, depth) +} + +fun FirSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1): FirSourceElement? { + return getChild(types.types.toSet(), index, depth) +} + +fun FirSourceElement.getChild(types: Set, index: Int = 0, depth: Int = -1): FirSourceElement? { + return when (this) { + is FirPsiSourceElement<*> -> { + getChild(types, index, depth) + } + is FirLightSourceElement -> { + getChild(types, index, depth) + } + else -> null + } +} + +private fun FirPsiSourceElement<*>.getChild(types: Set, index: Int, depth: Int): FirSourceElement? { + val visitor = PsiElementFinderByType(types, index, depth) + return visitor.find(psi)?.toFirPsiSourceElement() +} + +private fun FirLightSourceElement.getChild(types: Set, index: Int, depth: Int): FirSourceElement? { + val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth) + return visitor.find(lighterASTNode)?.let { withNode(it) } +} + +fun FirSourceElement.getParent(type: IElementType, includingSelf: Boolean = false): FirSourceElement? { + return getParent(setOf(type), includingSelf) +} + +fun FirSourceElement.getParent(types: TokenSet, includingSelf: Boolean = false): FirSourceElement? { + return getParent(types.types.toSet(), includingSelf) +} + +fun FirSourceElement.getParent(types: Set, includingSelf: Boolean = false): FirSourceElement? { + return when (this) { + is FirPsiSourceElement<*> -> { + getParent(types, includingSelf) + } + is FirLightSourceElement -> { + getParent(types, includingSelf) + } + else -> null + } +} + +private fun FirPsiSourceElement<*>.getParent(types: Set, includingSelf: Boolean): FirSourceElement? { + var parent: PsiElement? = if (includingSelf) psi else psi.parent + while (parent != null && parent.node.elementType !in types) { + parent = parent.parent + } + return parent?.toFirPsiSourceElement() +} + +private fun FirLightSourceElement.getParent(types: Set, includingSelf: Boolean): FirSourceElement? { + var parent: LighterASTNode? = if (includingSelf) lighterASTNode else treeStructure.getParent(lighterASTNode) + while (parent != null && parent.tokenType !in types) { + parent = treeStructure.getParent(parent) + } + return parent?.let { withNode(it) } +} + +private fun FirLightSourceElement.withNode(newNode: LighterASTNode): FirLightSourceElement { + // It seems sometimes the `startOffset` from a `LighterASTNode` could count from some partial sub tree of the file. If this is the case, + // we need to compute the delta between the corresponding position in this partial tree and the file start. The latter can be retrieved + // from `FirLightSourceElement`. + val startDelta = this.startOffset - lighterASTNode.startOffset + val endDelta = this.endOffset - lighterASTNode.endOffset + return newNode.toFirLightSourceElement( + treeStructure, + startOffset = startDelta + newNode.startOffset, + endOffset = endDelta + newNode.endOffset + ) +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt new file mode 100644 index 00000000000..e38e8232c7b --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirPrimaryConstructorSuperTypeChecker.kt @@ -0,0 +1,100 @@ +/* + * 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 org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.getParent +import org.jetbrains.kotlin.fir.declarations.FirConstructor +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.isInterface +import org.jetbrains.kotlin.fir.declarations.primaryConstructor +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef +import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull + +/** Checker on super type declarations in the primary constructor of a class declaration. */ +object FirPrimaryConstructorSuperTypeChecker : FirRegularClassChecker() { + override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration.isInterface) { + return + } + + val primaryConstructor = declaration.primaryConstructor + + if (primaryConstructor == null) { + checkSupertypeInitializedWithoutPrimaryConstructor(declaration, reporter, context) + } else { + checkSuperTypeNotInitialized(primaryConstructor, declaration, context, reporter) + } + } + + /** + * SUPERTYPE_NOT_INITIALIZED is reported on code like the following. It's skipped if `A` has `()` after it, in which case any + * diagnostics for that constructor call will be reported, if applicable. + * + * ``` + * open class A + * class B : A + * ``` + */ + private fun checkSuperTypeNotInitialized( + primaryConstructor: FirConstructor, + regularClass: FirRegularClass, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + val containingClass = context.containingDeclarations.lastIsInstanceOrNull() + val delegatedConstructorCall = primaryConstructor.delegatedConstructor ?: return + // No need to check implicit call to the constructor of `kotlin.Any`. + if (delegatedConstructorCall.constructedTypeRef is FirImplicitAnyTypeRef) return + val superClass = delegatedConstructorCall.constructedTypeRef.coneType.toRegularClass(context.session) ?: return + // Subclassing a singleton should be reported as SINGLETON_IN_SUPERTYPE + if (superClass.classKind.isSingleton) return + if (regularClass.isEffectivelyExpect(containingClass, context) || + regularClass.isEffectivelyExternal(containingClass, context) + ) { + return + } + if (delegatedConstructorCall.source?.elementType != KtNodeTypes.SUPER_TYPE_CALL_ENTRY) { + reporter.reportOn( + delegatedConstructorCall.constructedTypeRef.source?.getParent(KtNodeTypes.SUPER_TYPE_ENTRY), + FirErrors.SUPERTYPE_NOT_INITIALIZED, + context + ) + } + } + + /** + * SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR is reported on code like the following, where `B` does not have a primary + * constructor, in which case, one can not call the delegated constructor of `A` in the super type list. `B` doesn't have a primary + * constructor because it doesn't declare it, nor is it implicitly created in presence of a explicitly declared constructor inside the + * class body. + * + * ``` + * open class A + * class B : A() { + * constructor() + * } + * ``` + */ + private fun checkSupertypeInitializedWithoutPrimaryConstructor( + regularClass: FirRegularClass, + reporter: DiagnosticReporter, + context: CheckerContext + ) { + for (superTypeRef in regularClass.superTypeRefs) { + val source = superTypeRef.source ?: continue + if (source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.CONSTRUCTOR_CALLEE) { + reporter.reportOn(regularClass.source, FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, context) + } + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypeInitializedWithoutPrimaryConstructor.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypeInitializedWithoutPrimaryConstructor.kt deleted file mode 100644 index f350c6d450c..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSupertypeInitializedWithoutPrimaryConstructor.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 org.jetbrains.kotlin.KtNodeTypes -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.analysis.diagnostics.reportOn -import org.jetbrains.kotlin.fir.declarations.FirConstructor -import org.jetbrains.kotlin.fir.declarations.FirRegularClass -import org.jetbrains.kotlin.fir.declarations.isInterface - -object FirSupertypeInitializedWithoutPrimaryConstructor : FirRegularClassChecker() { - override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { - if (declaration.isInterface) { - return - } - - if (declaration.declarations.any { it is FirConstructor && it.isPrimary }) { - return - } - - for (superTypeRef in declaration.superTypeRefs) { - val source = superTypeRef.source ?: continue - if (source.treeStructure.getParent(source.lighterASTNode)?.tokenType == KtNodeTypes.CONSTRUCTOR_CALLEE) { - reporter.reportOn(declaration.source, FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, context) - } - } - } -} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index dd12550c257..7c81af2aac9 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -184,6 +184,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_AC import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_NOT_A_CLASS_OR_INTERFACE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_NOT_INITIALIZED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_IS_NOT_AN_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPER_NOT_AVAILABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SYNTAX @@ -365,6 +366,9 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(REDUNDANT_OPEN_IN_INTERFACE, "Modifier 'open' is redundant for abstract interface members") map.put(WRONG_MODIFIER_TARGET, "Modifier ''{0}'' is not applicable to ''{1}''", TO_STRING, TO_STRING) + // Classes and interfaces + map.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here") + // Applicability map.put(NONE_APPLICABLE, "None of the following functions are applicable: {0}", SYMBOLS) map.put(INAPPLICABLE_CANDIDATE, "Inapplicable candidate(s): {0}", SYMBOL) diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index 0ac037f7846..6772017f00d 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -58,7 +58,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirMethodOfAnyImplementedInInterfaceChecker, FirDataClassPrimaryConstructorChecker, FirSupertypeInitializedInInterfaceChecker, - FirSupertypeInitializedWithoutPrimaryConstructor, + FirPrimaryConstructorSuperTypeChecker, FirTypeParametersInObjectChecker, FirMemberFunctionsChecker, FirMemberPropertiesChecker, diff --git a/compiler/testData/diagnostics/tests/Constructors.fir.kt b/compiler/testData/diagnostics/tests/Constructors.fir.kt index cddf4a45d93..9d78865c290 100644 --- a/compiler/testData/diagnostics/tests/Constructors.fir.kt +++ b/compiler/testData/diagnostics/tests/Constructors.fir.kt @@ -1,11 +1,11 @@ open class NoC -class NoC1 : NoC +class NoC1 : NoC class WithC0() : NoC() -open class WithC1() : NoC -class NoC2 : WithC1 +open class WithC1() : NoC +class NoC2 : WithC1 class NoC3 : WithC1() -class WithC2() : WithC1 +class WithC2() : WithC1 class WithPC0() { } @@ -32,4 +32,4 @@ class NoCPI { var ab = 1 get() = 1 set(v) {} -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/SupertypeListChecks.fir.kt b/compiler/testData/diagnostics/tests/SupertypeListChecks.fir.kt index 637e06a5411..695bbaad894 100644 --- a/compiler/testData/diagnostics/tests/SupertypeListChecks.fir.kt +++ b/compiler/testData/diagnostics/tests/SupertypeListChecks.fir.kt @@ -38,12 +38,12 @@ interface Test6 : C1 {} class CTest1() : OC1() {} -class CTest2 : C2 {} +class CTest2 : C2 {} -class CTest3 : C2, C3 {} +class CTest3 : C2, C3 {} class CTest4 : T1 {} class CTest5 : T1, T1 {} -class CTest6 : C1 {} \ No newline at end of file +class CTest6 : C1 {} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.fir.kt index 74d1173e4b2..27fb7bbe48a 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn/simpleClass.fir.kt @@ -10,10 +10,10 @@ package test.p; class C {fun f() {}} package test.p; open class G {open fun f(): T {} fun a() {}} // FILE: d.kt -package test.p; class G2 : G { fun g() : E {} override fun f() : E {}} +package test.p; class G2 : G { fun g() : E {} override fun f() : E {}} // FILE: e.kt package test.p; fun foo() {} // FILE: f.kt -package test.p; fun foo(a: C) {} \ No newline at end of file +package test.p; fun foo(a: C) {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/baseExpectClassWithoutConstructor.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/baseExpectClassWithoutConstructor.fir.kt index 1fc42183281..ab7dd939b17 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/baseExpectClassWithoutConstructor.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/baseExpectClassWithoutConstructor.fir.kt @@ -4,7 +4,7 @@ expect open class A expect class B : A -open class C : A +open class C : A // MODULE: m1-jvm(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/sealedTypeAlias.kt b/compiler/testData/diagnostics/tests/multiplatform/sealedTypeAlias.kt index 5c37dc14af6..aeb9f76e5f0 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/sealedTypeAlias.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/sealedTypeAlias.kt @@ -15,4 +15,4 @@ actual typealias Presence = P sealed class P { object Online : P() object Offline : P() -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/objects/Objects.fir.kt b/compiler/testData/diagnostics/tests/objects/Objects.fir.kt index 2b8bb4061c0..56cb0460e7d 100644 --- a/compiler/testData/diagnostics/tests/objects/Objects.fir.kt +++ b/compiler/testData/diagnostics/tests/objects/Objects.fir.kt @@ -4,9 +4,9 @@ package toplevelObjectDeclarations open fun foo() : Int = 1 } - class T : Foo {} + class T : Foo {} - object A : Foo { + object A : Foo { val x : Int = 2 fun test() : Int { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 3e803848417..a6afa523bee 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtSuperTypeEntry import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -554,6 +555,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SUPERTYPE_NOT_INITIALIZED) { firDiagnostic -> + SupertypeNotInitializedImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.INLINE_CLASS_NOT_TOP_LEVEL) { firDiagnostic -> InlineClassNotTopLevelImpl( firDiagnostic as FirPsiDiagnostic<*>, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 4b026b1a13d..4e221cae0d2 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtSuperTypeEntry import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -403,6 +404,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val target: String } + abstract class SupertypeNotInitialized : KtFirDiagnostic() { + override val diagnosticClass get() = SupertypeNotInitialized::class + } + abstract class InlineClassNotTopLevel : KtFirDiagnostic() { override val diagnosticClass get() = InlineClassNotTopLevel::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index a626ce7067c..bdee7bccaca 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtPropertyDelegate import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtSuperTypeEntry import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterList import org.jetbrains.kotlin.psi.KtTypeReference @@ -638,6 +639,13 @@ internal class WrongModifierTargetImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class SupertypeNotInitializedImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.SupertypeNotInitialized(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class InlineClassNotTopLevelImpl( firDiagnostic: FirPsiDiagnostic<*>, override val token: ValidityToken,