[FIR] Report PRIVATE_CLASS_MEMBER_FROM_INLINE on qualifiers of companions

^KT-56172 Fixed
^KT-55179
This commit is contained in:
Dmitriy Novozhilov
2023-01-25 12:09:36 +02:00
committed by Space Team
parent 522eec8c51
commit 4941fcd10e
5 changed files with 103 additions and 90 deletions
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
@@ -34,6 +35,7 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
abstract class FirInlineDeclarationChecker : FirFunctionChecker() { abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -114,6 +116,62 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
checkQualifiedAccess(variableAssignment, setterSymbol, data) checkQualifiedAccess(variableAssignment, setterSymbol, data)
} }
override fun visitResolvedQualifier(resolvedQualifier: FirResolvedQualifier, data: CheckerContext) {
val accessedClass = resolvedQualifier.symbol ?: return
val source = resolvedQualifier.source ?: return
if (accessedClass.isCompanion) {
checkAccessedDeclaration(source, accessedClass, accessedClass.visibility, data)
}
}
private fun checkAccessedDeclaration(
source: KtSourceElement,
accessedSymbol: FirBasedSymbol<*>,
declarationVisibility: Visibility,
context: CheckerContext
): AccessedDeclarationVisibilityData {
val recordedEffectiveVisibility = when (accessedSymbol) {
is FirCallableSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
is FirClassLikeSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
else -> shouldNotBeCalled()
}
val accessedDeclarationEffectiveVisibility = recordedEffectiveVisibility.let {
if (it == EffectiveVisibility.Local) {
EffectiveVisibility.Public
} else {
it
}
}
val isCalledFunPublicOrPublishedApi = accessedDeclarationEffectiveVisibility.publicApi
val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi
if (isInlineFunPublicOrPublishedApi &&
!isCalledFunPublicOrPublishedApi &&
declarationVisibility !== Visibilities.Local
) {
reporter.reportOn(
source,
FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE,
accessedSymbol,
inlineFunction.symbol,
context
)
} else {
checkPrivateClassMemberAccess(accessedSymbol, source, context)
}
return AccessedDeclarationVisibilityData(
isInlineFunPublicOrPublishedApi,
isCalledFunPublicOrPublishedApi,
accessedDeclarationEffectiveVisibility
)
}
private data class AccessedDeclarationVisibilityData(
val isInlineFunPublicOrPublishedApi: Boolean,
val isCalledFunPublicOrPublishedApi: Boolean,
val calledFunEffectiveVisibility: EffectiveVisibility
)
private fun checkReceiversOfQualifiedAccessExpression( private fun checkReceiversOfQualifiedAccessExpression(
qualifiedAccessExpression: FirQualifiedAccessExpression, qualifiedAccessExpression: FirQualifiedAccessExpression,
targetSymbol: FirBasedSymbol<*>?, targetSymbol: FirBasedSymbol<*>?,
@@ -217,32 +275,15 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
) { ) {
return return
} }
val recordedEffectiveVisibility = calledDeclaration.publishedApiEffectiveVisibility ?: calledDeclaration.effectiveVisibility val (isInlineFunPublicOrPublishedApi, isCalledFunPublicOrPublishedApi, calledFunEffectiveVisibility) = checkAccessedDeclaration(
val calledFunEffectiveVisibility = recordedEffectiveVisibility.let { source,
if (it == EffectiveVisibility.Local) { calledDeclaration,
EffectiveVisibility.Public calledDeclaration.visibility,
} else { context
it )
}
} if (isInlineFunPublicOrPublishedApi && isCalledFunPublicOrPublishedApi) {
val isCalledFunPublicOrPublishedApi = calledFunEffectiveVisibility.publicApi checkSuperCalls(calledDeclaration, accessExpression, context)
val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi
if (isInlineFunPublicOrPublishedApi &&
!isCalledFunPublicOrPublishedApi &&
calledDeclaration.visibility !== Visibilities.Local
) {
reporter.reportOn(
source,
FirErrors.NON_PUBLIC_CALL_FROM_PUBLIC_INLINE,
calledDeclaration,
inlineFunction.symbol,
context
)
} else {
checkPrivateClassMemberAccess(calledDeclaration, source, context)
if (isInlineFunPublicOrPublishedApi) {
checkSuperCalls(calledDeclaration, accessExpression, context)
}
} }
val isConstructorCall = calledDeclaration is FirConstructorSymbol val isConstructorCall = calledDeclaration is FirConstructorSymbol
@@ -261,7 +302,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
} }
private fun checkPrivateClassMemberAccess( private fun checkPrivateClassMemberAccess(
calledDeclaration: FirCallableSymbol<*>, calledDeclaration: FirBasedSymbol<*>,
source: KtSourceElement, source: KtSourceElement,
context: CheckerContext context: CheckerContext
) { ) {
@@ -327,7 +368,8 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
if (containingClassVisibility == Visibilities.Private || containingClassVisibility == Visibilities.PrivateToThis) { if (containingClassVisibility == Visibilities.Private || containingClassVisibility == Visibilities.PrivateToThis) {
return true return true
} }
if (containingClassSymbol is FirRegularClassSymbol && containingClassSymbol.isCompanion) { // We should check containing class of declaration only if this declaration is a member, not a class
if (this is FirCallableSymbol<*> && containingClassSymbol is FirRegularClassSymbol && containingClassSymbol.isCompanion) {
return containingClassSymbol.isInsidePrivateClass() return containingClassSymbol.isInsidePrivateClass()
} }
return false return false
@@ -10,13 +10,15 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataKey
import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry import org.jetbrains.kotlin.fir.declarations.FirDeclarationDataRegistry
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
private object PublishedApiEffectiveVisibilityKey : FirDeclarationDataKey() private object PublishedApiEffectiveVisibilityKey : FirDeclarationDataKey()
var FirDeclaration.publishedApiEffectiveVisibility: EffectiveVisibility? by FirDeclarationDataRegistry.data(PublishedApiEffectiveVisibilityKey) var FirDeclaration.publishedApiEffectiveVisibility: EffectiveVisibility? by FirDeclarationDataRegistry.data(PublishedApiEffectiveVisibilityKey)
inline val FirCallableSymbol<*>.publishedApiEffectiveVisibility: EffectiveVisibility? inline val FirBasedSymbol<*>.publishedApiEffectiveVisibility: EffectiveVisibility?
get() { get() {
lazyResolveToPhase(FirResolvePhase.STATUS) lazyResolveToPhase(FirResolvePhase.STATUS)
return fir.publishedApiEffectiveVisibility return fir.publishedApiEffectiveVisibility
@@ -44,63 +44,37 @@ inline val FirCallableSymbol<*>.isFromSealedClass: Boolean get() = rawStatus.isF
inline val FirCallableSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass inline val FirCallableSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass
inline val FirCallableSymbol<*>.isFun: Boolean get() = rawStatus.isFun inline val FirCallableSymbol<*>.isFun: Boolean get() = rawStatus.isFun
// ---------------------- regular with status ---------------------- // ---------------------- class like with status ----------------------
inline val FirClassSymbol<*>.modality: Modality? get() = resolvedStatus.modality inline val FirClassLikeSymbol<*>.modality: Modality? get() = resolvedStatus.modality
inline val FirClassSymbol<*>.isAbstract: Boolean get() = resolvedStatus.modality == Modality.ABSTRACT inline val FirClassLikeSymbol<*>.isAbstract: Boolean get() = resolvedStatus.modality == Modality.ABSTRACT
inline val FirClassSymbol<*>.isOpen: Boolean get() = resolvedStatus.modality == Modality.OPEN inline val FirClassLikeSymbol<*>.isOpen: Boolean get() = resolvedStatus.modality == Modality.OPEN
inline val FirClassSymbol<*>.isFinal: Boolean inline val FirClassLikeSymbol<*>.isFinal: Boolean
get() { get() {
// member with unspecified modality is final // member with unspecified modality is final
val modality = resolvedStatus.modality ?: return true val modality = resolvedStatus.modality ?: return true
return modality == Modality.FINAL return modality == Modality.FINAL
} }
inline val FirClassSymbol<*>.visibility: Visibility get() = resolvedStatus.visibility inline val FirClassLikeSymbol<*>.visibility: Visibility get() = resolvedStatus.visibility
inline val FirClassSymbol<*>.effectiveVisibility: EffectiveVisibility get() = resolvedStatus.effectiveVisibility inline val FirClassLikeSymbol<*>.effectiveVisibility: EffectiveVisibility get() = resolvedStatus.effectiveVisibility
inline val FirClassSymbol<*>.isActual: Boolean get() = resolvedStatus.isActual inline val FirClassLikeSymbol<*>.isActual: Boolean get() = resolvedStatus.isActual
inline val FirClassSymbol<*>.isExpect: Boolean get() = resolvedStatus.isExpect inline val FirClassLikeSymbol<*>.isExpect: Boolean get() = resolvedStatus.isExpect
inline val FirClassSymbol<*>.isInner: Boolean get() = rawStatus.isInner inline val FirClassLikeSymbol<*>.isInner: Boolean get() = rawStatus.isInner
inline val FirClassSymbol<*>.isStatic: Boolean get() = rawStatus.isStatic inline val FirClassLikeSymbol<*>.isStatic: Boolean get() = rawStatus.isStatic
inline val FirClassSymbol<*>.isInline: Boolean get() = rawStatus.isInline inline val FirClassLikeSymbol<*>.isInline: Boolean get() = rawStatus.isInline
inline val FirClassSymbol<*>.isExternal: Boolean get() = rawStatus.isExternal inline val FirClassLikeSymbol<*>.isExternal: Boolean get() = rawStatus.isExternal
inline val FirClassSymbol<*>.isFromSealedClass: Boolean get() = rawStatus.isFromSealedClass inline val FirClassLikeSymbol<*>.isFromSealedClass: Boolean get() = rawStatus.isFromSealedClass
inline val FirClassSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass inline val FirClassLikeSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass
inline val FirClassSymbol<*>.isFun: Boolean get() = rawStatus.isFun inline val FirClassLikeSymbol<*>.isFun: Boolean get() = rawStatus.isFun
inline val FirClassSymbol<*>.isCompanion: Boolean get() = rawStatus.isCompanion inline val FirClassLikeSymbol<*>.isCompanion: Boolean get() = rawStatus.isCompanion
inline val FirClassSymbol<*>.isData: Boolean get() = rawStatus.isData inline val FirClassLikeSymbol<*>.isData: Boolean get() = rawStatus.isData
inline val FirClassSymbol<*>.isSealed: Boolean get() = resolvedStatus.modality == Modality.SEALED inline val FirClassLikeSymbol<*>.isSealed: Boolean get() = resolvedStatus.modality == Modality.SEALED
inline val FirRegularClassSymbol.canHaveAbstractDeclaration: Boolean inline val FirRegularClassSymbol.canHaveAbstractDeclaration: Boolean
get() = isAbstract || isSealed || isEnumClass get() = isAbstract || isSealed || isEnumClass
// ---------------------- type aliases with status ----------------------
inline val FirTypeAliasSymbol.modality: Modality? get() = resolvedStatus.modality
inline val FirTypeAliasSymbol.isAbstract: Boolean get() = resolvedStatus.modality == Modality.ABSTRACT
inline val FirTypeAliasSymbol.isOpen: Boolean get() = resolvedStatus.modality == Modality.OPEN
inline val FirTypeAliasSymbol.isFinal: Boolean
get() {
// member with unspecified modality is final
val modality = resolvedStatus.modality ?: return true
return modality == Modality.FINAL
}
inline val FirTypeAliasSymbol.visibility: Visibility get() = resolvedStatus.visibility
inline val FirTypeAliasSymbol.effectiveVisibility: EffectiveVisibility
get() = resolvedStatus.effectiveVisibility
inline val FirTypeAliasSymbol.isActual: Boolean get() = resolvedStatus.isActual
inline val FirTypeAliasSymbol.isExpect: Boolean get() = resolvedStatus.isExpect
inline val FirTypeAliasSymbol.isInner: Boolean get() = rawStatus.isInner
inline val FirTypeAliasSymbol.isStatic: Boolean get() = rawStatus.isStatic
inline val FirTypeAliasSymbol.isInline: Boolean get() = rawStatus.isInline
inline val FirTypeAliasSymbol.isExternal: Boolean get() = rawStatus.isExternal
inline val FirTypeAliasSymbol.isFromSealedClass: Boolean get() = rawStatus.isFromSealedClass
inline val FirTypeAliasSymbol.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass
inline val FirTypeAliasSymbol.isFun: Boolean get() = rawStatus.isFun
// ---------------------- common classes ---------------------- // ---------------------- common classes ----------------------
inline val FirClassLikeSymbol<*>.isLocal: Boolean get() = classId.isLocal inline val FirClassLikeSymbol<*>.isLocal: Boolean get() = classId.isLocal
@@ -108,19 +82,6 @@ inline val FirClassLikeSymbol<*>.isLocal: Boolean get() = classId.isLocal
inline val FirClassSymbol<*>.isLocalClassOrAnonymousObject: Boolean inline val FirClassSymbol<*>.isLocalClassOrAnonymousObject: Boolean
get() = classId.isLocal || this is FirAnonymousObjectSymbol get() = classId.isLocal || this is FirAnonymousObjectSymbol
inline val FirClassLikeSymbol<*>.isExpect: Boolean
get() = when (this) {
is FirRegularClassSymbol -> isExpect
is FirTypeAliasSymbol -> isExpect
else -> false
}
inline val FirClassLikeSymbol<*>.isActual: Boolean
get() = when (this) {
is FirRegularClassSymbol -> isActual
is FirTypeAliasSymbol -> isActual
else -> false
}
inline val FirClassSymbol<*>.isInterface: Boolean inline val FirClassSymbol<*>.isInterface: Boolean
get() = classKind.isInterface get() = classKind.isInterface
@@ -4,12 +4,16 @@
private class Foo { private class Foo {
companion object { companion object {
fun buildFoo() = Foo() fun buildFoo() = Foo()
object Nested {
fun bar() {}
}
} }
} }
internal <!NOTHING_TO_INLINE!>inline<!> fun foo() { internal <!NOTHING_TO_INLINE!>inline<!> fun foo() {
<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>() <!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>()
Foo.Companion Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Companion<!>
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>buildFoo<!>() Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>buildFoo<!>()
Foo.Companion.Nested.bar()
} }
@@ -4,6 +4,10 @@
private class Foo { private class Foo {
companion object { companion object {
fun buildFoo() = Foo() fun buildFoo() = Foo()
object Nested {
fun bar() {}
}
} }
} }
@@ -11,5 +15,5 @@ internal <!NOTHING_TO_INLINE!>inline<!> fun foo() {
<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>() <!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>()
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Companion<!> Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Companion<!>
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE_WARNING!>buildFoo<!>() Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE_WARNING!>buildFoo<!>()
Foo.Companion.Nested.bar()
} }