[FIR] Introduce RedundantVisibilityModifierChecker
This commit is contained in:
committed by
Mikhail Glukhikh
parent
510ac66ce1
commit
4643d483a0
Vendored
+52
@@ -0,0 +1,52 @@
|
|||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> class C {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> val foo: Int = 0
|
||||||
|
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> fun bar() {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
open class D {
|
||||||
|
protected open fun willRemainProtected() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun willBecomePublic() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class E : D() {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>protected<!> override fun willRemainProtected() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun willBecomePublic() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class F <!REDUNDANT_VISIBILITY_MODIFIER!>private<!> constructor(val x: Int) {
|
||||||
|
FIRST(42)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class G constructor(val y: Int) {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>private<!> constructor(): this(42)
|
||||||
|
|
||||||
|
object H : G()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
fun bar()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f() {
|
||||||
|
val i = object : I {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
|
||||||
|
override fun bar() {}
|
||||||
|
}
|
||||||
|
i.foo = 1
|
||||||
|
|
||||||
|
class LocalClass {
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
|
||||||
|
}
|
||||||
|
LocalClass().foo = 1
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> var baz = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> var baz = 0
|
||||||
Generated
+18
@@ -1414,6 +1414,24 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/extendedCheckers")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ExtendedCheckers extends AbstractFirDiagnosticsTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInExtendedCheckers() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/extendedCheckers"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedundantVisibilityModifierChecker.kt")
|
||||||
|
public void testRedundantVisibilityModifierChecker() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/extendedCheckers/RedundantVisibilityModifierChecker.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/fromBuilder")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/fromBuilder")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+18
@@ -1414,6 +1414,24 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/extendedCheckers")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ExtendedCheckers extends AbstractFirDiagnosticsWithLightTreeTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInExtendedCheckers() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/extendedCheckers"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedundantVisibilityModifierChecker.kt")
|
||||||
|
public void testRedundantVisibilityModifierChecker() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/extendedCheckers/RedundantVisibilityModifierChecker.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/fromBuilder")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolve/fromBuilder")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
@@ -15,12 +16,19 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
|||||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||||
|
import org.jetbrains.kotlin.psi.KtModifierList
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.toVisibility
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,3 +193,40 @@ fun CheckerContext.findClosestClassOrObject(): FirClass<*>? {
|
|||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of functions that overridden by given
|
||||||
|
*/
|
||||||
|
fun FirSimpleFunction.overriddenFunctions(
|
||||||
|
containingClass: FirClass<*>,
|
||||||
|
context: CheckerContext
|
||||||
|
): List<FirFunctionSymbol<*>> {
|
||||||
|
val firTypeScope = containingClass.unsubstitutedScope(
|
||||||
|
context.sessionHolder.session,
|
||||||
|
context.sessionHolder.scopeSession
|
||||||
|
) as FirTypeScope
|
||||||
|
|
||||||
|
val overriddenFunctions = mutableListOf<FirFunctionSymbol<*>>()
|
||||||
|
firTypeScope.processFunctionsByName(symbol.fir.name) { }
|
||||||
|
firTypeScope.processOverriddenFunctions(symbol) {
|
||||||
|
overriddenFunctions.add(it)
|
||||||
|
ProcessorAction.NEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
return overriddenFunctions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the visibility by given KtModifierList
|
||||||
|
*/
|
||||||
|
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toVisibility()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the modality of the class
|
||||||
|
*/
|
||||||
|
fun FirClass<*>.modality(): Modality? {
|
||||||
|
return when (this) {
|
||||||
|
is FirRegularClass -> modality
|
||||||
|
else -> Modality.FINAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-1
@@ -5,10 +5,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantVisibilityModifierChecker
|
||||||
|
|
||||||
object CommonDeclarationCheckers : DeclarationCheckers() {
|
object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||||
override val declarationCheckers: List<FirBasicDeclarationChecker> = listOf(
|
override val declarationCheckers: List<FirBasicDeclarationChecker> = listOf(
|
||||||
FirAnnotationClassDeclarationChecker,
|
FirAnnotationClassDeclarationChecker,
|
||||||
FirModifierChecker
|
FirModifierChecker,
|
||||||
|
RedundantVisibilityModifierChecker
|
||||||
)
|
)
|
||||||
|
|
||||||
override val memberDeclarationCheckers: List<FirMemberDeclarationChecker> = listOf(
|
override val memberDeclarationCheckers: List<FirMemberDeclarationChecker> = listOf(
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
|
import org.jetbrains.kotlin.fir.FirFakeSourceElement
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtModifierList
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||||
|
|
||||||
|
object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
|
||||||
|
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
if (declaration is FirPropertyAccessor && declaration.isGetter) return
|
||||||
|
if (declaration !is FirMemberDeclaration) return
|
||||||
|
if (declaration is FirConstructor && declaration.source is FirFakeSourceElement<*>) return
|
||||||
|
|
||||||
|
val modifierList = (declaration.source.getModifierList() as? FirPsiModifierList)?.modifierList ?: return
|
||||||
|
val visibilityModifier = modifierList.getVisibility()
|
||||||
|
val implicitVisibility = declaration.implicitVisibility(context)
|
||||||
|
val containingMemberDeclaration = context.containingDeclarations.getOrNull(1) as? FirMemberDeclaration
|
||||||
|
|
||||||
|
val redundantVisibility = when {
|
||||||
|
visibilityModifier == implicitVisibility -> implicitVisibility
|
||||||
|
visibilityModifier == Visibilities.INTERNAL
|
||||||
|
&& containingMemberDeclaration?.visibility == Visibilities.PRIVATE
|
||||||
|
|| declaration.visibility == Visibilities.INTERNAL -> Visibilities.INTERNAL
|
||||||
|
else -> null
|
||||||
|
} ?: return
|
||||||
|
|
||||||
|
if (redundantVisibility == Visibilities.PUBLIC
|
||||||
|
&& declaration is FirProperty
|
||||||
|
&& declaration.isVar
|
||||||
|
&& declaration.setter?.visibility.let { it != null && it != Visibilities.PUBLIC }
|
||||||
|
) return
|
||||||
|
|
||||||
|
val source = modifierList.visibilityModifier()?.toFirPsiSourceElement()
|
||||||
|
reporter.report(source, FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirDeclaration.implicitVisibility(context: CheckerContext): Visibility {
|
||||||
|
return when {
|
||||||
|
this is FirPropertyAccessor && isSetter && status.isOverride -> {
|
||||||
|
this.visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
this is FirConstructor -> {
|
||||||
|
val clazz = this.getContainingClass(context)
|
||||||
|
if (
|
||||||
|
clazz is FirClass
|
||||||
|
&& (clazz.classKind == ClassKind.ENUM_CLASS || clazz.modality() == Modality.SEALED)
|
||||||
|
) {
|
||||||
|
return Visibilities.PRIVATE
|
||||||
|
} else {
|
||||||
|
Visibilities.DEFAULT_VISIBILITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this is FirSimpleFunction
|
||||||
|
&& context.containingDeclarations.getOrNull(1) is FirClass<*>
|
||||||
|
&& this.isOverride -> findFunctionVisibility(this, context)
|
||||||
|
|
||||||
|
else -> Visibilities.DEFAULT_VISIBILITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findFunctionVisibility(function: FirSimpleFunction, context: CheckerContext): Visibility {
|
||||||
|
val currentClass = context.findClosestClassOrObject() ?: return Visibilities.UNKNOWN
|
||||||
|
val overriddenFunctions = function.overriddenFunctions(currentClass, context)
|
||||||
|
var visibility: Visibility = Visibilities.PRIVATE
|
||||||
|
for (func in overriddenFunctions) {
|
||||||
|
val currentVisibility = func.fir.visibility()
|
||||||
|
if (currentVisibility != null) {
|
||||||
|
val compareResult = Visibilities.compare(currentVisibility, visibility)
|
||||||
|
if (compareResult != null && compareResult > 0) {
|
||||||
|
visibility = currentVisibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirFunction<*>.visibility(): Visibility? {
|
||||||
|
(symbol.fir as? FirMemberDeclaration)?.visibility?.let {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
(symbol.fir as? FirPropertyAccessor)?.visibility?.let {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* 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.PsiElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory0
|
||||||
|
|
||||||
|
inline fun <reified T : FirSourceElement, P : PsiElement> DiagnosticReporter.report(
|
||||||
|
source: T?,
|
||||||
|
factory: FirDiagnosticFactory0<T, P>
|
||||||
|
) {
|
||||||
|
source?.let { report(factory.on(it)) }
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.fir.DeclarationWithRelation
|
import org.jetbrains.kotlin.fir.DeclarationWithRelation
|
||||||
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.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
@@ -48,6 +49,7 @@ object FirErrors {
|
|||||||
val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE by error0<FirSourceElement, PsiElement>()
|
val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE by error0<FirSourceElement, PsiElement>()
|
||||||
val ABSTRACT_SUPER_CALL by error0<FirSourceElement, PsiElement>()
|
val ABSTRACT_SUPER_CALL by error0<FirSourceElement, PsiElement>()
|
||||||
val QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
val QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
||||||
|
val REDUNDANT_VISIBILITY_MODIFIER by error0<FirSourceElement, PsiElement>()
|
||||||
|
|
||||||
val INAPPLICABLE_INFIX_MODIFIER by existing<FirSourceElement, PsiElement, String>(Errors.INAPPLICABLE_INFIX_MODIFIER)
|
val INAPPLICABLE_INFIX_MODIFIER by existing<FirSourceElement, PsiElement, String>(Errors.INAPPLICABLE_INFIX_MODIFIER)
|
||||||
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
||||||
|
|||||||
Reference in New Issue
Block a user