[FIR] Add RedundantModalityModifierChecker

This commit is contained in:
vladislavf7@gmail.com
2020-07-14 16:07:51 +03:00
committed by Mikhail Glukhikh
parent e7c88a4349
commit fb5fb44993
7 changed files with 250 additions and 1 deletions
@@ -0,0 +1,60 @@
// Abstract
abstract class Base {
// Redundant final
<!REDUNDANT_MODALITY_MODIFIER!>final<!> fun foo() {}
// Abstract
abstract fun bar()
// Open
open val gav = 42
}
class FinalDerived : Base() {
// Redundant final
override <!REDUNDANT_MODALITY_MODIFIER!>final<!> fun bar() {}
// Non-final member in final class
override open val gav = 13
}
// Open
open class OpenDerived : Base() {
// Final
override final fun bar() {}
// Redundant open
override <!REDUNDANT_MODALITY_MODIFIER!>open<!> val gav = 13
}
// Redundant final
<!REDUNDANT_MODALITY_MODIFIER!>final<!> class Final
// Interface
interface Interface {
// Redundant
<!REDUNDANT_MODALITY_MODIFIER!>abstract<!> fun foo()
// Redundant
private <!REDUNDANT_MODALITY_MODIFIER!>final<!> fun bar() {}
// Redundant
<!REDUNDANT_MODALITY_MODIFIER!>open<!> val gav: Int
get() = 42
}
// Derived interface
interface Derived : Interface {
// Redundant
override <!REDUNDANT_MODALITY_MODIFIER!>open<!> fun foo() {}
// Redundant
<!REDUNDANT_MODALITY_MODIFIER!>final<!> class Nested
}
// Derived abstract class
abstract class AbstractDerived1(override final val gav: Int) : Interface {
// Redundant
override <!REDUNDANT_MODALITY_MODIFIER!>open<!> fun foo() {}
}
// Derived abstract class
abstract class AbstractDerived2 : Interface {
// Final
override final fun foo() {}
// Redundant
override <!REDUNDANT_MODALITY_MODIFIER!>open<!> val gav = 13
}
// Redundant abstract interface
abstract interface AbstractInterface
// Redundant final object
<!REDUNDANT_MODALITY_MODIFIER!>final<!> object FinalObject
// Open interface
open interface OpenInterface
@@ -0,0 +1,103 @@
FILE: RedundantModalityModifierChecker.kt
public abstract class Base : R|kotlin/Any| {
public constructor(): R|Base| {
super<R|kotlin/Any|>()
}
public final fun foo(): R|kotlin/Unit| {
}
public abstract fun bar(): R|kotlin/Unit|
public open val gav: R|kotlin/Int| = Int(42)
public get(): R|kotlin/Int|
}
public final class FinalDerived : R|Base| {
public constructor(): R|FinalDerived| {
super<R|Base|>()
}
public final override fun bar(): R|kotlin/Unit| {
}
public open override val gav: R|kotlin/Int| = Int(13)
public get(): R|kotlin/Int|
}
public open class OpenDerived : R|Base| {
public constructor(): R|OpenDerived| {
super<R|Base|>()
}
public final override fun bar(): R|kotlin/Unit| {
}
public open override val gav: R|kotlin/Int| = Int(13)
public get(): R|kotlin/Int|
}
public final class Final : R|kotlin/Any| {
public constructor(): R|Final| {
super<R|kotlin/Any|>()
}
}
public abstract interface Interface : R|kotlin/Any| {
public abstract fun foo(): R|kotlin/Unit|
private final fun bar(): R|kotlin/Unit| {
}
public open val gav: R|kotlin/Int|
public get(): R|kotlin/Int| {
^ Int(42)
}
}
public abstract interface Derived : R|Interface| {
public open override fun foo(): R|kotlin/Unit| {
}
public final class Nested : R|kotlin/Any| {
public constructor(): R|Derived.Nested| {
super<R|kotlin/Any|>()
}
}
}
public abstract class AbstractDerived1 : R|Interface| {
public constructor(gav: R|kotlin/Int|): R|AbstractDerived1| {
super<R|kotlin/Any|>()
}
public final override val gav: R|kotlin/Int| = R|<local>/gav|
public get(): R|kotlin/Int|
public open override fun foo(): R|kotlin/Unit| {
}
}
public abstract class AbstractDerived2 : R|Interface| {
public constructor(): R|AbstractDerived2| {
super<R|kotlin/Any|>()
}
public final override fun foo(): R|kotlin/Unit| {
}
public open override val gav: R|kotlin/Int| = Int(13)
public get(): R|kotlin/Int|
}
public abstract interface AbstractInterface : R|kotlin/Any| {
}
public final object FinalObject : R|kotlin/Any| {
private constructor(): R|FinalObject| {
super<R|kotlin/Any|>()
}
}
public open interface OpenInterface : R|kotlin/Any| {
}
@@ -28,6 +28,11 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("RedundantModalityModifierChecker.kt")
public void testRedundantModalityModifierChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantModalityModifierChecker.kt");
}
@TestMetadata("RedundantReturnUnitTypeChecker.kt")
public void testRedundantReturnUnitTypeChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantReturnUnitTypeChecker.kt");
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
@@ -26,6 +27,8 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.psiUtil.toVisibility
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
@@ -230,3 +233,42 @@ fun FirClass<*>.modality(): Modality? {
else -> Modality.FINAL
}
}
/**
* returns implicit modality by FirMemberDeclaration
*/
fun FirMemberDeclaration.implicitModality(context: CheckerContext): KtModifierKeywordToken {
if (this is FirRegularClass && (this.classKind == ClassKind.CLASS || this.classKind == ClassKind.OBJECT)) {
if (this.classKind == ClassKind.INTERFACE) return KtTokens.ABSTRACT_KEYWORD
return KtTokens.FINAL_KEYWORD
}
val klass = context.findClosestClassOrObject() ?: return KtTokens.FINAL_KEYWORD
val modifiers = this.modifierListOrNull() ?: return KtTokens.FINAL_KEYWORD
if (modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val klassModifiers = klass.modifierListOrNull()
if (klassModifiers != null && klassModifiers.run {
hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.SEALED_KEYWORD)
}) {
return KtTokens.OPEN_KEYWORD
}
}
if (
klass is FirRegularClass
&& klass.classKind == ClassKind.INTERFACE
&& !modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD)
) {
return if (this.hasBody()) KtTokens.OPEN_KEYWORD else KtTokens.ABSTRACT_KEYWORD
}
return KtTokens.FINAL_KEYWORD
}
private fun FirDeclaration.modifierListOrNull() = (this.source.getModifierList() as? FirPsiModifierList)?.modifierList
private fun FirDeclaration.hasBody(): Boolean = when (this) {
is FirSimpleFunction -> this.body != null && this.body !is FirSingleExpressionBlock
is FirProperty -> this.setter != null || this.getter != null
else -> false
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantModalityModifierChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantReturnUnitType
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantVisibilityModifierChecker
@@ -18,7 +19,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val memberDeclarationCheckers: List<FirMemberDeclarationChecker> = listOf(
FirInfixFunctionDeclarationChecker,
FirExposedVisibilityChecker
FirExposedVisibilityChecker,
RedundantModalityModifierChecker
)
override val constructorCheckers: List<FirConstructorChecker> = listOf(
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.extended
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.implicitModality
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
override val isExtended = true
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.source is FirFakeSourceElement<*>) return
val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return
val modality = (modalityModifier as? LeafPsiElement)?.elementType as? KtModifierKeywordToken ?: return
val implicitModality = declaration.implicitModality(context)
if (modality != implicitModality) return
reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER)
}
}
@@ -84,6 +84,7 @@ object FirErrors {
// Extended checkers group
val REDUNDANT_VISIBILITY_MODIFIER by warning0<FirSourceElement, PsiElement>()
val REDUNDANT_MODALITY_MODIFIER by warning0<FirSourceElement, PsiElement>()
val REDUNDANT_RETURN_UNIT_TYPE by warning0<FirSourceElement, PsiTypeElement>()
}