[FIR] Report MISSING_CONSTRUCTOR_KEYWORD

^KT-59407 Fixed
This commit is contained in:
Nikolay Lunyak
2023-08-17 16:54:40 +03:00
committed by Space Team
parent f238ffa4bf
commit c33f1cda69
16 changed files with 59 additions and 29 deletions
@@ -278,6 +278,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, token,
) )
} }
add(FirErrors.MISSING_CONSTRUCTOR_KEYWORD) { firDiagnostic ->
MissingConstructorKeywordImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INVISIBLE_REFERENCE) { firDiagnostic -> add(FirErrors.INVISIBLE_REFERENCE) { firDiagnostic ->
InvisibleReferenceImpl( InvisibleReferenceImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a), firSymbolBuilder.buildSymbol(firDiagnostic.a),
@@ -237,6 +237,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val diagnosticName: String val diagnosticName: String
} }
interface MissingConstructorKeyword : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = MissingConstructorKeyword::class
}
interface InvisibleReference : KtFirDiagnostic<PsiElement> { interface InvisibleReference : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = InvisibleReference::class override val diagnosticClass get() = InvisibleReference::class
val reference: KtSymbol val reference: KtSymbol
@@ -270,6 +270,11 @@ internal class ErrorSuppressionImpl(
token: KtLifetimeToken, token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ErrorSuppression ) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ErrorSuppression
internal class MissingConstructorKeywordImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.MissingConstructorKeyword
internal class InvisibleReferenceImpl( internal class InvisibleReferenceImpl(
override val reference: KtSymbol, override val reference: KtSymbol,
override val visible: Visibility, override val visible: Visibility,
@@ -108,6 +108,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ERROR_SUPPRESSION by warning<PsiElement> { val ERROR_SUPPRESSION by warning<PsiElement> {
parameter<String>("diagnosticName") parameter<String>("diagnosticName")
} }
val MISSING_CONSTRUCTOR_KEYWORD by error<PsiElement>()
} }
val UNRESOLVED by object : DiagnosticGroup("Unresolved") { val UNRESOLVED by object : DiagnosticGroup("Unresolved") {
@@ -153,6 +153,7 @@ object FirErrors {
val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED) val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError0<PsiElement>(ProhibitScriptTopLevelInnerClasses) val INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError0<PsiElement>(ProhibitScriptTopLevelInnerClasses)
val ERROR_SUPPRESSION by warning1<PsiElement, String>() val ERROR_SUPPRESSION by warning1<PsiElement, String>()
val MISSING_CONSTRUCTOR_KEYWORD by error0<PsiElement>()
// Unresolved // Unresolved
val INVISIBLE_REFERENCE by error3<PsiElement, FirBasedSymbol<*>, Visibility, ClassId?>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val INVISIBLE_REFERENCE by error3<PsiElement, FirBasedSymbol<*>, Visibility, ClassId?>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -40,6 +40,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"VAL_OR_VAR_ON_CATCH_PARAMETER", "VAL_OR_VAR_ON_CATCH_PARAMETER",
"VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER", "VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER",
"INVISIBLE_SETTER", "INVISIBLE_SETTER",
"MISSING_CONSTRUCTOR_KEYWORD",
"INVISIBLE_REFERENCE", "INVISIBLE_REFERENCE",
"UNRESOLVED_REFERENCE", "UNRESOLVED_REFERENCE",
"UNRESOLVED_LABEL", "UNRESOLVED_LABEL",
@@ -8,20 +8,20 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.descriptors.isEnumEntry import org.jetbrains.kotlin.descriptors.isEnumEntry
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirErrorPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.declarations.utils.isErrorPrimaryConstructor
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
@@ -43,8 +43,7 @@ object FirPrimaryConstructorSuperTypeChecker : FirClassChecker() {
val primaryConstructorSymbol = declaration.primaryConstructorIfAny(context.session) val primaryConstructorSymbol = declaration.primaryConstructorIfAny(context.session)
@OptIn(SymbolInternals::class) if (primaryConstructorSymbol == null || primaryConstructorSymbol.isErrorPrimaryConstructor) {
if (primaryConstructorSymbol == null || primaryConstructorSymbol.fir is FirErrorPrimaryConstructor) {
checkSupertypeInitializedWithoutPrimaryConstructor(declaration, reporter, context) checkSupertypeInitializedWithoutPrimaryConstructor(declaration, reporter, context)
} else { } else {
checkSuperTypeNotInitialized(primaryConstructorSymbol, declaration, context, reporter) checkSuperTypeNotInitialized(primaryConstructorSymbol, declaration, context, reporter)
@@ -122,6 +122,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
} }
} }
override fun visitErrorPrimaryConstructor(errorPrimaryConstructor: FirErrorPrimaryConstructor, data: Nothing?) =
visitConstructor(errorPrimaryConstructor, data)
override fun visitAnonymousFunctionExpression(anonymousFunctionExpression: FirAnonymousFunctionExpression, data: Nothing?) { override fun visitAnonymousFunctionExpression(anonymousFunctionExpression: FirAnonymousFunctionExpression, data: Nothing?) {
visitAnonymousFunction(anonymousFunctionExpression.anonymousFunction, data) visitAnonymousFunction(anonymousFunctionExpression.anonymousFunction, data)
} }
@@ -355,6 +355,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_M
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_CONSTRUCTOR_KEYWORD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_STDLIB_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_STDLIB_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MIXING_FUNCTIONAL_KINDS_IN_SUPERTYPES import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MIXING_FUNCTIONAL_KINDS_IN_SUPERTYPES
@@ -765,6 +766,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"This code uses error suppression for ''{0}''. While it might compile and work, the compiler behavior is UNSPECIFIED and WON''T BE PRESERVED. Please report your use case to the Kotlin issue tracker instead: https://kotl.in/issue", "This code uses error suppression for ''{0}''. While it might compile and work, the compiler behavior is UNSPECIFIED and WON''T BE PRESERVED. Please report your use case to the Kotlin issue tracker instead: https://kotl.in/issue",
TO_STRING TO_STRING
) )
map.put(MISSING_CONSTRUCTOR_KEYWORD, "Use the 'constructor' keyword after the modifiers of the primary constructor.")
map.put(UNRESOLVED_REFERENCE, "Unresolved reference ''{0}''.", NULLABLE_STRING) map.put(UNRESOLVED_REFERENCE, "Unresolved reference ''{0}''.", NULLABLE_STRING)
map.put(UNRESOLVED_IMPORT, "Unresolved reference ''{0}''.", NULLABLE_STRING) // & map.put(UNRESOLVED_IMPORT, "Unresolved reference ''{0}''.", NULLABLE_STRING) // &
map.put(UNRESOLVED_LABEL, "Unresolved label.") map.put(UNRESOLVED_LABEL, "Unresolved label.")
@@ -153,6 +153,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase)) FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase))
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds) is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
is ConeUnsupportedClassLiteralsWithEmptyLhs -> FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS.createOn(source) is ConeUnsupportedClassLiteralsWithEmptyLhs -> FirErrors.UNSUPPORTED_CLASS_LITERALS_WITH_EMPTY_LHS.createOn(source)
is ConeMissingConstructorKeyword -> FirErrors.MISSING_CONSTRUCTOR_KEYWORD.createOn(source)
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
} }
@@ -904,14 +904,17 @@ class LightTreeRawFirDeclarationBuilder(
} }
val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor()) val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor())
var modifiers = Modifier() var modifiersIfPresent: Modifier? = null
val valueParameters = mutableListOf<ValueParameter>() val valueParameters = mutableListOf<ValueParameter>()
var hasConstructorKeyword = false
primaryConstructor?.forEachChildren { primaryConstructor?.forEachChildren {
when (it.tokenType) { when (it.tokenType) {
MODIFIER_LIST -> modifiers = convertModifierList(it) MODIFIER_LIST -> modifiersIfPresent = convertModifierList(it)
CONSTRUCTOR_KEYWORD -> hasConstructorKeyword = true
VALUE_PARAMETER_LIST -> valueParameters += convertValueParameters(it, constructorSymbol, ValueParameterDeclaration.PRIMARY_CONSTRUCTOR) VALUE_PARAMETER_LIST -> valueParameters += convertValueParameters(it, constructorSymbol, ValueParameterDeclaration.PRIMARY_CONSTRUCTOR)
} }
} }
val modifiers = modifiersIfPresent ?: Modifier()
val defaultVisibility = classWrapper.defaultConstructorVisibility() val defaultVisibility = classWrapper.defaultConstructorVisibility()
val firDelegatedCall = runUnless(containingClassIsExpectClass) { val firDelegatedCall = runUnless(containingClassIsExpectClass) {
@@ -968,12 +971,10 @@ class LightTreeRawFirDeclarationBuilder(
isFromEnumClass = classWrapper.isEnum() isFromEnumClass = classWrapper.isEnum()
} }
val builder = if (isErrorConstructor) { val builder = when {
FirErrorConstructorBuilder().apply { modifiersIfPresent != null && !hasConstructorKeyword -> createErrorConstructorBuilder(ConeMissingConstructorKeyword)
diagnostic = ConeNoConstructorError isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
} else -> FirPrimaryConstructorBuilder()
} else {
FirPrimaryConstructorBuilder()
} }
builder.apply { builder.apply {
source = primaryConstructor?.toFirSourceElement() source = primaryConstructor?.toFirSourceElement()
@@ -1059,12 +1059,11 @@ open class PsiRawFirBuilder(
isFromSealedClass = owner.hasModifier(SEALED_KEYWORD) && explicitVisibility !== Visibilities.Private isFromSealedClass = owner.hasModifier(SEALED_KEYWORD) && explicitVisibility !== Visibilities.Private
isFromEnumClass = owner.hasModifier(ENUM_KEYWORD) isFromEnumClass = owner.hasModifier(ENUM_KEYWORD)
} }
val builder = if (isErrorConstructor) { val hasConstructorKeyword = this@toFirConstructor?.getConstructorKeyword() != null
FirErrorConstructorBuilder().apply { val builder = when {
diagnostic = ConeNoConstructorError this?.modifierList != null && !hasConstructorKeyword -> createErrorConstructorBuilder(ConeMissingConstructorKeyword)
} isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
} else { else -> FirPrimaryConstructorBuilder()
FirPrimaryConstructorBuilder()
} }
builder.apply { builder.apply {
source = constructorSource source = constructorSource
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.declarations.utils package org.jetbrains.kotlin.fir.declarations.utils
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.coneTypeSafe
@@ -68,3 +69,5 @@ val FirNamedFunctionSymbol.isMethodOfAny: Boolean
else -> false else -> false
} }
} }
val FirConstructorSymbol.isErrorPrimaryConstructor get() = fir is FirErrorPrimaryConstructor
@@ -83,6 +83,10 @@ object ConeNoConstructorError : ConeDiagnostic {
override val reason: String get() = "This type does not have a constructor" override val reason: String get() = "This type does not have a constructor"
} }
object ConeMissingConstructorKeyword : ConeDiagnostic {
override val reason: String get() = "Use the 'constructor' keyword after the modifiers of the primary constructor."
}
enum class DiagnosticKind { enum class DiagnosticKind {
ExpressionExpected, ExpressionExpected,
NotLoopLabel, NotLoopLabel,
@@ -1,9 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
annotation class Ann(val x: Int = 1) annotation class Ann(val x: Int = 1)
class A private (val x: Int) { class A <!MISSING_CONSTRUCTOR_KEYWORD!>private (val x: Int)<!> {
inner class B @Ann(2) (val y: Int) inner class B <!MISSING_CONSTRUCTOR_KEYWORD!>@Ann(2) (val y: Int)<!>
fun foo() { fun foo() {
class C private @Ann(3) (args: Int) class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3) (args: Int)<!>
} }
} }
@@ -1,9 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
annotation class Ann(val x: Int = 1) annotation class Ann(val x: Int = 1)
class A <!MISSING_CONSTRUCTOR_KEYWORD!>private<!> (val x: Int) { class A <!MISSING_CONSTRUCTOR_KEYWORD!>private<!> (val x: Int) {
inner class B <!MISSING_CONSTRUCTOR_KEYWORD!>@Ann(2)<!> (val y: Int) inner class B <!MISSING_CONSTRUCTOR_KEYWORD!>@Ann(2)<!> (val y: Int)
fun foo() { fun foo() {
class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3)<!> (args: Int) class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3)<!> (args: Int)
} }
} }