[FIR] KT-54507 Report REDUNDANT_MODALITY_MODIFIER for open interface

- `REDUNDANT_MODIFIER_FOR_TARGET` is already reported for `open`
  interfaces, but for consistency and IDE support, the compiler now
  reports `REDUNDANT_MODALITY_MODIFIER` as well via the extended
  checker.
- `REDUNDANT_MODIFIER_FOR_TARGET` cannot be disabled for this case
  because it's reported via a basic checker while
  `REDUNDANT_MODALITY_MODIFIER` is reported via an extended checker.
- Rename `implicitModality` to `redundantModalities` and return a set of
  modalities. The idea of a *single* implicit/redundant modality doesn't
  stand up to scrutiny. For example, for interfaces with `ABSTRACT`
  implicit modality, `OPEN` is also a redundant modifier. But this is
  not necessarily the case for all kinds of declarations.
  Hypothetically, if it was possible to declare a class that is abstract
  by default, adding an `open` modifier to that class would not be
  redundant, as it would make the class instantiable.
This commit is contained in:
Marco Pennekamp
2023-02-06 19:12:53 +01:00
committed by Space Team
parent 76ca96d2e2
commit 5909b191a5
4 changed files with 20 additions and 16 deletions
@@ -18,6 +18,8 @@ FILE: RedundantModalityModifierChecker.kt
private final fun bar(): R|kotlin/Unit|
public abstract fun loo(): R|kotlin/Unit|
public open fun goo(): R|kotlin/Unit| {
}
@@ -12,6 +12,7 @@ interface Interface {
// error
<!PRIVATE_FUNCTION_WITH_NO_BODY!>private<!> <!WRONG_MODIFIER_CONTAINING_DECLARATION!>final<!> fun bar()
<!REDUNDANT_MODALITY_MODIFIER, REDUNDANT_OPEN_IN_INTERFACE!>open<!> fun loo()
<!REDUNDANT_MODALITY_MODIFIER!>open<!> fun goo() {}
<!REDUNDANT_MODALITY_MODIFIER!>abstract<!> fun tar()
@@ -84,7 +85,7 @@ abstract class AbstractDerived2 : Interface {
// Redundant final object
<!REDUNDANT_MODALITY_MODIFIER!>final<!> object FinalObject
// Open interface
<!REDUNDANT_MODIFIER_FOR_TARGET!>open<!> interface OpenInterface
<!REDUNDANT_MODALITY_MODIFIER, REDUNDANT_MODIFIER_FOR_TARGET!>open<!> interface OpenInterface
class FinalDerived2(override <!REDUNDANT_MODALITY_MODIFIER!>final<!> val gav: Int) : Base() {
override fun bar() {}
@@ -221,25 +221,26 @@ fun FirClass.modality(): Modality? {
}
/**
* returns implicit modality by FirMemberDeclaration<*>
* Returns a set of [Modality] modifiers which are redundant for the given [FirMemberDeclaration]. If a modality modifier is redundant, the
* declaration's modality won't be changed by the modifier.
*/
fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
fun FirMemberDeclaration.redundantModalities(context: CheckerContext): Set<Modality> {
if (this is FirRegularClass) {
return when (classKind) {
ClassKind.INTERFACE -> Modality.ABSTRACT
else -> Modality.FINAL
ClassKind.INTERFACE -> setOf(Modality.ABSTRACT, Modality.OPEN)
else -> setOf(Modality.FINAL)
}
}
val containingClass = context.findClosestClassOrObject() ?: return Modality.FINAL
val containingClass = context.findClosestClassOrObject() ?: return setOf(Modality.FINAL)
return when {
isOverride && !containingClass.isFinal -> Modality.OPEN
isOverride && !containingClass.isFinal -> setOf(Modality.OPEN)
containingClass.isInterface -> when {
hasBody() -> Modality.OPEN
else -> Modality.ABSTRACT
hasBody() -> setOf(Modality.OPEN)
else -> setOf(Modality.ABSTRACT, Modality.OPEN)
}
else -> Modality.FINAL
else -> setOf(Modality.FINAL)
}
}
@@ -10,13 +10,13 @@ import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.implicitModality
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.diagnostics.modalityModifier
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.redundantModalities
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
@@ -46,8 +46,8 @@ object RedundantModalityModifierSyntaxChecker : FirDeclarationSyntaxChecker<FirD
) return
if (source.treeStructure.modalityModifier(source.lighterASTNode) == null) return
val implicitModality = element.implicitModality(context)
if (modality == implicitModality) {
val redundantModalities = element.redundantModalities(context)
if (redundantModalities.contains(modality)) {
reporter.reportOn(source, REDUNDANT_MODALITY_MODIFIER, context)
}
}