[FIR] Add SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC

This commit is contained in:
Ivan Kochurkin
2021-09-06 21:39:11 +03:00
committed by teamcityserver
parent 55990f65cb
commit dd46c36b24
13 changed files with 115 additions and 22 deletions
@@ -34,7 +34,7 @@ class Derived : Protected() {
Nested().<!INVISIBLE_REFERENCE!>bar<!>() // hidden
fromCompanion()
protectedFromCompanion()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>protectedFromCompanion<!>()
}
private class NestedDerived : Nested() {
@@ -5,5 +5,5 @@ abstract class A {
}
class B : A() {
val y: String = PROTECTED_CONST
val y: String = <!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>PROTECTED_CONST<!>
}
@@ -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<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
}
}
@@ -110,5 +110,6 @@ object FirJvmErrors {
val JVM_SYNTHETIC_ON_DELEGATE by error0<KtAnnotationEntry>()
val DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0<PsiElement>(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET by deprecationError0<PsiElement>(DefaultMethodsCallFromJava6TargetError, SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
}
@@ -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<FirQualifiedAccessChecker>
get() = setOf(
FirJvmProtectedInSuperClassCompanionCallChecker
)
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
get() = setOf(
FirInterfaceDefaultMethodCallChecker
@@ -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<FirClass> {
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<FirClass> {
it.symbol == dispatchClassSymbol || it.symbol == companionContainingClassSymbol
} == null
) {
reporter.reportOn(expression.calleeReference.source, FirJvmErrors.SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, context)
}
}
}
@@ -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
@@ -28,6 +28,6 @@ class B: A() {
devNull(A.internal_val)
devNull(A.public_val)
devNull(A.<!INVISIBLE_REFERENCE!>private_val<!>)
devNull(A.protected_val)
devNull(A.<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>protected_val<!>)
}
}
@@ -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
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>bar<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>baz<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>prop<!> = 0
}
inner class DerivedInner {
fun fromDerivedInner() {
foo() // Ok
gav() // Ok
bar()
baz()
prop = 0
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>bar<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>baz<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>prop<!> = 0
}
}
companion object {
fun test2() {
gav() // Ok
bar()
baz()
prop = 0
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>bar<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>baz<!>()
<!SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC!>prop<!> = 0
}
}
}
@@ -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() {
@@ -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,
)
}
}
@@ -2803,4 +2803,8 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = InterfaceStaticMethodCallFromJava6TargetWarning::class
}
abstract class SubclassCantCallCompanionProtectedNonStatic : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = SubclassCantCallCompanionProtectedNonStatic::class
}
}
@@ -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<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}