[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.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
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.lexer.KtTokens
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
override fun check(declaration: FirFunction, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -114,6 +116,62 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
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(
qualifiedAccessExpression: FirQualifiedAccessExpression,
targetSymbol: FirBasedSymbol<*>?,
@@ -217,32 +275,15 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
) {
return
}
val recordedEffectiveVisibility = calledDeclaration.publishedApiEffectiveVisibility ?: calledDeclaration.effectiveVisibility
val calledFunEffectiveVisibility = recordedEffectiveVisibility.let {
if (it == EffectiveVisibility.Local) {
EffectiveVisibility.Public
} else {
it
}
}
val isCalledFunPublicOrPublishedApi = calledFunEffectiveVisibility.publicApi
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 (isInlineFunPublicOrPublishedApi, isCalledFunPublicOrPublishedApi, calledFunEffectiveVisibility) = checkAccessedDeclaration(
source,
calledDeclaration,
calledDeclaration.visibility,
context
)
if (isInlineFunPublicOrPublishedApi && isCalledFunPublicOrPublishedApi) {
checkSuperCalls(calledDeclaration, accessExpression, context)
}
val isConstructorCall = calledDeclaration is FirConstructorSymbol
@@ -261,7 +302,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
}
private fun checkPrivateClassMemberAccess(
calledDeclaration: FirCallableSymbol<*>,
calledDeclaration: FirBasedSymbol<*>,
source: KtSourceElement,
context: CheckerContext
) {
@@ -327,7 +368,8 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
if (containingClassVisibility == Visibilities.Private || containingClassVisibility == Visibilities.PrivateToThis) {
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 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.FirDeclarationDataRegistry
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.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
private object PublishedApiEffectiveVisibilityKey : FirDeclarationDataKey()
var FirDeclaration.publishedApiEffectiveVisibility: EffectiveVisibility? by FirDeclarationDataRegistry.data(PublishedApiEffectiveVisibilityKey)
inline val FirCallableSymbol<*>.publishedApiEffectiveVisibility: EffectiveVisibility?
inline val FirBasedSymbol<*>.publishedApiEffectiveVisibility: EffectiveVisibility?
get() {
lazyResolveToPhase(FirResolvePhase.STATUS)
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<*>.isFun: Boolean get() = rawStatus.isFun
// ---------------------- regular with status ----------------------
// ---------------------- class like with status ----------------------
inline val FirClassSymbol<*>.modality: Modality? get() = resolvedStatus.modality
inline val FirClassSymbol<*>.isAbstract: Boolean get() = resolvedStatus.modality == Modality.ABSTRACT
inline val FirClassSymbol<*>.isOpen: Boolean get() = resolvedStatus.modality == Modality.OPEN
inline val FirClassSymbol<*>.isFinal: Boolean
inline val FirClassLikeSymbol<*>.modality: Modality? get() = resolvedStatus.modality
inline val FirClassLikeSymbol<*>.isAbstract: Boolean get() = resolvedStatus.modality == Modality.ABSTRACT
inline val FirClassLikeSymbol<*>.isOpen: Boolean get() = resolvedStatus.modality == Modality.OPEN
inline val FirClassLikeSymbol<*>.isFinal: Boolean
get() {
// member with unspecified modality is final
val modality = resolvedStatus.modality ?: return true
return modality == Modality.FINAL
}
inline val FirClassSymbol<*>.visibility: Visibility get() = resolvedStatus.visibility
inline val FirClassSymbol<*>.effectiveVisibility: EffectiveVisibility get() = resolvedStatus.effectiveVisibility
inline val FirClassLikeSymbol<*>.visibility: Visibility get() = resolvedStatus.visibility
inline val FirClassLikeSymbol<*>.effectiveVisibility: EffectiveVisibility get() = resolvedStatus.effectiveVisibility
inline val FirClassSymbol<*>.isActual: Boolean get() = resolvedStatus.isActual
inline val FirClassSymbol<*>.isExpect: Boolean get() = resolvedStatus.isExpect
inline val FirClassSymbol<*>.isInner: Boolean get() = rawStatus.isInner
inline val FirClassSymbol<*>.isStatic: Boolean get() = rawStatus.isStatic
inline val FirClassSymbol<*>.isInline: Boolean get() = rawStatus.isInline
inline val FirClassSymbol<*>.isExternal: Boolean get() = rawStatus.isExternal
inline val FirClassSymbol<*>.isFromSealedClass: Boolean get() = rawStatus.isFromSealedClass
inline val FirClassSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass
inline val FirClassSymbol<*>.isFun: Boolean get() = rawStatus.isFun
inline val FirClassSymbol<*>.isCompanion: Boolean get() = rawStatus.isCompanion
inline val FirClassSymbol<*>.isData: Boolean get() = rawStatus.isData
inline val FirClassSymbol<*>.isSealed: Boolean get() = resolvedStatus.modality == Modality.SEALED
inline val FirClassLikeSymbol<*>.isActual: Boolean get() = resolvedStatus.isActual
inline val FirClassLikeSymbol<*>.isExpect: Boolean get() = resolvedStatus.isExpect
inline val FirClassLikeSymbol<*>.isInner: Boolean get() = rawStatus.isInner
inline val FirClassLikeSymbol<*>.isStatic: Boolean get() = rawStatus.isStatic
inline val FirClassLikeSymbol<*>.isInline: Boolean get() = rawStatus.isInline
inline val FirClassLikeSymbol<*>.isExternal: Boolean get() = rawStatus.isExternal
inline val FirClassLikeSymbol<*>.isFromSealedClass: Boolean get() = rawStatus.isFromSealedClass
inline val FirClassLikeSymbol<*>.isFromEnumClass: Boolean get() = rawStatus.isFromEnumClass
inline val FirClassLikeSymbol<*>.isFun: Boolean get() = rawStatus.isFun
inline val FirClassLikeSymbol<*>.isCompanion: Boolean get() = rawStatus.isCompanion
inline val FirClassLikeSymbol<*>.isData: Boolean get() = rawStatus.isData
inline val FirClassLikeSymbol<*>.isSealed: Boolean get() = resolvedStatus.modality == Modality.SEALED
inline val FirRegularClassSymbol.canHaveAbstractDeclaration: Boolean
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 ----------------------
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
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
get() = classKind.isInterface
@@ -4,12 +4,16 @@
private class Foo {
companion object {
fun buildFoo() = Foo()
object Nested {
fun bar() {}
}
}
}
internal <!NOTHING_TO_INLINE!>inline<!> fun foo() {
<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>()
Foo.Companion
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Companion<!>
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>buildFoo<!>()
Foo.Companion.Nested.bar()
}
@@ -4,6 +4,10 @@
private class Foo {
companion object {
fun buildFoo() = Foo()
object Nested {
fun bar() {}
}
}
}
@@ -11,5 +15,5 @@ internal <!NOTHING_TO_INLINE!>inline<!> fun foo() {
<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Foo<!>()
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE!>Companion<!>
Foo.<!PRIVATE_CLASS_MEMBER_FROM_INLINE_WARNING!>buildFoo<!>()
Foo.Companion.Nested.bar()
}