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
|
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.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||||
@@ -80,6 +82,20 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
|||||||
return substitutorByMap(map).substituteOrSelf(this)
|
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(
|
private fun FirCallableMemberDeclaration<*>.checkReturnType(
|
||||||
overriddenSymbols: List<FirCallableSymbol<*>>,
|
overriddenSymbols: List<FirCallableSymbol<*>>,
|
||||||
typeCheckerContext: AbstractTypeCheckerContext,
|
typeCheckerContext: AbstractTypeCheckerContext,
|
||||||
@@ -121,9 +137,14 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
|||||||
val overriddenFunctionSymbols = firTypeScope.retrieveDirectOverriddenOf(function)
|
val overriddenFunctionSymbols = firTypeScope.retrieveDirectOverriddenOf(function)
|
||||||
|
|
||||||
if (overriddenFunctionSymbols.isEmpty()) {
|
if (overriddenFunctionSymbols.isEmpty()) {
|
||||||
|
reporter.reportNothingToOverride(function)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkModality(overriddenFunctionSymbols)?.let {
|
||||||
|
reporter.reportOverridingFinalMember(function, it)
|
||||||
|
}
|
||||||
|
|
||||||
val restriction = function.checkReturnType(
|
val restriction = function.checkReturnType(
|
||||||
overriddenSymbols = overriddenFunctionSymbols,
|
overriddenSymbols = overriddenFunctionSymbols,
|
||||||
typeCheckerContext = typeCheckerContext,
|
typeCheckerContext = typeCheckerContext,
|
||||||
@@ -149,9 +170,14 @@ object FirOverrideChecker : FirRegularClassChecker() {
|
|||||||
val overriddenPropertySymbols = firTypeScope.retrieveDirectOverriddenOf(property)
|
val overriddenPropertySymbols = firTypeScope.retrieveDirectOverriddenOf(property)
|
||||||
|
|
||||||
if (overriddenPropertySymbols.isEmpty()) {
|
if (overriddenPropertySymbols.isEmpty()) {
|
||||||
|
reporter.reportNothingToOverride(property)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkModality(overriddenPropertySymbols)?.let {
|
||||||
|
reporter.reportOverridingFinalMember(property, it)
|
||||||
|
}
|
||||||
|
|
||||||
val restriction = property.checkReturnType(
|
val restriction = property.checkReturnType(
|
||||||
overriddenSymbols = overriddenPropertySymbols,
|
overriddenSymbols = overriddenPropertySymbols,
|
||||||
typeCheckerContext = typeCheckerContext,
|
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(
|
private fun DiagnosticReporter.reportReturnTypeMismatchOnFunction(
|
||||||
overriding: FirMemberDeclaration,
|
overriding: FirMemberDeclaration,
|
||||||
overridden: 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.AMBIGUOUS_CALLS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
|
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.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.NULLABLE_STRING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.PROPERTY_NAME
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.PROPERTY_NAME
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
|
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_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_ENUM
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_PRIVATE_CONSTRUCTOR_IN_SEALED
|
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_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_LOOP_LABEL
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
|
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.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.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
|
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_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.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_FUNCTION_WITH_NO_BODY
|
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")
|
map.put(TYPE_PARAMETER_IN_CATCH_CLAUSE, "Type parameter is forbidden for catch parameter")
|
||||||
|
|
||||||
// Overrides
|
// 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(
|
map.put(
|
||||||
RETURN_TYPE_MISMATCH_ON_OVERRIDE,
|
RETURN_TYPE_MISMATCH_ON_OVERRIDE,
|
||||||
"Return type of ''{0}'' is not a subtype of the return type of the overridden member ''{1}''",
|
"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()
|
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 DECLARATION_NAME = Renderer { declaration: FirMemberDeclaration ->
|
||||||
val name = when (declaration) {
|
val name = when (declaration) {
|
||||||
is FirProperty -> declaration.name
|
is FirProperty -> declaration.name
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiTypeElement
|
|||||||
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
||||||
import org.jetbrains.kotlin.fir.FirEffectiveVisibility
|
import org.jetbrains.kotlin.fir.FirEffectiveVisibility
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
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.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
@@ -137,6 +138,9 @@ object FirErrors {
|
|||||||
val TYPE_PARAMETER_IN_CATCH_CLAUSE by error0<FirSourceElement, PsiElement>()
|
val TYPE_PARAMETER_IN_CATCH_CLAUSE by error0<FirSourceElement, PsiElement>()
|
||||||
|
|
||||||
// Overrides
|
// 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 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 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)
|
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<!> {
|
interface MyTrait: <!INTERFACE_WITH_SUPERCLASS!>Object<!> {
|
||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
public override fun finalize()
|
public override fun finalize()
|
||||||
public override fun wait()
|
public <!OVERRIDING_FINAL_MEMBER!>override<!> fun wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// !WITH_NEW_INFERENCE
|
// !WITH_NEW_INFERENCE
|
||||||
enum class E : Cloneable {
|
enum class E : Cloneable {
|
||||||
A;
|
A;
|
||||||
override fun clone(): Any {
|
<!OVERRIDING_FINAL_MEMBER!>override<!> fun clone(): Any {
|
||||||
return super.<!UNRESOLVED_REFERENCE!>clone<!>()
|
return super.<!UNRESOLVED_REFERENCE!>clone<!>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ enum class E {
|
|||||||
override val name: String = "lol"
|
override val name: String = "lol"
|
||||||
override val ordinal: Int = 0
|
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
|
<!OVERRIDING_FINAL_MEMBER!>override<!> fun equals(other: Any?) = true
|
||||||
override fun hashCode() = -1
|
<!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 {
|
open class A {
|
||||||
final fun foo() {}
|
final fun foo() {}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ class Foo {
|
|||||||
|
|
||||||
class Bar : Foo() {
|
class Bar : Foo() {
|
||||||
override fun openFoo() {}
|
override fun openFoo() {}
|
||||||
override fun finalFoo() {}
|
<!OVERRIDING_FINAL_MEMBER!>override<!> fun finalFoo() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user