[FIR] Create checker for simple enum entries with missing overrides

Complex enum entries (those with bodies) with missing overrides
are covered by an existing checker that checks all types of classes. But
simple enum entries (those without bodies) were not covered and require
a separate checker for missing overrides.

#KT-58637 Fixed
This commit is contained in:
Brian Norman
2023-05-22 10:50:34 -05:00
committed by Space Team
parent 5bf51318eb
commit 8091aebbc7
11 changed files with 91 additions and 4 deletions
@@ -2474,6 +2474,16 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY) { firDiagnostic ->
AbstractMemberNotImplementedByEnumEntryImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b.map { firCallableSymbol ->
firSymbolBuilder.callableBuilder.buildCallableSymbol(firCallableSymbol)
},
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED) { firDiagnostic ->
AbstractClassMemberNotImplementedImpl(
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a),
@@ -1758,6 +1758,12 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val missingDeclaration: KtCallableSymbol
}
interface AbstractMemberNotImplementedByEnumEntry : KtFirDiagnostic<KtEnumEntry> {
override val diagnosticClass get() = AbstractMemberNotImplementedByEnumEntry::class
val enumEntry: KtSymbol
val missingDeclarations: List<KtCallableSymbol>
}
interface AbstractClassMemberNotImplemented : KtFirDiagnostic<KtClassOrObject> {
override val diagnosticClass get() = AbstractClassMemberNotImplemented::class
val classOrObject: KtClassLikeSymbol
@@ -2110,6 +2110,13 @@ internal class AbstractMemberNotImplementedImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtClassOrObject>(firDiagnostic, token), KtFirDiagnostic.AbstractMemberNotImplemented
internal class AbstractMemberNotImplementedByEnumEntryImpl(
override val enumEntry: KtSymbol,
override val missingDeclarations: List<KtCallableSymbol>,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtEnumEntry>(firDiagnostic, token), KtFirDiagnostic.AbstractMemberNotImplementedByEnumEntry
internal class AbstractClassMemberNotImplementedImpl(
override val classOrObject: KtClassLikeSymbol,
override val missingDeclaration: KtCallableSymbol,
@@ -861,6 +861,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<FirClassSymbol<*>>("classOrObject")
parameter<FirCallableSymbol<*>>("missingDeclaration")
}
val ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY by error<KtEnumEntry>(PositioningStrategy.DECLARATION_NAME) {
parameter<FirEnumEntrySymbol>("enumEntry")
parameter<List<FirCallableSymbol<*>>>("missingDeclarations")
}
val ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED by error<KtClassOrObject>(PositioningStrategy.DECLARATION_NAME) {
parameter<FirClassSymbol<*>>("classOrObject")
parameter<FirCallableSymbol<*>>("missingDeclaration")
@@ -471,6 +471,7 @@ object FirErrors {
val VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION by error2<KtClassOrObject, FirCallableSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME)
val CONFLICTING_INHERITED_MEMBERS by error2<KtClassOrObject, FirClassSymbol<*>, List<FirCallableSymbol<*>>>(SourceElementPositioningStrategies.DECLARATION_NAME)
val ABSTRACT_MEMBER_NOT_IMPLEMENTED by error2<KtClassOrObject, FirClassSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME)
val ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY by error2<KtEnumEntry, FirEnumEntrySymbol, List<FirCallableSymbol<*>>>(SourceElementPositioningStrategies.DECLARATION_NAME)
val ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED by error2<KtClassOrObject, FirClassSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME)
val INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER by deprecationError2<KtClassOrObject, FirClassSymbol<*>, FirCallableSymbol<*>>(ProhibitInvisibleAbstractMethodsInSuperclasses, SourceElementPositioningStrategies.DECLARATION_NAME)
val AMBIGUOUS_ANONYMOUS_TYPE_INFERRED by error1<KtDeclaration, Collection<ConeKotlinType>>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
@@ -92,6 +92,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
get() = setOf(
FirOverrideChecker,
FirNotImplementedOverrideChecker,
FirNotImplementedOverrideSimpleEnumEntryChecker,
FirThrowableSubclassChecker,
FirOpenMemberChecker,
FirClassVarianceChecker,
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2023 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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.scopes.processAllCallables
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
object FirNotImplementedOverrideSimpleEnumEntryChecker : FirClassChecker() {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.isEnumClass) return
// Enum entries with an initializer are handled by FirNotImplementedOverrideChecker since they contain an AnonymousObject.
val enumEntries = declaration.declarations.filterIsInstance<FirEnumEntry>().filter { it.initializer == null && it.source != null }
if (enumEntries.isEmpty()) return
val enumScope = declaration.unsubstitutedScope(context)
val notImplemented = mutableListOf<FirCallableSymbol<*>>()
enumScope.processAllCallables { symbol ->
if (symbol.isAbstract) {
notImplemented.add(symbol)
}
}
if (notImplemented.isEmpty()) return
for (enumEntry in enumEntries) {
reporter.reportOn(enumEntry.source, ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY, enumEntry.symbol, notImplemented, context)
}
}
}
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_DELEGATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_FUNCTION_WITH_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY_WITH_GETTER
@@ -1419,6 +1420,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
RENDER_CLASS_OR_OBJECT,
DECLARATION_NAME
)
map.put(
ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY,
"''{0}'' does not implement abstract members: {1}.",
SYMBOL,
SYMBOLS,
)
map.put(
ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED,
"{0} is not abstract and does not implement abstract base class member {1}",
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.scopes
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.name.Name
@@ -27,6 +28,13 @@ fun FirContainingNamesAwareScope.processAllProperties(processor: (FirVariableSym
}
}
fun FirContainingNamesAwareScope.processAllCallables(processor: (FirCallableSymbol<*>) -> Unit) {
for (name in getCallableNames()) {
processFunctionsByName(name, processor)
processPropertiesByName(name, processor)
}
}
fun FirContainingNamesAwareScope.collectAllProperties(): Collection<FirVariableSymbol<*>> {
return mutableListOf<FirVariableSymbol<*>>().apply {
processAllProperties(this::add)
@@ -2,7 +2,7 @@ package abstract
enum class MyEnum() {
INSTANCE;
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY!>INSTANCE<!>;
//properties
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>val a: Int<!>
val a1: Int = 1
@@ -6,7 +6,7 @@ enum class EnumImplementingTraitWithFun: T1 {
E1 {
override fun foo() {}
},
E2
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY!>E2<!>
}
interface T2 {
@@ -17,5 +17,5 @@ enum class EnumImplementingTraitWithVal: T2 {
E1 {
override val bar = 1
},
E2
}
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY!>E2<!>
}