FIR checker: add diagnostic OVERRIDING_FINAL_MEMBER
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
4ef1e1119f
commit
80d5a1a1db
+42
@@ -5,9 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
@@ -80,6 +82,20 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
||||
return substitutorByMap(map).substituteOrSelf(this)
|
||||
}
|
||||
|
||||
private fun checkModality(
|
||||
overriddenSymbols: List<FirCallableSymbol<*>>,
|
||||
): FirCallableDeclaration<*>? {
|
||||
for (overridden in overriddenSymbols) {
|
||||
if (overridden.fir !is FirMemberDeclaration) continue
|
||||
val modality = (overridden.fir as FirMemberDeclaration).status.modality
|
||||
val isEffectivelyFinal = modality == null || modality == Modality.FINAL
|
||||
if (isEffectivelyFinal) {
|
||||
return overridden.fir
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun FirCallableMemberDeclaration<*>.checkReturnType(
|
||||
overriddenSymbols: List<FirCallableSymbol<*>>,
|
||||
typeCheckerContext: AbstractTypeCheckerContext,
|
||||
@@ -121,9 +137,14 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
||||
val overriddenFunctionSymbols = firTypeScope.retrieveDirectOverriddenOf(function)
|
||||
|
||||
if (overriddenFunctionSymbols.isEmpty()) {
|
||||
reporter.reportNothingToOverride(function)
|
||||
return
|
||||
}
|
||||
|
||||
checkModality(overriddenFunctionSymbols)?.let {
|
||||
reporter.reportOverridingFinalMember(function, it)
|
||||
}
|
||||
|
||||
val restriction = function.checkReturnType(
|
||||
overriddenSymbols = overriddenFunctionSymbols,
|
||||
typeCheckerContext = typeCheckerContext,
|
||||
@@ -149,9 +170,14 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
||||
val overriddenPropertySymbols = firTypeScope.retrieveDirectOverriddenOf(property)
|
||||
|
||||
if (overriddenPropertySymbols.isEmpty()) {
|
||||
reporter.reportNothingToOverride(property)
|
||||
return
|
||||
}
|
||||
|
||||
checkModality(overriddenPropertySymbols)?.let {
|
||||
reporter.reportOverridingFinalMember(property, it)
|
||||
}
|
||||
|
||||
val restriction = property.checkReturnType(
|
||||
overriddenSymbols = overriddenPropertySymbols,
|
||||
typeCheckerContext = typeCheckerContext,
|
||||
@@ -167,6 +193,22 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.reportNothingToOverride(declaration: FirMemberDeclaration) {
|
||||
// TODO: not ready yet, e.g., Collections
|
||||
// declaration.source?.let { report(FirErrors.NOTHING_TO_OVERRIDE.on(it, declaration)) }
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.reportOverridingFinalMember(
|
||||
overriding: FirMemberDeclaration,
|
||||
overridden: FirCallableDeclaration<*>,
|
||||
) {
|
||||
overriding.source?.let { source ->
|
||||
overridden.containingClass()?.let { containingClass ->
|
||||
report(FirErrors.OVERRIDING_FINAL_MEMBER.on(source, overridden, containingClass.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.reportReturnTypeMismatchOnFunction(
|
||||
overriding: FirMemberDeclaration,
|
||||
overridden: FirMemberDeclaration
|
||||
|
||||
+6
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBIGUOUS_CALLS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FQ_NAMES_IN_TYPES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.PROPERTY_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
|
||||
@@ -90,6 +91,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNC
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_MEMBER_FUNCTION_NO_BODY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_ENUM
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_SEALED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_AN_ANNOTATION_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
|
||||
@@ -97,6 +99,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_FOR_TYPE_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_FUNCTION_WITH_NO_BODY
|
||||
@@ -328,6 +331,9 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
map.put(TYPE_PARAMETER_IN_CATCH_CLAUSE, "Type parameter is forbidden for catch parameter")
|
||||
|
||||
// Overrides
|
||||
map.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", DECLARATION_NAME)
|
||||
map.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, TO_STRING)
|
||||
|
||||
map.put(
|
||||
RETURN_TYPE_MISMATCH_ON_OVERRIDE,
|
||||
"Return type of ''{0}'' is not a subtype of the return type of the overridden member ''{1}''",
|
||||
|
||||
+8
@@ -47,6 +47,14 @@ object FirDiagnosticRenderers {
|
||||
element.render()
|
||||
}
|
||||
|
||||
val NAME = Renderer { element: FirElement ->
|
||||
when (element) {
|
||||
is FirMemberDeclaration -> DECLARATION_NAME.render(element)
|
||||
is FirCallableDeclaration<*> -> element.symbol.callableId.callableName.asString()
|
||||
else -> "???"
|
||||
}
|
||||
}
|
||||
|
||||
val DECLARATION_NAME = Renderer { declaration: FirMemberDeclaration ->
|
||||
val name = when (declaration) {
|
||||
is FirProperty -> declaration.name
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiTypeElement
|
||||
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
||||
import org.jetbrains.kotlin.fir.FirEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
@@ -137,6 +138,9 @@ object FirErrors {
|
||||
val TYPE_PARAMETER_IN_CATCH_CLAUSE by error0<FirSourceElement, PsiElement>()
|
||||
|
||||
// Overrides
|
||||
val NOTHING_TO_OVERRIDE by error1<FirSourceElement, KtModifierListOwner, FirMemberDeclaration>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
||||
val OVERRIDING_FINAL_MEMBER by error2<FirSourceElement, KtNamedDeclaration, FirCallableDeclaration<*>, Name>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
||||
|
||||
val RETURN_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, KtNamedDeclaration, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
|
||||
val PROPERTY_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, KtNamedDeclaration, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
|
||||
val VAR_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, KtNamedDeclaration, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.DECLARATION_RETURN_TYPE)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
interface MyTrait: <!INTERFACE_WITH_SUPERCLASS!>Object<!> {
|
||||
override fun toString(): String
|
||||
public override fun finalize()
|
||||
public override fun wait()
|
||||
public <!OVERRIDING_FINAL_MEMBER!>override<!> fun wait()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
enum class E : Cloneable {
|
||||
A;
|
||||
override fun clone(): Any {
|
||||
<!OVERRIDING_FINAL_MEMBER!>override<!> fun clone(): Any {
|
||||
return super.<!UNRESOLVED_REFERENCE!>clone<!>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ enum class E {
|
||||
override val name: String = "lol"
|
||||
override val ordinal: Int = 0
|
||||
|
||||
override fun compareTo(other: E) = -1
|
||||
<!OVERRIDING_FINAL_MEMBER!>override<!> fun compareTo(other: E) = -1
|
||||
|
||||
override fun equals(other: Any?) = true
|
||||
override fun hashCode() = -1
|
||||
<!OVERRIDING_FINAL_MEMBER!>override<!> fun equals(other: Any?) = true
|
||||
<!OVERRIDING_FINAL_MEMBER!>override<!> fun hashCode() = -1
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
open class A {
|
||||
final fun foo() {}
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override fun foo() {}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
open class A {
|
||||
final fun foo() {}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ class Foo {
|
||||
|
||||
class Bar : Foo() {
|
||||
override fun openFoo() {}
|
||||
override fun finalFoo() {}
|
||||
<!OVERRIDING_FINAL_MEMBER!>override<!> fun finalFoo() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user