[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