From dd46c36b2433a9583cba6bd3a4ef36b8fa9fed84 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 6 Sep 2021 21:39:11 +0300 Subject: [PATCH] [FIR] Add SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC --- .../expresssions/protectedVisibility.kt | 2 +- .../visibility/protectedInCompanion.kt | 2 +- .../diagnostics/FirJvmDiagnosticsList.kt | 1 + .../analysis/diagnostics/jvm/FirJvmErrors.kt | 1 + .../jvm/checkers/JvmExpressionCheckers.kt | 10 +-- ...otectedInSuperClassCompanionCallChecker.kt | 70 +++++++++++++++++++ .../kotlin/resolve/AnnotationUtil.kt | 2 + .../scopes/VisibilityInClassObject.fir.kt | 2 +- .../CallCompanionProtectedNonStatic.fir.kt | 24 +++---- .../CallCompanionProtectedNonStatic.kt | 6 +- .../diagnostics/KtFirDataClassConverters.kt | 6 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 ++ 13 files changed, 115 insertions(+), 22 deletions(-) create mode 100644 compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmProtectedInSuperClassCompanionCallChecker.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/expresssions/protectedVisibility.kt b/compiler/fir/analysis-tests/testData/resolve/expresssions/protectedVisibility.kt index f50885b96b5..b848c930480 100644 --- a/compiler/fir/analysis-tests/testData/resolve/expresssions/protectedVisibility.kt +++ b/compiler/fir/analysis-tests/testData/resolve/expresssions/protectedVisibility.kt @@ -34,7 +34,7 @@ class Derived : Protected() { Nested().bar() // hidden fromCompanion() - protectedFromCompanion() + protectedFromCompanion() } private class NestedDerived : Nested() { diff --git a/compiler/fir/analysis-tests/testData/resolve/visibility/protectedInCompanion.kt b/compiler/fir/analysis-tests/testData/resolve/visibility/protectedInCompanion.kt index 77269b84e77..e30002df5e6 100644 --- a/compiler/fir/analysis-tests/testData/resolve/visibility/protectedInCompanion.kt +++ b/compiler/fir/analysis-tests/testData/resolve/visibility/protectedInCompanion.kt @@ -5,5 +5,5 @@ abstract class A { } class B : A() { - val y: String = PROTECTED_CONST + val y: String = PROTECTED_CONST } diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 66a3955d2cf..bd80e30de01 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -163,5 +163,6 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { DefaultMethodsCallFromJava6TargetError, PositioningStrategy.REFERENCE_BY_QUALIFIED ) + val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) } } diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index 1c2500c775c..19736366c46 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -110,5 +110,6 @@ object FirJvmErrors { val JVM_SYNTHETIC_ON_DELEGATE by error0() val DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index 9051da70194..a3c6c277efd 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -5,13 +5,15 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers -import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationChecker -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.* import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.* object JvmExpressionCheckers : ExpressionCheckers() { + override val qualifiedAccessCheckers: Set + get() = setOf( + FirJvmProtectedInSuperClassCompanionCallChecker + ) + override val qualifiedAccessExpressionCheckers: Set get() = setOf( FirInterfaceDefaultMethodCallChecker diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmProtectedInSuperClassCompanionCallChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmProtectedInSuperClassCompanionCallChecker.kt new file mode 100644 index 00000000000..b95e7a7494a --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmProtectedInSuperClassCompanionCallChecker.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2021 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.jvm.checkers.expression + +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId +import org.jetbrains.kotlin.fir.declarations.utils.isCompanion +import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess +import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment +import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.resolve.defaultType +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.typeContext +import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_CLASS_ID +import org.jetbrains.kotlin.types.AbstractTypeChecker + +object FirJvmProtectedInSuperClassCompanionCallChecker : FirQualifiedAccessChecker() { + override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) { + val dispatchReceiver = expression.dispatchReceiver + if (dispatchReceiver is FirNoReceiverExpression) return + val dispatchClassSymbol = dispatchReceiver.typeRef.toRegularClassSymbol(context.session) ?: return + val resolvedSymbol = expression.calleeReference.toResolvedCallableSymbol() ?: return + + val visibility = if (resolvedSymbol is FirPropertySymbol) { + if (expression is FirVariableAssignment) + resolvedSymbol.setterSymbol?.visibility ?: resolvedSymbol.visibility + else + resolvedSymbol.getterSymbol?.visibility ?: resolvedSymbol.visibility + } else { + resolvedSymbol.visibility + } + if (visibility != Visibilities.Protected) return + if (resolvedSymbol.getAnnotationByClassId(JVM_STATIC_ANNOTATION_CLASS_ID) != null) return + if (!dispatchClassSymbol.isCompanion) return + val companionContainingClassSymbol = + dispatchClassSymbol.getContainingDeclarationSymbol(context.session) as? FirRegularClassSymbol ?: return + + // Called from within a derived class + val companionContainingType = companionContainingClassSymbol.defaultType() + if (context.findClosest { + AbstractTypeChecker.isSubtypeOf(context.session.typeContext, it.symbol.defaultType(), companionContainingType) + } == null + ) { + return + } + + // Called not within the same companion object or its owner class + if (context.findClosest { + it.symbol == dispatchClassSymbol || it.symbol == companionContainingClassSymbol + } == null + ) { + reporter.reportOn(expression.calleeReference.source, FirJvmErrors.SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, context) + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt index bc17c9c215f..98700ed16ba 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve.annotations import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.constants.ConstantValue @@ -26,6 +27,7 @@ import org.jetbrains.kotlin.resolve.constants.ErrorValue // This annotation is declared here in frontend (as opposed to frontend.java) because it's used in MainFunctionDetector. // If you wish to add another JVM-related annotation and has/find utility methods, please proceed to jvmAnnotationUtil.kt val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic") +val JVM_STATIC_ANNOTATION_CLASS_ID = ClassId.topLevel(JVM_STATIC_ANNOTATION_FQ_NAME) fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean { return annotations.findAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) != null diff --git a/compiler/testData/diagnostics/tests/scopes/VisibilityInClassObject.fir.kt b/compiler/testData/diagnostics/tests/scopes/VisibilityInClassObject.fir.kt index 1408ac18fa1..c7fba024def 100644 --- a/compiler/testData/diagnostics/tests/scopes/VisibilityInClassObject.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/VisibilityInClassObject.fir.kt @@ -28,6 +28,6 @@ class B: A() { devNull(A.internal_val) devNull(A.public_val) devNull(A.private_val) - devNull(A.protected_val) + devNull(A.protected_val) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt index 17f6cac2902..c0a0e0d9edb 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.fir.kt @@ -3,7 +3,7 @@ open class VeryBase { } open class Base { - protected fun foo() { + protected fun foo() { bar() // Ok baz() // Ok } @@ -25,14 +25,14 @@ open class Base { baz() // Ok } } - + companion object : VeryBase() { var prop = 42 protected set protected fun bar() {} - @JvmStatic protected fun gav() {} + @JvmStatic protected fun gav() {} class Nested { fun fromNested() { @@ -47,27 +47,27 @@ class Derived : Base() { fun test() { foo() // Ok gav() // Ok - bar() - baz() - prop = 0 + bar() + baz() + prop = 0 } inner class DerivedInner { fun fromDerivedInner() { foo() // Ok gav() // Ok - bar() - baz() - prop = 0 + bar() + baz() + prop = 0 } } companion object { fun test2() { gav() // Ok - bar() - baz() - prop = 0 + bar() + baz() + prop = 0 } } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.kt b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.kt index e9512072dbe..bec6f23b452 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/CallCompanionProtectedNonStatic.kt @@ -3,7 +3,7 @@ open class VeryBase { } open class Base { - protected fun foo() { + protected fun foo() { bar() // Ok baz() // Ok } @@ -25,14 +25,14 @@ open class Base { baz() // Ok } } - + companion object : VeryBase() { var prop = 42 protected set protected fun bar() {} - @JvmStatic protected fun gav() {} + @JvmStatic protected fun gav() {} class Nested { fun fromNested() { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 45123e58e58..1102ecb1fb3 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -4028,4 +4028,10 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC) { firDiagnostic -> + SubclassCantCallCompanionProtectedNonStaticImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 83effb7fa7a..70d5bad34ec 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2803,4 +2803,8 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = InterfaceStaticMethodCallFromJava6TargetWarning::class } + abstract class SubclassCantCallCompanionProtectedNonStatic : KtFirDiagnostic() { + override val diagnosticClass get() = SubclassCantCallCompanionProtectedNonStatic::class + } + } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 529b4a997c6..337502f08cc 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -4559,3 +4559,10 @@ internal class InterfaceStaticMethodCallFromJava6TargetWarningImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class SubclassCantCallCompanionProtectedNonStaticImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.SubclassCantCallCompanionProtectedNonStatic(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} +