[FIR] Implement deprecation for implementing var by inherited val

#KT-56779 Fixed
This commit is contained in:
Kirill Rakhman
2023-05-12 12:58:04 +02:00
committed by Space Team
parent 941446ea39
commit df03f0df0a
19 changed files with 303 additions and 19 deletions
@@ -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")
@@ -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")
@@ -908,6 +908,14 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<FirCallableSymbol<*>>("overridingDeclaration")
parameter<FirCallableSymbol<*>>("overriddenDeclaration")
}
val VAR_IMPLEMENTED_BY_INHERITED_VAL by deprecationError<KtNamedDeclaration>(
LanguageFeature.ProhibitImplementingVarByInheritedVal,
PositioningStrategy.DECLARATION_NAME,
) {
parameter<FirClassSymbol<*>>("classOrObject")
parameter<FirCallableSymbol<*>>("overridingDeclaration")
parameter<FirCallableSymbol<*>>("overriddenDeclaration")
}
val NON_FINAL_MEMBER_IN_FINAL_CLASS by warning<KtNamedDeclaration>(PositioningStrategy.OPEN_MODIFIER)
val NON_FINAL_MEMBER_IN_OBJECT by warning<KtNamedDeclaration>(PositioningStrategy.OPEN_MODIFIER)
val VIRTUAL_MEMBER_HIDDEN by error<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME) {
@@ -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<KtNamedDeclaration, FirCallableSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
val VAR_TYPE_MISMATCH_ON_OVERRIDE by error2<KtNamedDeclaration, FirCallableSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
val VAR_OVERRIDDEN_BY_VAL by error2<KtNamedDeclaration, FirCallableSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE)
val VAR_IMPLEMENTED_BY_INHERITED_VAL by deprecationError3<KtNamedDeclaration, FirClassSymbol<*>, FirCallableSymbol<*>, FirCallableSymbol<*>>(ProhibitImplementingVarByInheritedVal, SourceElementPositioningStrategies.DECLARATION_NAME)
val NON_FINAL_MEMBER_IN_FINAL_CLASS by warning0<KtNamedDeclaration>(SourceElementPositioningStrategies.OPEN_MODIFIER)
val NON_FINAL_MEMBER_IN_OBJECT by warning0<KtNamedDeclaration>(SourceElementPositioningStrategies.OPEN_MODIFIER)
val VIRTUAL_MEMBER_HIDDEN by error2<KtNamedDeclaration, FirCallableSymbol<*>, FirRegularClassSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME)
@@ -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
@@ -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<Pair<FirCallableSymbol<*>, FirCallableSymbol<*>>>()
val delegationOverrideOfOpen = mutableListOf<Pair<FirCallableSymbol<*>, FirCallableSymbol<*>>>()
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
val varsImplementedByInheritedVal = mutableListOf<FirIntersectionCallableSymbol>()
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()
@@ -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(
@@ -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"
}
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class C1<!> : IVar, IVarDefault
<!VAR_IMPLEMENTED_BY_INHERITED_VAL_WARNING!>class C2<!> : CVal(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C3<!> : CVal(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C4<!> : CVal(), IVar, IVarDefault
class C5 : CVar(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C6<!> : CVar(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C7<!> : CVar(), IVar, IVarDefault
<!VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class C8<!>(ival: IVal) : IVar, IVal by ival
@@ -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"
}
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class C1<!> : IVar, IVarDefault
class C2 : CVal(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C3<!> : CVal(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C4<!> : CVal(), IVar, IVarDefault
class C5 : CVar(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C6<!> : CVar(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C7<!> : CVar(), IVar, IVarDefault
<!VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class C8<!>(ival: IVal) : IVar, IVal by ival
@@ -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"
}
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class C1<!> : IVar, IVarDefault
<!VAR_IMPLEMENTED_BY_INHERITED_VAL_ERROR!>class C2<!> : CVal(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C3<!> : CVal(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C4<!> : CVal(), IVar, IVarDefault
class C5 : CVar(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C6<!> : CVar(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C7<!> : CVar(), IVar, IVarDefault
<!VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class C8<!>(ival: IVal) : IVar, IVal by ival
@@ -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"
}
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class C1<!> : IVar, IVarDefault
class C2 : CVal(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C3<!> : CVal(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C4<!> : CVal(), IVar, IVarDefault
class C5 : CVar(), IVar
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C6<!> : CVar(), IVarDefault
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C7<!> : CVar(), IVar, IVarDefault
<!VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class C8<!>(ival: IVal) : IVar, IVal by ival
@@ -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")
@@ -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 --------------------------------------------------