[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,
)
}
add(FirErrors.MISSING_CONSTRUCTOR_KEYWORD) { firDiagnostic ->
MissingConstructorKeywordImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INVISIBLE_REFERENCE) { firDiagnostic ->
InvisibleReferenceImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
@@ -237,6 +237,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val diagnosticName: String
}
interface MissingConstructorKeyword : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = MissingConstructorKeyword::class
}
interface InvisibleReference : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = InvisibleReference::class
val reference: KtSymbol
@@ -270,6 +270,11 @@ internal class ErrorSuppressionImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.ErrorSuppression
internal class MissingConstructorKeywordImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.MissingConstructorKeyword
internal class InvisibleReferenceImpl(
override val reference: KtSymbol,
override val visible: Visibility,
@@ -108,6 +108,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ERROR_SUPPRESSION by warning<PsiElement> {
parameter<String>("diagnosticName")
}
val MISSING_CONSTRUCTOR_KEYWORD by error<PsiElement>()
}
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 INNER_ON_TOP_LEVEL_SCRIPT_CLASS by deprecationError0<PsiElement>(ProhibitScriptTopLevelInnerClasses)
val ERROR_SUPPRESSION by warning1<PsiElement, String>()
val MISSING_CONSTRUCTOR_KEYWORD by error0<PsiElement>()
// Unresolved
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_SECONDARY_CONSTRUCTOR_PARAMETER",
"INVISIBLE_SETTER",
"MISSING_CONSTRUCTOR_KEYWORD",
"INVISIBLE_REFERENCE",
"UNRESOLVED_REFERENCE",
"UNRESOLVED_LABEL",
@@ -8,20 +8,20 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtNodeTypes
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.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
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.FirErrorPrimaryConstructor
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.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.types.*
import org.jetbrains.kotlin.fir.types.coneType
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.utils.addToStdlib.lastIsInstanceOrNull
@@ -43,8 +43,7 @@ object FirPrimaryConstructorSuperTypeChecker : FirClassChecker() {
val primaryConstructorSymbol = declaration.primaryConstructorIfAny(context.session)
@OptIn(SymbolInternals::class)
if (primaryConstructorSymbol == null || primaryConstructorSymbol.fir is FirErrorPrimaryConstructor) {
if (primaryConstructorSymbol == null || primaryConstructorSymbol.isErrorPrimaryConstructor) {
checkSupertypeInitializedWithoutPrimaryConstructor(declaration, reporter, context)
} else {
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?) {
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.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.MISSING_CONSTRUCTOR_KEYWORD
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.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",
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_IMPORT, "Unresolved reference ''{0}''.", NULLABLE_STRING) // &
map.put(UNRESOLVED_LABEL, "Unresolved label.")
@@ -153,6 +153,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase))
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
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}")
}
@@ -904,14 +904,17 @@ class LightTreeRawFirDeclarationBuilder(
}
val constructorSymbol = FirConstructorSymbol(callableIdForClassConstructor())
var modifiers = Modifier()
var modifiersIfPresent: Modifier? = null
val valueParameters = mutableListOf<ValueParameter>()
var hasConstructorKeyword = false
primaryConstructor?.forEachChildren {
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)
}
}
val modifiers = modifiersIfPresent ?: Modifier()
val defaultVisibility = classWrapper.defaultConstructorVisibility()
val firDelegatedCall = runUnless(containingClassIsExpectClass) {
@@ -968,12 +971,10 @@ class LightTreeRawFirDeclarationBuilder(
isFromEnumClass = classWrapper.isEnum()
}
val builder = if (isErrorConstructor) {
FirErrorConstructorBuilder().apply {
diagnostic = ConeNoConstructorError
}
} else {
FirPrimaryConstructorBuilder()
val builder = when {
modifiersIfPresent != null && !hasConstructorKeyword -> createErrorConstructorBuilder(ConeMissingConstructorKeyword)
isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
else -> FirPrimaryConstructorBuilder()
}
builder.apply {
source = primaryConstructor?.toFirSourceElement()
@@ -1059,12 +1059,11 @@ open class PsiRawFirBuilder(
isFromSealedClass = owner.hasModifier(SEALED_KEYWORD) && explicitVisibility !== Visibilities.Private
isFromEnumClass = owner.hasModifier(ENUM_KEYWORD)
}
val builder = if (isErrorConstructor) {
FirErrorConstructorBuilder().apply {
diagnostic = ConeNoConstructorError
}
} else {
FirPrimaryConstructorBuilder()
val hasConstructorKeyword = this@toFirConstructor?.getConstructorKeyword() != null
val builder = when {
this?.modifierList != null && !hasConstructorKeyword -> createErrorConstructorBuilder(ConeMissingConstructorKeyword)
isErrorConstructor -> createErrorConstructorBuilder(ConeNoConstructorError)
else -> FirPrimaryConstructorBuilder()
}
builder.apply {
source = constructorSource
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.declarations.utils
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.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
@@ -68,3 +69,5 @@ val FirNamedFunctionSymbol.isMethodOfAny: Boolean
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"
}
object ConeMissingConstructorKeyword : ConeDiagnostic {
override val reason: String get() = "Use the 'constructor' keyword after the modifiers of the primary constructor."
}
enum class DiagnosticKind {
ExpressionExpected,
NotLoopLabel,
@@ -1,9 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
annotation class Ann(val x: Int = 1)
class A private (val x: Int) {
inner class B @Ann(2) (val y: Int)
class A <!MISSING_CONSTRUCTOR_KEYWORD!>private (val x: Int)<!> {
inner class B <!MISSING_CONSTRUCTOR_KEYWORD!>@Ann(2) (val y: Int)<!>
fun foo() {
class C private @Ann(3) (args: Int)
fun foo() {
class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3) (args: Int)<!>
}
}
@@ -1,9 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
annotation class Ann(val x: Int = 1)
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() {
class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3)<!> (args: Int)
fun foo() {
class C <!MISSING_CONSTRUCTOR_KEYWORD!>private @Ann(3)<!> (args: Int)
}
}