[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:
committed by
Space Team
parent
6a073e0b17
commit
911e62257a
+2
@@ -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,
|
||||
)
|
||||
|
||||
+2
@@ -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> {
|
||||
|
||||
+2
@@ -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
|
||||
|
||||
+1
-1
@@ -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'>
|
||||
+1
-1
@@ -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
-1
@@ -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:
|
||||
|
||||
+3
-3
@@ -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|()
|
||||
|
||||
+2
-2
@@ -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>#|()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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>| {
|
||||
|
||||
+3
@@ -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")
|
||||
|
||||
+2
-1
@@ -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>()
|
||||
|
||||
+2
-6
@@ -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")
|
||||
}
|
||||
|
||||
+4
-3
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -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
|
||||
|
||||
+5
-1
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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}",
|
||||
|
||||
+10
-1
@@ -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
|
||||
|
||||
+46
@@ -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()
|
||||
^
|
||||
|
||||
+27
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user