[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.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
|
||||||
|
|||||||
+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.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
|
||||||
|
|||||||
+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<*>.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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user