[FIR] Report PRIVATE_CLASS_MEMBER_FROM_INLINE on qualifiers of companions
^KT-56172 Fixed ^KT-55179
This commit is contained in:
committed by
Space Team
parent
522eec8c51
commit
4941fcd10e
+70
-28
@@ -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
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+19
-58
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user