[FIR] 3/n FirInlineDeclarationChecker: take into consideration upcoming data class copy visibility change
^KT-68621 Fixed
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 12e6b38..9c7d2f9 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
@@ -18,6 +18,10 @@
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.isDataClassCopy
+import org.jetbrains.kotlin.fir.analysis.checkers.getDirectOverriddenSymbols
+import org.jetbrains.kotlin.fir.analysis.checkers.inlineCheckerExtension
+import org.jetbrains.kotlin.fir.analysis.checkers.isInlineOnly
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
@@ -63,6 +67,34 @@
// prevent delegation to visitQualifiedAccessExpression, which causes redundant diagnostics
override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression, data: CheckerContext) {}
+ private fun accessedDeclarationEffectiveVisibility(
+ accessExpression: FirStatement,
+ accessedSymbol: FirBasedSymbol<*>,
+ context: CheckerContext,
+ ): EffectiveVisibility {
+ val recordedEffectiveVisibility = when (accessedSymbol) {
+ is FirCallableSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
+ is FirClassLikeSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
+ else -> shouldNotBeCalled()
+ }
+ return when {
+ recordedEffectiveVisibility.isReachableDueToLocalDispatchReceiver(accessExpression, context) -> EffectiveVisibility.Public
+ recordedEffectiveVisibility == EffectiveVisibility.Local -> EffectiveVisibility.Public
+ else -> recordedEffectiveVisibility
+ }
+ }
+
+ private fun shouldReportNonPublicCallFromPublicInline(
+ accessedDeclarationEffectiveVisibility: EffectiveVisibility,
+ declarationVisibility: Visibility,
+ ): Boolean {
+ val isCalledFunPublicOrPublishedApi = accessedDeclarationEffectiveVisibility.publicApi
+ val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi
+ return isInlineFunPublicOrPublishedApi &&
+ !isCalledFunPublicOrPublishedApi &&
+ declarationVisibility !== Visibilities.Local
+ }
+
internal fun checkAccessedDeclaration(
source: KtSourceElement,
accessExpression: FirStatement,
@@ -71,37 +103,27 @@
context: CheckerContext,
reporter: DiagnosticReporter,
): AccessedDeclarationVisibilityData {
- val recordedEffectiveVisibility = when (accessedSymbol) {
- is FirCallableSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
- is FirClassLikeSymbol<*> -> accessedSymbol.publishedApiEffectiveVisibility ?: accessedSymbol.effectiveVisibility
- else -> shouldNotBeCalled()
- }
-
- val accessedDeclarationEffectiveVisibility = when {
- recordedEffectiveVisibility.isReachableDueToLocalDispatchReceiver(accessExpression, context) -> EffectiveVisibility.Public
- recordedEffectiveVisibility == EffectiveVisibility.Local -> EffectiveVisibility.Public
- else -> recordedEffectiveVisibility
- }
- val isCalledFunPublicOrPublishedApi = accessedDeclarationEffectiveVisibility.publicApi
- val isInlineFunPublicOrPublishedApi = inlineFunEffectiveVisibility.publicApi
- if (isInlineFunPublicOrPublishedApi &&
- !isCalledFunPublicOrPublishedApi &&
- declarationVisibility !== Visibilities.Local
- ) {
- reporter.reportOn(
- source,
- getNonPublicCallFromPublicInlineFactory(accessExpression, accessedSymbol, source, context),
- accessedSymbol,
- inlineFunction.symbol,
- context
- )
- } else {
- checkPrivateClassMemberAccess(accessedSymbol, source, context, reporter)
+ val accessedVisibility = accessedDeclarationEffectiveVisibility(accessExpression, accessedSymbol, context)
+ val accessedDataCopyVisibility = accessedSymbol.unwrapDataClassCopyWithPrimaryConstructorOrNull(context.session)
+ ?.effectiveVisibility
+ when {
+ shouldReportNonPublicCallFromPublicInline(accessedVisibility, declarationVisibility) ->
+ reporter.reportOn(
+ source,
+ getNonPublicCallFromPublicInlineFactory(accessExpression, accessedSymbol, source, context),
+ accessedSymbol,
+ inlineFunction.symbol,
+ context
+ )
+ accessedDataCopyVisibility != null &&
+ shouldReportNonPublicCallFromPublicInline(accessedDataCopyVisibility, declarationVisibility) ->
+ reporter.reportOn(source, FirErrors.NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE, context)
+ else -> checkPrivateClassMemberAccess(accessedSymbol, source, context, reporter)
}
return AccessedDeclarationVisibilityData(
- isInlineFunPublicOrPublishedApi,
- isCalledFunPublicOrPublishedApi,
- accessedDeclarationEffectiveVisibility
+ inlineFunEffectiveVisibility.publicApi,
+ accessedVisibility.publicApi,
+ accessedVisibility
)
}
@@ -573,3 +595,8 @@
session,
)
}
+
+fun FirBasedSymbol<*>.unwrapDataClassCopyWithPrimaryConstructorOrNull(session: FirSession): FirCallableSymbol<*>? =
+ (this as? FirCallableSymbol<*>)?.containingClassLookupTag()?.toSymbol(session)?.let { it as? FirClassSymbol<*> }
+ ?.takeIf { containingClass -> isDataClassCopy(containingClass, session) }
+ ?.primaryConstructorSymbol(session)
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
index 45b1e1b..eb336f8 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirDataClassCopyUsageWillBecomeInaccessibleChecker.kt
@@ -67,10 +67,11 @@
}
}
-internal fun FirCallableSymbol<*>.isDataClassCopy(containingClass: FirClassSymbol<*>, session: FirSession): Boolean {
- if (!DataClassResolver.isCopy(name)) return false
- val constructor = containingClass.primaryConstructorSymbol(session)
+internal fun FirCallableSymbol<*>.isDataClassCopy(containingClass: FirClassSymbol<*>?, session: FirSession): Boolean {
+ val constructor = containingClass?.primaryConstructorSymbol(session)
return this is FirNamedFunctionSymbol &&
+ DataClassResolver.isCopy(name) &&
+ containingClass != null &&
containingClass.isData &&
containingClass.classKind.isClass &&
dispatchReceiverType?.classId == containingClass.classId &&
diff --git a/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase1_inlineFun.fir.kt b/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase1_inlineFun.fir.kt
index 381f36b..bac9dfb 100644
--- a/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase1_inlineFun.fir.kt
+++ b/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase1_inlineFun.fir.kt
@@ -2,18 +2,18 @@
// WITH_STDLIB
data class PrivateInline <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING!>private<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_WARNING!>copy<!>()
}
}
data class PublishedApiInline @PublishedApi <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING!>internal<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_WARNING!>copy<!>()
}
}
data class InternalInline <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_WARNING!>internal<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_WARNING!>copy<!>()
}
}
diff --git a/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase2_inlineFun.fir.kt b/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase2_inlineFun.fir.kt
index 4fc41cc..395f53c 100644
--- a/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase2_inlineFun.fir.kt
+++ b/compiler/testData/diagnostics/tests/dataClassNonPublicConstructor/deprecationPhase2_inlineFun.fir.kt
@@ -2,18 +2,18 @@
// WITH_STDLIB
data class PrivateInline <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_ERROR!>private<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_ERROR!>copy<!>()
}
}
data class PublishedApiInline @PublishedApi <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_ERROR!>internal<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_ERROR!>copy<!>()
}
}
data class InternalInline <!DATA_CLASS_COPY_VISIBILITY_WILL_BE_CHANGED_ERROR!>internal<!> constructor(val value: Int) {
<!NOTHING_TO_INLINE!>inline<!> fun huh() {
- copy()
+ <!NON_PUBLIC_DATA_COPY_CALL_FROM_PUBLIC_INLINE_ERROR!>copy<!>()
}
}