[FIR] Improve INVISIBLE_REFERENCE message

Report the nearest invisible containing declaration in cases where
the declaration itself is visible.

#KT-53820 Fixed
This commit is contained in:
Kirill Rakhman
2023-06-19 15:02:57 +02:00
committed by Space Team
parent 6a073e0b17
commit 911e62257a
24 changed files with 237 additions and 98 deletions
@@ -256,6 +256,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
add(FirErrors.INVISIBLE_REFERENCE) { firDiagnostic ->
InvisibleReferenceImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b,
firDiagnostic.c,
firDiagnostic as KtPsiDiagnostic,
token,
)
@@ -220,6 +220,8 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
interface InvisibleReference : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = InvisibleReference::class
val reference: KtSymbol
val visible: Visibility
val containingDeclaration: ClassId?
}
interface UnresolvedReference : KtFirDiagnostic<PsiElement> {
@@ -248,6 +248,8 @@ internal class InvisibleSetterImpl(
internal class InvisibleReferenceImpl(
override val reference: KtSymbol,
override val visible: Visibility,
override val containingDeclaration: ClassId?,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.InvisibleReference
@@ -17,4 +17,4 @@ KtErrorCallInfo:
typeArgumentsMapping = {}
argumentMapping = {}
]
diagnostic = ERROR<INVISIBLE_REFERENCE: Symbol fun foo(): Unit is invisible>
diagnostic = ERROR<INVISIBLE_REFERENCE: Cannot access 'fun foo(): Unit': it is private in '/A'>
@@ -16,7 +16,7 @@ KtInapplicableCallCandidateInfo:
isInBestCandidates = false
KtInapplicableCallCandidateInfo:
diagnostic = ERROR<INVISIBLE_REFERENCE: Symbol constructor(p: String): A is invisible>
diagnostic = ERROR<INVISIBLE_REFERENCE: Cannot access 'constructor(p: String): A': it is private in '/A'>
candidate = KtDelegatedConstructorCall:
kind = SUPER_CALL
partiallyAppliedSymbol = KtPartiallyAppliedSymbol:
@@ -1,5 +1,5 @@
KtInapplicableCallCandidateInfo:
diagnostic = ERROR<INVISIBLE_REFERENCE: Symbol fun foo(): Unit is invisible>
diagnostic = ERROR<INVISIBLE_REFERENCE: Cannot access 'fun foo(): Unit': it is private in '/A'>
candidate = KtSimpleFunctionCall:
isImplicitInvoke = false
partiallyAppliedSymbol = KtPartiallyAppliedSymbol:
@@ -53,7 +53,7 @@ FILE: nestedVisibility.kt
}
public final fun foo(): R|kotlin/Unit| {
Q|Outer|.R|/Outer.PrivateNested.PrivateNested<HIDDEN: /Outer.PrivateNested.PrivateNested is invisible>#|()
Q|Outer|.R|/Outer.PrivateNested.PrivateNested<HIDDEN: Outer.PrivateNested is invisible>#|()
this@R|/Derived|.super<R|Outer|>.R|/Outer.PrivateInner.PrivateInner<HIDDEN: /Outer.PrivateInner.PrivateInner is invisible>#|()
Q|Outer|.R|/Outer.ProtectedNested.ProtectedNested|()
this@R|/Derived|.super<R|Outer|>.R|/Outer.ProtectedInner.ProtectedInner|()
@@ -63,9 +63,9 @@ FILE: nestedVisibility.kt
}
public final fun foo(): R|kotlin/Unit| {
Q|Outer|.R|/Outer.PrivateNested.PrivateNested<HIDDEN: /Outer.PrivateNested.PrivateNested is invisible>#|()
Q|Outer|.R|/Outer.PrivateNested.PrivateNested<HIDDEN: Outer.PrivateNested is invisible>#|()
R|/Outer.Outer|().R|/Outer.PrivateInner.PrivateInner<HIDDEN: /Outer.PrivateInner.PrivateInner is invisible>#|()
Q|Outer|.R|/Outer.ProtectedNested.ProtectedNested<HIDDEN: /Outer.ProtectedNested.ProtectedNested is invisible>#|()
Q|Outer|.R|/Outer.ProtectedNested.ProtectedNested<HIDDEN: Outer.ProtectedNested is invisible>#|()
R|/Outer.Outer|().R|/Outer.ProtectedInner.ProtectedInner<HIDDEN: /Outer.ProtectedInner.ProtectedInner is invisible>#|()
Q|Outer|.R|/Outer.PublicNested.PublicNested|()
R|/Outer.Outer|().R|/Outer.PublicInner.PublicInner|()
@@ -96,11 +96,11 @@ FILE: first.kt
R|/Private.Private|().R|/Private.baz|()
R|/Private.Private|().R|/Private.Inner.Inner|()
R|/Private.Private|().R|/Private.bar<HIDDEN: /Private.bar is invisible>#|()
Q|Private|.R|/Private.Nested.Nested<HIDDEN: /Private.Nested.Nested is invisible>#|()
Q|Private|.R|/Private.Nested.Nested<HIDDEN: Private.Nested is invisible>#|()
Q|Private|.R|/Private.Companion.fromCompanion<HIDDEN: /Private.Companion.fromCompanion is invisible>#|()
}
FILE: second.kt
public final fun secondTest(): R|kotlin/Unit| {
R|/foo<HIDDEN: /foo is invisible>#|()
R|/Private.Private<HIDDEN: /Private.Private is invisible>#|()
R|/Private.Private<HIDDEN: Private is invisible>#|()
}
@@ -80,7 +80,7 @@ FILE: protectedVisibility.kt
R|/Protected.Protected|().R|/Protected.baz|()
R|/Protected.Protected|().R|/Protected.Inner.Inner|()
R|/Protected.Protected|().R|/Protected.bar<HIDDEN: /Protected.bar is invisible>#|()
Q|Protected|.R|/Protected.Nested.Nested<HIDDEN: /Protected.Nested.Nested is invisible>#|()
Q|Protected|.R|/Protected.Nested.Nested<HIDDEN: Protected.Nested is invisible>#|()
}
public open class Generic<T> : R|kotlin/Any| {
public constructor<T>(x: R|T|): R|Generic<T>| {
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtKeywordToken
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -103,6 +104,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val UNRESOLVED by object : DiagnosticGroup("Unresolved") {
val INVISIBLE_REFERENCE by error<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<Symbol>("reference")
parameter<Visibility>("visible")
parameter<ClassId?>("containingDeclaration")
}
val UNRESOLVED_REFERENCE by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<String>("reference")
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.lexer.KtKeywordToken
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
@@ -147,7 +148,7 @@ object FirErrors {
val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
// Unresolved
val INVISIBLE_REFERENCE by error1<PsiElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val INVISIBLE_REFERENCE by error3<PsiElement, FirBasedSymbol<*>, Visibility, ClassId?>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val UNRESOLVED_REFERENCE by error1<PsiElement, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val UNRESOLVED_LABEL by error0<PsiElement>(SourceElementPositioningStrategies.LABEL)
val DESERIALIZATION_ERROR by error0<PsiElement>()
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.toInvisibleReferenceDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
@@ -176,12 +177,7 @@ object FirDestructuringDeclarationChecker : FirPropertyChecker() {
}
}
is ConeVisibilityError -> {
reporter.reportOn(
property.source,
FirErrors.INVISIBLE_REFERENCE,
diagnostic.symbol,
context,
)
reporter.report(diagnostic.symbol.toInvisibleReferenceDiagnostic(property.source), context)
}
else -> error("Unhandled error during a component function call")
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirDeprecationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.toInvisibleReferenceDiagnostic
import org.jetbrains.kotlin.fir.analysis.getSourceForImportSegment
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
@@ -75,7 +76,7 @@ object FirImportsChecker : FirFileChecker() {
fun reportInvisibleParentClasses(classSymbol: FirRegularClassSymbol, depth: Int) {
if (!classSymbol.fir.isVisible(context)) {
val source = import.getSourceForImportSegment(indexFromLast = depth)
reporter.reportOn(source, FirErrors.INVISIBLE_REFERENCE, classSymbol, context)
reporter.report(classSymbol.toInvisibleReferenceDiagnostic(source), context)
}
classSymbol.classId.outerClassId?.resolveToClass(context)?.let { reportInvisibleParentClasses(it, depth + 1) }
@@ -87,7 +88,7 @@ object FirImportsChecker : FirFileChecker() {
ImportStatus.OK -> return
is ImportStatus.Invisible -> {
val source = import.getSourceForImportSegment(0)
reporter.reportOn(source, FirErrors.INVISIBLE_REFERENCE, status.symbol, context)
reporter.report(status.symbol.toInvisibleReferenceDiagnostic(source), context)
}
else -> {
val classId = parentClassSymbol.classId.createNestedClassId(importedName)
@@ -128,7 +129,7 @@ object FirImportsChecker : FirFileChecker() {
resolvedDeclaration?.let {
val source = import.getSourceForImportSegment(0) ?: import.source
reporter.reportOn(source, FirErrors.INVISIBLE_REFERENCE, it.symbol, context)
reporter.report(it.symbol.toInvisibleReferenceDiagnostic(source), context)
return
}
@@ -6,10 +6,9 @@
package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.toInvisibleReferenceDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.expressions.FirErrorResolvedQualifier
@@ -45,7 +44,7 @@ object FirVisibilityQualifierChecker : FirResolvedQualifierChecker() {
)
) {
if (expression !is FirErrorResolvedQualifier || expression.diagnostic !is ConeVisibilityError) {
reporter.reportOn(expression.source, FirErrors.INVISIBLE_REFERENCE, symbol, context)
reporter.report(symbol.toInvisibleReferenceDiagnostic(expression.source), context)
}
return
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.renderReadableWithFqNames
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
object FirDiagnosticRenderers {
@OptIn(SymbolInternals::class)
@@ -148,7 +149,10 @@ object FirDiagnosticRenderers {
}
val NAME_OF_CONTAINING_DECLARATION_OR_FILE = Renderer { symbol: CallableId ->
val classId = symbol.classId
NAME_OF_DECLARATION_OR_FILE.render(symbol.classId)
}
val NAME_OF_DECLARATION_OR_FILE = Renderer { classId: ClassId? ->
if (classId == null) {
"file"
} else {
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FUNC
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS_ON_NEWLINE_WITH_INDENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.MODULE_DATA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME_OF_CONTAINING_DECLARATION_OR_FILE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME_OF_DECLARATION_OR_FILE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_CLASS_OR_OBJECT_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_COLLECTION_OF_TYPES
@@ -704,7 +705,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(NULL_FOR_NONNULL_TYPE, "Null can not be a value of a non-null type")
// Unresolved
map.put(INVISIBLE_REFERENCE, "Symbol {0} is invisible", SYMBOL)
map.put(INVISIBLE_REFERENCE, "Cannot access ''{0}'': it is {1} in {2}", SYMBOL, VISIBILITY, NAME_OF_DECLARATION_OR_FILE)
map.put(
INVISIBLE_SETTER,
"Cannot access ''{0}'': it is {1} in {2}",
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isInfix
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
@@ -27,6 +28,7 @@ import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPo
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeExpectedTypeConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeLambdaArgumentConstraintPosition
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -40,6 +42,7 @@ import org.jetbrains.kotlin.types.EmptyIntersectionTypeKind
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
private fun ConeDiagnostic.toKtDiagnostic(
source: KtSourceElement,
@@ -73,7 +76,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
)
}
is ConeVisibilityError -> FirErrors.INVISIBLE_REFERENCE.createOn(source, this.symbol)
is ConeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(source)
is ConeInapplicableWrongReceiver -> FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER.createOn(source, this.candidateSymbols)
is ConeNoCompanionObject -> FirErrors.NO_COMPANION_OBJECT.createOn(source, this.candidateSymbol as FirClassLikeSymbol<*>)
is ConeAmbiguityError -> when {
@@ -150,6 +153,12 @@ private fun ConeDiagnostic.toKtDiagnostic(
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
}
fun FirBasedSymbol<*>.toInvisibleReferenceDiagnostic(source: KtSourceElement?): KtDiagnostic? = when (val symbol = this) {
is FirCallableSymbol<*> -> FirErrors.INVISIBLE_REFERENCE.createOn(source, symbol, symbol.visibility, symbol.callableId.classId)
is FirClassLikeSymbol<*> -> FirErrors.INVISIBLE_REFERENCE.createOn(source, symbol, symbol.visibility, symbol.classId.outerClassId)
else -> shouldNotBeCalled("Unexpected receiver $javaClass")
}
fun ConeDiagnostic.toFirDiagnostics(
session: FirSession,
source: KtSourceElement,
@@ -130,13 +130,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
supertypeSupplier
)
}
val parentClass = declaration.containingNonLocalClass(
session,
dispatchReceiver,
containingDeclarations,
supertypeSupplier
) ?: return true
return generateSequence(parentClass) { it.containingNonLocalClass(session) }.all { parent ->
return declaration.parentDeclarationSequence(session, dispatchReceiver, containingDeclarations, supertypeSupplier)?.all { parent ->
isSpecificDeclarationVisible(
parent,
session,
@@ -146,7 +140,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
isCallToPropertySetter,
supertypeSupplier
)
}
} ?: true
}
fun isVisibleForOverriding(
@@ -169,51 +163,6 @@ abstract class FirVisibilityChecker : FirSessionComponent {
else -> platformOverrideVisibilityCheck(symbolFromDerivedClass, candidateInBaseClass.symbol, candidateInBaseClass.visibility)
}
private fun FirMemberDeclaration.containingNonLocalClass(
session: FirSession,
dispatchReceiver: FirExpression?,
containingUseSiteDeclarations: List<FirDeclaration>,
supertypeSupplier: SupertypeSupplier
): FirClassLikeDeclaration? {
return when (this) {
is FirCallableDeclaration -> {
if (dispatchReceiver != null) {
val baseReceiverType = dispatchReceiverClassTypeOrNull()
if (baseReceiverType != null) {
dispatchReceiver.typeRef.coneType.findClassRepresentation(baseReceiverType, session)?.toSymbol(session)?.fir?.let {
return it
}
}
}
val containingLookupTag = this.containingClassLookupTag()
val containingClass = containingLookupTag?.toSymbol(session)?.fir
if (isStatic && containingClass != null) {
containingUseSiteDeclarations.firstNotNullOfOrNull {
if (it !is FirClass) return@firstNotNullOfOrNull null
it.takeIf { it.isSubclassOf(containingLookupTag, session, isStrict = false, supertypeSupplier) }
}?.let { return it }
}
containingClass
}
is FirClassLikeDeclaration -> containingNonLocalClass(session)
}
}
private fun FirClassLikeDeclaration.containingNonLocalClass(session: FirSession): FirClassLikeDeclaration? {
return when (this) {
is FirClass -> {
if (isLocal) return null
this.classId.outerClassId?.let { session.symbolProvider.getClassLikeSymbolByClassId(it)?.fir }
}
// Currently, type aliases are only top-level
is FirTypeAlias -> null
}
}
private fun isSpecificDeclarationVisible(
declaration: FirMemberDeclaration,
session: FirSession,
@@ -521,3 +470,65 @@ fun FirBasedSymbol<*>.getOwnerLookupTag(): ConeClassLikeLookupTag? {
fun FirBasedSymbol<*>.isVariableOrNamedFunction(): Boolean {
return this is FirVariableSymbol || this is FirNamedFunctionSymbol || this is FirPropertyAccessorSymbol
}
fun FirMemberDeclaration.parentDeclarationSequence(
session: FirSession,
dispatchReceiver: FirExpression?,
containingDeclarations: List<FirDeclaration>,
supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default,
): Sequence<FirClassLikeDeclaration>? {
val parentClass = containingNonLocalClass(
session,
dispatchReceiver,
containingDeclarations,
supertypeSupplier
) ?: return null
return generateSequence(parentClass) { it.containingNonLocalClass(session) }
}
private fun FirMemberDeclaration.containingNonLocalClass(
session: FirSession,
dispatchReceiver: FirExpression?,
containingUseSiteDeclarations: List<FirDeclaration>,
supertypeSupplier: SupertypeSupplier
): FirClassLikeDeclaration? {
return when (this) {
is FirCallableDeclaration -> {
if (dispatchReceiver != null) {
val baseReceiverType = dispatchReceiverClassTypeOrNull()
if (baseReceiverType != null) {
dispatchReceiver.typeRef.coneType.findClassRepresentation(baseReceiverType, session)?.toSymbol(session)?.fir?.let {
return it
}
}
}
val containingLookupTag = this.containingClassLookupTag()
val containingClass = containingLookupTag?.toSymbol(session)?.fir
if (isStatic && containingClass != null) {
containingUseSiteDeclarations.firstNotNullOfOrNull {
if (it !is FirClass) return@firstNotNullOfOrNull null
it.takeIf { it.isSubclassOf(containingLookupTag, session, isStrict = false, supertypeSupplier) }
}?.let { return it }
}
containingClass
}
is FirClassLikeDeclaration -> containingNonLocalClass(session)
}
}
private fun FirClassLikeDeclaration.containingNonLocalClass(session: FirSession): FirClassLikeDeclaration? {
return when (this) {
is FirClass -> {
if (isLocal) return null
this.classId.outerClassId?.let { session.symbolProvider.getClassLikeSymbolByClassId(it)?.fir }
}
// Currently, type aliases are only top-level
is FirTypeAlias -> null
}
}
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedErrorReference
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.calls.FirPropertyWithExplicitBackingFieldResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.PropertyStability
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
@@ -193,7 +190,7 @@ fun BodyResolveComponents.buildResolvedQualifierForClass(
typeArgumentsForQualifier: List<FirTypeProjection> = emptyList(),
diagnostic: ConeDiagnostic? = null,
nonFatalDiagnostics: List<ConeDiagnostic> = emptyList(),
annotations: List<FirAnnotation> = emptyList()
annotations: List<FirAnnotation> = emptyList(),
): FirResolvedQualifier {
val classId = regularClass.classId
@@ -370,7 +367,7 @@ private fun BodyResolveComponents.typeFromSymbol(symbol: FirBasedSymbol<*>, make
}
fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(
qualifiedAccessExpression: FirQualifiedAccessExpression
qualifiedAccessExpression: FirQualifiedAccessExpression,
): FirExpression {
val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(qualifiedAccessExpression)
?: return qualifiedAccessExpression
@@ -379,7 +376,7 @@ fun BodyResolveComponents.transformQualifiedAccessUsingSmartcastInfo(
}
fun BodyResolveComponents.transformWhenSubjectExpressionUsingSmartcastInfo(
whenSubjectExpression: FirWhenSubjectExpression
whenSubjectExpression: FirWhenSubjectExpression,
): FirExpression {
val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(whenSubjectExpression)
?: return whenSubjectExpression
@@ -388,7 +385,7 @@ fun BodyResolveComponents.transformWhenSubjectExpressionUsingSmartcastInfo(
}
fun BodyResolveComponents.transformDesugaredAssignmentValueUsingSmartcastInfo(
expression: FirDesugaredAssignmentValueReferenceExpression
expression: FirDesugaredAssignmentValueReferenceExpression,
): FirExpression {
val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(expression.expressionRef.value)
?: return expression
@@ -410,7 +407,7 @@ private fun FirSmartCastExpressionBuilder.applyResultTypeRef() {
private fun <T : FirExpression> BodyResolveComponents.transformExpressionUsingSmartcastInfo(
expression: T,
stability: PropertyStability,
typesFromSmartCast: MutableList<ConeKotlinType>
typesFromSmartCast: MutableList<ConeKotlinType>,
): FirSmartCastExpression? {
val smartcastStability = stability.impliedSmartcastStability
?: if (dataFlowAnalyzer.isAccessToUnstableLocalVariable(expression)) {
@@ -477,7 +474,7 @@ private fun <T : FirExpression> BodyResolveComponents.transformExpressionUsingSm
fun FirCheckedSafeCallSubject.propagateTypeFromOriginalReceiver(
nullableReceiverExpression: FirExpression,
session: FirSession,
file: FirFile
file: FirFile,
) {
// If the receiver expression is smartcast to `null`, it would have `Nothing?` as its type, which may not have members called by user
// code. Hence, we fallback to the type before intersecting with `Nothing?`.
@@ -555,7 +552,7 @@ fun FirFunction.getAsForbiddenNamedArgumentsTarget(
session: FirSession,
// NB: with originScope given this function will try to find overridden declaration with allowed parameter names
// for intersection/substitution overrides
originScope: FirTypeScope? = null
originScope: FirTypeScope? = null,
): ForbiddenNamedArgumentsTarget? {
if (hasStableParameterNames) return null
@@ -602,11 +599,36 @@ fun FirExpression?.isIntegerLiteralOrOperatorCall(): Boolean {
fun createConeDiagnosticForCandidateWithError(
applicability: CandidateApplicability,
candidate: Candidate
candidate: Candidate,
): ConeDiagnostic {
return when (applicability) {
CandidateApplicability.HIDDEN -> ConeHiddenCandidateError(candidate)
CandidateApplicability.K2_VISIBILITY_ERROR -> ConeVisibilityError(candidate.symbol)
CandidateApplicability.K2_VISIBILITY_ERROR -> {
val session = candidate.callInfo.session
val declaration = candidate.symbol.fir
if (declaration is FirMemberDeclaration &&
session.visibilityChecker.isVisible(declaration, candidate, skipCheckForContainingClassVisibility = true)
) {
// We can have declarations that are visible by themselves, but some containing declaration is invisible.
// We report the nearest invisible containing declaration, otherwise we'll get a confusing diagnostic like
// Cannot access 'foo', it is public in 'Bar'.
declaration
.parentDeclarationSequence(session, candidate.dispatchReceiver, candidate.callInfo.containingDeclarations)
?.firstOrNull {
!session.visibilityChecker.isVisible(
it,
session,
candidate.callInfo.containingFile,
candidate.callInfo.containingDeclarations,
dispatchReceiver = null,
skipCheckForContainingClassVisibility = true,
)
}?.let {
return ConeVisibilityError(it.symbol)
}
}
ConeVisibilityError(candidate.symbol)
}
CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER -> ConeInapplicableWrongReceiver(listOf(candidate))
CandidateApplicability.K2_NO_COMPANION_OBJECT -> ConeNoCompanionObject(candidate)
else -> ConeInapplicableCandidateError(applicability, candidate)
@@ -29,7 +29,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.runIf
fun FirVisibilityChecker.isVisible(
declaration: FirMemberDeclaration,
callInfo: CallInfo,
dispatchReceiver: FirExpression?
dispatchReceiver: FirExpression?,
skipCheckForContainingClassVisibility: Boolean = false,
): Boolean {
val staticQualifierForCallable = runIf(
declaration is FirCallableDeclaration &&
@@ -50,28 +51,30 @@ fun FirVisibilityChecker.isVisible(
callInfo.containingDeclarations,
dispatchReceiver,
staticQualifierClassForCallable = staticQualifierForCallable,
isCallToPropertySetter = callInfo.callSite is FirVariableAssignment
isCallToPropertySetter = callInfo.callSite is FirVariableAssignment,
skipCheckForContainingClassVisibility = skipCheckForContainingClassVisibility,
)
}
fun FirVisibilityChecker.isVisible(
declaration: FirMemberDeclaration,
candidate: Candidate
candidate: Candidate,
skipCheckForContainingClassVisibility: Boolean = false,
): Boolean {
val callInfo = candidate.callInfo
if (!isVisible(declaration, callInfo, candidate.dispatchReceiver)) {
if (!isVisible(declaration, callInfo, candidate.dispatchReceiver, skipCheckForContainingClassVisibility)) {
val dispatchReceiverWithoutSmartCastType =
removeSmartCastTypeForAttemptToFitVisibility(candidate.dispatchReceiver, candidate.callInfo.session) ?: return false
if (!isVisible(declaration, callInfo, dispatchReceiverWithoutSmartCastType)) return false
if (!isVisible(declaration, callInfo, dispatchReceiverWithoutSmartCastType, skipCheckForContainingClassVisibility)) return false
candidate.dispatchReceiver = dispatchReceiverWithoutSmartCastType
}
val backingField = declaration.getBackingFieldIfApplicable()
if (backingField != null) {
candidate.hasVisibleBackingField = isVisible(backingField, callInfo, candidate.dispatchReceiver)
candidate.hasVisibleBackingField = isVisible(backingField, callInfo, candidate.dispatchReceiver, skipCheckForContainingClassVisibility)
}
return true
@@ -0,0 +1,46 @@
/b.kt:34:10: error: cannot access 'B': it is private in file
import a.B
^
/b.kt:35:10: error: cannot access 'B': it is private in file
import a.B.Companion.bar
^
/b.kt:37:12: error: cannot access 'Companion': it is private in 'C'
import a.C.Companion.baz
^
/b.kt:38:10: error: cannot access 'D': it is private in file
import a.D
^
/b.kt:39:10: error: cannot access 'D': it is private in file
import a.D.Companion.quux
^
/b.kt:39:12: error: cannot access 'Companion': it is private in 'D'
import a.D.Companion.quux
^
/b.kt:43:7: error: cannot access 'B': it is private in file
f(B)
^
/b.kt:44:7: error: cannot access 'Companion': it is private in 'C'
f(C)
^
/b.kt:45:7: error: cannot access 'Companion': it is private in 'D'
f(D)
^
/b.kt:48:5: error: cannot access 'B': it is private in file
B.bar()
^
/b.kt:48:7: error: cannot access 'B': it is private in file
B.bar()
^
/b.kt:49:7: error: cannot access 'Companion': it is private in 'C'
C.baz()
^
/b.kt:50:5: error: cannot access 'D': it is private in file
D.quux()
^
/b.kt:50:7: error: cannot access 'Companion': it is private in 'D'
D.quux()
^
/b.kt:53:9: error: cannot access 'Companion': it is private in 'C'
a.C.baz()
^
@@ -0,0 +1,27 @@
/b.kt:(96,97): error: Cannot access 'class B : Any': it is private in file
/b.kt:(107,108): error: Cannot access 'class B : Any': it is private in file
/b.kt:(145,154): error: Cannot access 'companion object Companion : Any': it is private in 'a/C'
/b.kt:(168,169): error: Cannot access 'class D : Any': it is private in file
/b.kt:(179,180): error: Cannot access 'class D : Any': it is private in file
/b.kt:(181,190): error: Cannot access 'companion object Companion : Any': it is private in 'a/D'
/b.kt:(225,226): error: Cannot access 'class B : Any': it is private in file
/b.kt:(243,244): error: Cannot access 'class D : Any': it is private in file
/b.kt:(263,264): error: Cannot access 'class B : Any': it is private in file
/b.kt:(265,268): error: Cannot access 'class B : Any': it is private in file
/b.kt:(277,280): error: Cannot access 'companion object Companion : Any': it is private in 'a/C'
/b.kt:(287,288): error: Cannot access 'class D : Any': it is private in file
/b.kt:(289,293): error: Cannot access 'companion object Companion : Any': it is private in 'a/D'
/b.kt:(319,322): error: Cannot access 'companion object Companion : Any': it is private in 'a/C'
@@ -1,3 +1,4 @@
// RENDER_DIAGNOSTICS_FULL_TEXT
//FILE:a.kt
package a
@@ -29,9 +30,13 @@ private class D {
package b
import a.A
import a.A.Companion.foo
import a.<!INVISIBLE_REFERENCE!>B<!>
import a.<!INVISIBLE_REFERENCE!>B<!>.Companion.bar
import a.C
import a.C.<!INVISIBLE_REFERENCE!>Companion<!>.baz
import a.<!INVISIBLE_REFERENCE!>D<!>
import a.<!INVISIBLE_REFERENCE!>D<!>.<!INVISIBLE_REFERENCE!>Companion<!>.quux
fun test() {
f(A)
@@ -1,3 +1,4 @@
// RENDER_DIAGNOSTICS_FULL_TEXT
//FILE:a.kt
package a
@@ -29,9 +30,13 @@ private class D {
package b
import a.A
import a.A.Companion.foo
import a.<!INVISIBLE_REFERENCE!>B<!>
import a.<!INVISIBLE_REFERENCE!>B<!>.Companion.bar
import a.C
import a.C.<!INVISIBLE_REFERENCE!>Companion<!>.baz
import a.<!INVISIBLE_REFERENCE!>D<!>
import a.<!INVISIBLE_REFERENCE!>D<!>.<!INVISIBLE_REFERENCE!>Companion<!>.quux
fun test() {
f(A)