From 8091aebbc71d58849c09f06b37d0b31568c5fdef Mon Sep 17 00:00:00 2001 From: Brian Norman Date: Mon, 22 May 2023 10:50:34 -0500 Subject: [PATCH] [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 --- .../diagnostics/KtFirDataClassConverters.kt | 10 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 6 +++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 +++ .../diagnostics/FirDiagnosticsList.kt | 4 ++ .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/CommonDeclarationCheckers.kt | 1 + ...plementedOverrideSimpleEnumEntryChecker.kt | 43 +++++++++++++++++++ .../diagnostics/FirErrorsDefaultMessages.kt | 7 +++ .../scopes/FirContainingNamesAwareScope.kt | 8 ++++ .../tests/enum/AbstractInEnum.fir.kt | 2 +- .../tests/enum/enumImplementingTrait.fir.kt | 6 +-- 11 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideSimpleEnumEntryChecker.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 1fa1b252d5e..8a7a5816d2d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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), 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 5bb244e650f..057526ccc62 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 @@ -1758,6 +1758,12 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { val missingDeclaration: KtCallableSymbol } + interface AbstractMemberNotImplementedByEnumEntry : KtFirDiagnostic { + override val diagnosticClass get() = AbstractMemberNotImplementedByEnumEntry::class + val enumEntry: KtSymbol + val missingDeclarations: List + } + interface AbstractClassMemberNotImplemented : KtFirDiagnostic { override val diagnosticClass get() = AbstractClassMemberNotImplemented::class val classOrObject: KtClassLikeSymbol 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 98e9b0b1404..fc9a4a69e38 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 @@ -2110,6 +2110,13 @@ internal class AbstractMemberNotImplementedImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.AbstractMemberNotImplemented +internal class AbstractMemberNotImplementedByEnumEntryImpl( + override val enumEntry: KtSymbol, + override val missingDeclarations: List, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.AbstractMemberNotImplementedByEnumEntry + internal class AbstractClassMemberNotImplementedImpl( override val classOrObject: KtClassLikeSymbol, override val missingDeclaration: KtCallableSymbol, 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 b7266b7f716..ef094fd83fc 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 @@ -861,6 +861,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter>("classOrObject") parameter>("missingDeclaration") } + val ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY by error(PositioningStrategy.DECLARATION_NAME) { + parameter("enumEntry") + parameter>>("missingDeclarations") + } val ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED by error(PositioningStrategy.DECLARATION_NAME) { parameter>("classOrObject") parameter>("missingDeclaration") 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 7c22b7e0f58..fa0aca7e5df 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 @@ -471,6 +471,7 @@ object FirErrors { val VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) val CONFLICTING_INHERITED_MEMBERS by error2, List>>(SourceElementPositioningStrategies.DECLARATION_NAME) val ABSTRACT_MEMBER_NOT_IMPLEMENTED by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) + val ABSTRACT_MEMBER_NOT_IMPLEMENTED_BY_ENUM_ENTRY by error2>>(SourceElementPositioningStrategies.DECLARATION_NAME) val ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) val INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER by deprecationError2, FirCallableSymbol<*>>(ProhibitInvisibleAbstractMethodsInSuperclasses, SourceElementPositioningStrategies.DECLARATION_NAME) val AMBIGUOUS_ANONYMOUS_TYPE_INFERRED by error1>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index 9d55889320e..40b9da1cdfc 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -92,6 +92,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { get() = setOf( FirOverrideChecker, FirNotImplementedOverrideChecker, + FirNotImplementedOverrideSimpleEnumEntryChecker, FirThrowableSubclassChecker, FirOpenMemberChecker, FirClassVarianceChecker, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideSimpleEnumEntryChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideSimpleEnumEntryChecker.kt new file mode 100644 index 00000000000..ae1335fb503 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideSimpleEnumEntryChecker.kt @@ -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().filter { it.initializer == null && it.source != null } + if (enumEntries.isEmpty()) return + + val enumScope = declaration.unsubstitutedScope(context) + + val notImplemented = mutableListOf>() + 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) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index ac8e6460946..61cae6c7bde 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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}", diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt index 2dd52cf8325..33f0ec41dd1 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirContainingNamesAwareScope.kt @@ -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> { return mutableListOf>().apply { processAllProperties(this::add) diff --git a/compiler/testData/diagnostics/tests/enum/AbstractInEnum.fir.kt b/compiler/testData/diagnostics/tests/enum/AbstractInEnum.fir.kt index 732b4eec884..16ca7eab842 100644 --- a/compiler/testData/diagnostics/tests/enum/AbstractInEnum.fir.kt +++ b/compiler/testData/diagnostics/tests/enum/AbstractInEnum.fir.kt @@ -2,7 +2,7 @@ package abstract enum class MyEnum() { - INSTANCE; + INSTANCE; //properties val a: Int val a1: Int = 1 diff --git a/compiler/testData/diagnostics/tests/enum/enumImplementingTrait.fir.kt b/compiler/testData/diagnostics/tests/enum/enumImplementingTrait.fir.kt index 3faa491a53d..4c2d4626b99 100644 --- a/compiler/testData/diagnostics/tests/enum/enumImplementingTrait.fir.kt +++ b/compiler/testData/diagnostics/tests/enum/enumImplementingTrait.fir.kt @@ -6,7 +6,7 @@ enum class EnumImplementingTraitWithFun: T1 { E1 { override fun foo() {} }, - E2 + E2 } interface T2 { @@ -17,5 +17,5 @@ enum class EnumImplementingTraitWithVal: T2 { E1 { override val bar = 1 }, - E2 -} \ No newline at end of file + E2 +}