diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt index 5c587b5314f..c9c7e4a2d41 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineDeclarationChecker.kt @@ -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 diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/transformers/publishedApiEffectiveVisibility.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/transformers/publishedApiEffectiveVisibility.kt index 36ee318c623..fcd8c72e8ef 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/transformers/publishedApiEffectiveVisibility.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/transformers/publishedApiEffectiveVisibility.kt @@ -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 diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirSymbolStatusUtils.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirSymbolStatusUtils.kt index 5fdf92edc81..af47a2a1dfd 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirSymbolStatusUtils.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/FirSymbolStatusUtils.kt @@ -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 diff --git a/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.fir.kt b/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.fir.kt index e2fb69382a2..2ac00e64fa7 100644 --- a/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.fir.kt +++ b/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.fir.kt @@ -4,12 +4,16 @@ private class Foo { companion object { fun buildFoo() = Foo() + + object Nested { + fun bar() {} + } } } internal inline fun foo() { Foo() - Foo.Companion + Foo.Companion Foo.buildFoo() + Foo.Companion.Nested.bar() } - diff --git a/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.kt b/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.kt index f7587bf9a9b..31e6e669aab 100644 --- a/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.kt +++ b/compiler/testData/diagnostics/tests/inline/nonPublicMember/kt55179.kt @@ -4,6 +4,10 @@ private class Foo { companion object { fun buildFoo() = Foo() + + object Nested { + fun bar() {} + } } } @@ -11,5 +15,5 @@ internal inline fun foo() { Foo() Foo.Companion Foo.buildFoo() + Foo.Companion.Nested.bar() } -