From df03f0df0a61e7725b20d3910a541367c3fccf1f Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Fri, 12 May 2023 12:58:04 +0200 Subject: [PATCH] [FIR] Implement deprecation for implementing var by inherited val #KT-56779 Fixed --- .../diagnostics/KtFirDataClassConverters.kt | 18 +++++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 14 ++++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 16 +++++ ...CompilerTestFE10TestdataTestGenerated.java | 12 ++++ ...sticCompilerFE10TestDataTestGenerated.java | 12 ++++ ...eeOldFrontendDiagnosticsTestGenerated.java | 12 ++++ ...siOldFrontendDiagnosticsTestGenerated.java | 12 ++++ .../diagnostics/FirDiagnosticsList.kt | 8 +++ .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../fir/analysis/checkers/FirHelpers.kt | 65 ++++++++++++++----- .../FirNotImplementedOverrideChecker.kt | 15 ++++- .../diagnostics/FirErrorsDefaultMessages.kt | 8 +++ .../varImplementedByInheritedVal.fir.kt | 28 ++++++++ .../override/varImplementedByInheritedVal.kt | 28 ++++++++ .../varImplementedByInheritedValError.fir.kt | 28 ++++++++ .../varImplementedByInheritedValError.kt | 28 ++++++++ .../test/runners/DiagnosticTestGenerated.java | 12 ++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + .../kotlin/util/ImplementationStatus.kt | 3 + 19 files changed, 303 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.fir.kt create mode 100644 compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt create mode 100644 compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.fir.kt create mode 100644 compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.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 9484d7ff5cb..2f219c4fb69 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 @@ -2571,6 +2571,24 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.VAR_IMPLEMENTED_BY_INHERITED_VAL.errorFactory) { firDiagnostic -> + VarImplementedByInheritedValErrorImpl( + firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a), + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.b), + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.c), + firDiagnostic as KtPsiDiagnostic, + token, + ) + } + add(FirErrors.VAR_IMPLEMENTED_BY_INHERITED_VAL.warningFactory) { firDiagnostic -> + VarImplementedByInheritedValWarningImpl( + firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a), + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.b), + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.c), + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.NON_FINAL_MEMBER_IN_FINAL_CLASS) { firDiagnostic -> NonFinalMemberInFinalClassImpl( firDiagnostic as KtPsiDiagnostic, 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 e43c5edf05e..2cdf0a41ab6 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 @@ -1829,6 +1829,20 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val overriddenDeclaration: KtCallableSymbol } + abstract class VarImplementedByInheritedValError : KtFirDiagnostic() { + override val diagnosticClass get() = VarImplementedByInheritedValError::class + abstract val classOrObject: KtClassLikeSymbol + abstract val overridingDeclaration: KtCallableSymbol + abstract val overriddenDeclaration: KtCallableSymbol + } + + abstract class VarImplementedByInheritedValWarning : KtFirDiagnostic() { + override val diagnosticClass get() = VarImplementedByInheritedValWarning::class + abstract val classOrObject: KtClassLikeSymbol + abstract val overridingDeclaration: KtCallableSymbol + abstract val overriddenDeclaration: KtCallableSymbol + } + abstract class NonFinalMemberInFinalClass : KtFirDiagnostic() { override val diagnosticClass get() = NonFinalMemberInFinalClass::class } 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 5982625b905..fd8a2e59e9f 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 @@ -2193,6 +2193,22 @@ internal class VarOverriddenByValImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.VarOverriddenByVal(), KtAbstractFirDiagnostic +internal class VarImplementedByInheritedValErrorImpl( + override val classOrObject: KtClassLikeSymbol, + override val overridingDeclaration: KtCallableSymbol, + override val overriddenDeclaration: KtCallableSymbol, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.VarImplementedByInheritedValError(), KtAbstractFirDiagnostic + +internal class VarImplementedByInheritedValWarningImpl( + override val classOrObject: KtClassLikeSymbol, + override val overridingDeclaration: KtCallableSymbol, + override val overriddenDeclaration: KtCallableSymbol, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.VarImplementedByInheritedValWarning(), KtAbstractFirDiagnostic + internal class NonFinalMemberInFinalClassImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 3bc6186f8fe..482c5914457 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -23756,6 +23756,18 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/override/ToAbstractMembersFromSuper-kt1996.kt"); } + @Test + @TestMetadata("varImplementedByInheritedVal.kt") + public void testVarImplementedByInheritedVal() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt"); + } + + @Test + @TestMetadata("varImplementedByInheritedValError.kt") + public void testVarImplementedByInheritedValError() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance") @TestDataPath("$PROJECT_ROOT") diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 40b4e414a01..c635cd6415b 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -23756,6 +23756,18 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/override/ToAbstractMembersFromSuper-kt1996.kt"); } + @Test + @TestMetadata("varImplementedByInheritedVal.kt") + public void testVarImplementedByInheritedVal() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt"); + } + + @Test + @TestMetadata("varImplementedByInheritedValError.kt") + public void testVarImplementedByInheritedValError() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 7909b23aeb2..9a099fd9d70 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -23756,6 +23756,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/override/ToAbstractMembersFromSuper-kt1996.kt"); } + @Test + @TestMetadata("varImplementedByInheritedVal.kt") + public void testVarImplementedByInheritedVal() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt"); + } + + @Test + @TestMetadata("varImplementedByInheritedValError.kt") + public void testVarImplementedByInheritedValError() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 995bd569463..d7db1da4d8a 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -23762,6 +23762,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/override/ToAbstractMembersFromSuper-kt1996.kt"); } + @Test + @TestMetadata("varImplementedByInheritedVal.kt") + public void testVarImplementedByInheritedVal() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt"); + } + + @Test + @TestMetadata("varImplementedByInheritedValError.kt") + public void testVarImplementedByInheritedValError() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance") @TestDataPath("$PROJECT_ROOT") 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 1858401c809..d7f0a92af60 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 @@ -908,6 +908,14 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter>("overridingDeclaration") parameter>("overriddenDeclaration") } + val VAR_IMPLEMENTED_BY_INHERITED_VAL by deprecationError( + LanguageFeature.ProhibitImplementingVarByInheritedVal, + PositioningStrategy.DECLARATION_NAME, + ) { + parameter>("classOrObject") + parameter>("overridingDeclaration") + parameter>("overriddenDeclaration") + } val NON_FINAL_MEMBER_IN_FINAL_CLASS by warning(PositioningStrategy.OPEN_MODIFIER) val NON_FINAL_MEMBER_IN_OBJECT by warning(PositioningStrategy.OPEN_MODIFIER) val VIRTUAL_MEMBER_HIDDEN by error(PositioningStrategy.DECLARATION_NAME) { 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 dac5b55dccc..68b1385fdef 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 @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.config.LanguageFeature.ModifierNonBuiltinSuspendFunE import org.jetbrains.kotlin.config.LanguageFeature.ProhibitAssigningSingleElementsToVarargsInNamedForm import org.jetbrains.kotlin.config.LanguageFeature.ProhibitConfusingSyntaxInWhenBranches import org.jetbrains.kotlin.config.LanguageFeature.ProhibitCyclesInAnnotations +import org.jetbrains.kotlin.config.LanguageFeature.ProhibitImplementingVarByInheritedVal import org.jetbrains.kotlin.config.LanguageFeature.ProhibitInvisibleAbstractMethodsInSuperclasses import org.jetbrains.kotlin.config.LanguageFeature.ProhibitNonReifiedArraysAsReifiedTypeArguments import org.jetbrains.kotlin.config.LanguageFeature.ProhibitUseSiteTargetAnnotationsOnSuperTypes @@ -481,6 +482,7 @@ object FirErrors { val PROPERTY_TYPE_MISMATCH_ON_OVERRIDE by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE) val VAR_TYPE_MISMATCH_ON_OVERRIDE by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE) val VAR_OVERRIDDEN_BY_VAL by error2, FirCallableSymbol<*>>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE) + val VAR_IMPLEMENTED_BY_INHERITED_VAL by deprecationError3, FirCallableSymbol<*>, FirCallableSymbol<*>>(ProhibitImplementingVarByInheritedVal, SourceElementPositioningStrategies.DECLARATION_NAME) val NON_FINAL_MEMBER_IN_FINAL_CLASS by warning0(SourceElementPositioningStrategies.OPEN_MODIFIER) val NON_FINAL_MEMBER_IN_OBJECT by warning0(SourceElementPositioningStrategies.OPEN_MODIFIER) val VIRTUAL_MEMBER_HIDDEN by error2, FirRegularClassSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index c815eaa5e6d..6e406d8e5ea 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -12,7 +12,9 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget -import org.jetbrains.kotlin.diagnostics.* +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.isExpression +import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol @@ -26,8 +28,11 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.scopes.* +import org.jetbrains.kotlin.fir.scopes.FirTypeScope +import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature +import org.jetbrains.kotlin.fir.scopes.overriddenFunctions +import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals @@ -325,23 +330,48 @@ fun FirCallableSymbol<*>.getImplementationStatus( if (containingClassSymbol === parentClassSymbol && symbol.subjectToManyNotImplemented(sessionHolder)) { return ImplementationStatus.AMBIGUOUSLY_INHERITED } + + var hasAbstractFromClass = false + var hasInterfaceDelegation = false + var hasAbstractVar = false + var hasImplementation = false + var hasImplementationVar = false + + for (intersection in symbol.intersections) { + @OptIn(SymbolInternals::class) + val fir = intersection.fir + val unwrappedFir = fir.unwrapFakeOverrides() + val isVar = unwrappedFir is FirProperty && unwrappedFir.isVar + val isFromClass = unwrappedFir.getContainingClassSymbol(sessionHolder.session)?.classKind == ClassKind.CLASS + + if (fir.isAbstract) { + if (isFromClass) { + hasAbstractFromClass = true + } + if (isVar) { + hasAbstractVar = true + } + } else { + if (fir.origin == FirDeclarationOrigin.Delegated) { + hasInterfaceDelegation = true + } + if (isFromClass) { + hasImplementation = true + if (isVar) { + hasImplementationVar = true + } + } + } + } + // In Java 8, non-abstract intersection overrides having abstract symbol from base class // still should be implemented in current class (even when they have default interface implementation) - if (symbol.intersections.any { - @OptIn(SymbolInternals::class) - val fir = it.fir.unwrapFakeOverrides() - fir.isAbstract && (fir.getContainingClassSymbol(sessionHolder.session) as? FirRegularClassSymbol)?.classKind == ClassKind.CLASS - } - ) { - // Exception from the rule above: interface implementation via delegation - if (symbol.intersections.none { - @OptIn(SymbolInternals::class) - val fir = it.fir - fir.origin == FirDeclarationOrigin.Delegated && !fir.isAbstract - } - ) { - return ImplementationStatus.NOT_IMPLEMENTED - } + // Exception to the rule above: interface implementation via delegation + if (hasAbstractFromClass && !hasInterfaceDelegation) { + return ImplementationStatus.NOT_IMPLEMENTED + } + if (hasAbstractVar && hasImplementation && !hasImplementationVar) { + return ImplementationStatus.VAR_IMPLEMENTED_BY_VAL } } @@ -372,7 +402,6 @@ fun FirCallableSymbol<*>.getImplementationStatus( } } - private fun FirIntersectionCallableSymbol.subjectToManyNotImplemented(sessionHolder: SessionHolder): Boolean { var nonAbstractCountInClass = 0 var nonAbstractCountInInterface = 0 diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt index 3df511135a1..f6c4ad44a7a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirNotImplementedOverrideChecker.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER_BY_DELEGATION import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_IMPLEMENTED_BY_INHERITED_VAL import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin @@ -54,6 +55,7 @@ object FirNotImplementedOverrideChecker : FirClassChecker() { val delegationOverrideOfFinal = mutableListOf, FirCallableSymbol<*>>>() val delegationOverrideOfOpen = mutableListOf, FirCallableSymbol<*>>>() val invisibleSymbols = mutableListOf>() + val varsImplementedByInheritedVal = mutableListOf() fun collectSymbol(symbol: FirCallableSymbol<*>) { val delegatedWrapperData = symbol.delegatedWrapperData @@ -99,7 +101,7 @@ object FirNotImplementedOverrideChecker : FirClassChecker() { symbol.isVisibleInClass(classSymbol) -> notImplementedSymbols.add(symbol) else -> invisibleSymbols.add(symbol) } - + ImplementationStatus.VAR_IMPLEMENTED_BY_VAL -> varsImplementedByInheritedVal.add(symbol as FirIntersectionCallableSymbol) else -> { // nothing to do } @@ -111,6 +113,17 @@ object FirNotImplementedOverrideChecker : FirClassChecker() { classScope.processPropertiesByName(name, ::collectSymbol) } + varsImplementedByInheritedVal.firstOrNull()?.let { symbol -> + val implementationVal = symbol.intersections.first { it is FirPropertySymbol && it.isVal && !it.isAbstract } + reporter.reportOn( + source, + VAR_IMPLEMENTED_BY_INHERITED_VAL, + classSymbol, + symbol as FirCallableSymbol<*>, + implementationVal, + context, + ) + } if (!canHaveAbstractDeclarations && notImplementedSymbols.isNotEmpty()) { val notImplemented = (notImplementedSymbols.firstOrNull { !it.isFromInterfaceOrEnum(context) } ?: notImplementedSymbols.first()) .unwrapFakeOverrides() 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 cb7e90f5d0e..d4a6e234116 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 @@ -606,6 +606,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_NEVER_RE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_WITH_NO_TYPE_NO_INITIALIZER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_ANNOTATION_PARAMETER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_IMPLEMENTED_BY_INHERITED_VAL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH_ON_INHERITANCE @@ -1461,6 +1462,13 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { FQ_NAMES_IN_TYPES, FQ_NAMES_IN_TYPES ) + map.put( + VAR_IMPLEMENTED_BY_INHERITED_VAL, + "{0} implements var-property {1} by inherited val-property {2}", + RENDER_CLASS_OR_OBJECT, + DECLARATION_NAME, + FQ_NAMES_IN_TYPES + ) map.put(NON_FINAL_MEMBER_IN_FINAL_CLASS, "'open' has no effect in a final class") map.put(NON_FINAL_MEMBER_IN_OBJECT, "'open' has no effect in an object") map.put( diff --git a/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.fir.kt b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.fir.kt new file mode 100644 index 00000000000..66ae9d7edb4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.fir.kt @@ -0,0 +1,28 @@ +// LANGUAGE: -ProhibitImplementingVarByInheritedVal + +interface IVal { + val a: String +} +interface IVar { + var a: String +} +interface IVarDefault { + var a: String + get() = "" + set(value) {} +} +open class CVal { + val a: String = "default" +} +open class CVar { + var a: String = "default" +} + +class C1 : IVar, IVarDefault +class C2 : CVal(), IVar +class C3 : CVal(), IVarDefault +class C4 : CVal(), IVar, IVarDefault +class C5 : CVar(), IVar +class C6 : CVar(), IVarDefault +class C7 : CVar(), IVar, IVarDefault +class C8(ival: IVal) : IVar, IVal by ival diff --git a/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt new file mode 100644 index 00000000000..ab09ec48df2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt @@ -0,0 +1,28 @@ +// LANGUAGE: -ProhibitImplementingVarByInheritedVal + +interface IVal { + val a: String +} +interface IVar { + var a: String +} +interface IVarDefault { + var a: String + get() = "" + set(value) {} +} +open class CVal { + val a: String = "default" +} +open class CVar { + var a: String = "default" +} + +class C1 : IVar, IVarDefault +class C2 : CVal(), IVar +class C3 : CVal(), IVarDefault +class C4 : CVal(), IVar, IVarDefault +class C5 : CVar(), IVar +class C6 : CVar(), IVarDefault +class C7 : CVar(), IVar, IVarDefault +class C8(ival: IVal) : IVar, IVal by ival diff --git a/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.fir.kt b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.fir.kt new file mode 100644 index 00000000000..e92b4c97aee --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.fir.kt @@ -0,0 +1,28 @@ +// LANGUAGE: +ProhibitImplementingVarByInheritedVal + +interface IVal { + val a: String +} +interface IVar { + var a: String +} +interface IVarDefault { + var a: String + get() = "" + set(value) {} +} +open class CVal { + val a: String = "default" +} +open class CVar { + var a: String = "default" +} + +class C1 : IVar, IVarDefault +class C2 : CVal(), IVar +class C3 : CVal(), IVarDefault +class C4 : CVal(), IVar, IVarDefault +class C5 : CVar(), IVar +class C6 : CVar(), IVarDefault +class C7 : CVar(), IVar, IVarDefault +class C8(ival: IVal) : IVar, IVal by ival diff --git a/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt new file mode 100644 index 00000000000..de1765fa171 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt @@ -0,0 +1,28 @@ +// LANGUAGE: +ProhibitImplementingVarByInheritedVal + +interface IVal { + val a: String +} +interface IVar { + var a: String +} +interface IVarDefault { + var a: String + get() = "" + set(value) {} +} +open class CVal { + val a: String = "default" +} +open class CVar { + var a: String = "default" +} + +class C1 : IVar, IVarDefault +class C2 : CVal(), IVar +class C3 : CVal(), IVarDefault +class C4 : CVal(), IVar, IVarDefault +class C5 : CVar(), IVar +class C6 : CVar(), IVarDefault +class C7 : CVar(), IVar, IVarDefault +class C8(ival: IVal) : IVar, IVal by ival diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 60e3e3231ea..2f00767df25 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -24534,6 +24534,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/override/ToAbstractMembersFromSuper-kt1996.kt"); } + @Test + @TestMetadata("varImplementedByInheritedVal.kt") + public void testVarImplementedByInheritedVal() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedVal.kt"); + } + + @Test + @TestMetadata("varImplementedByInheritedValError.kt") + public void testVarImplementedByInheritedValError() throws Exception { + runTest("compiler/testData/diagnostics/tests/override/varImplementedByInheritedValError.kt"); + } + @Nested @TestMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 6c2de2435ac..727decca5fc 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -292,6 +292,7 @@ enum class LanguageFeature( // 2.1 ReferencesToSyntheticJavaProperties(KOTLIN_2_1), // KT-8575 + ProhibitImplementingVarByInheritedVal(KOTLIN_2_1, kind = BUG_FIX), // KT-56779 // End of 2.* language features -------------------------------------------------- diff --git a/core/compiler.common/src/org/jetbrains/kotlin/util/ImplementationStatus.kt b/core/compiler.common/src/org/jetbrains/kotlin/util/ImplementationStatus.kt index 39b9e3d4569..02d8b554f33 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/util/ImplementationStatus.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/util/ImplementationStatus.kt @@ -10,6 +10,9 @@ enum class ImplementationStatus { /** This symbol is not implemented and should be implemented if the class is not abstract. */ NOT_IMPLEMENTED, + /** This symbol is a var property but only has a val property as implementation */ + VAR_IMPLEMENTED_BY_VAL, + /** The symbol is inheriting multiple non-abstract symbols and hence must be explicitly implemented. */ AMBIGUOUSLY_INHERITED,