[FIR] Report INVISIBLE_REFERENCE on the first unresolved qualifier

#KT-61719 Fixed
This commit is contained in:
Kirill Rakhman
2023-09-08 14:34:12 +02:00
committed by Space Team
parent 2127b2ce68
commit 2566dabfce
24 changed files with 67 additions and 118 deletions
@@ -3,7 +3,7 @@ class A {
}
class B {
fun foo(<!EXPOSED_PARAMETER_TYPE!>value: <!INVISIBLE_REFERENCE!>A.AInner<!><!>) {
fun foo(<!EXPOSED_PARAMETER_TYPE!>value: A.<!INVISIBLE_REFERENCE!>AInner<!><!>) {
}
}
@@ -5,7 +5,7 @@ class A {
}
abstract class B {
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun <!EXPOSED_FUNCTION_RETURN_TYPE!>foo<!>(str: String): <!INVISIBLE_REFERENCE!>A.InnerA<!><!>
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!>fun <!EXPOSED_FUNCTION_RETURN_TYPE!>foo<!>(str: String): A.<!INVISIBLE_REFERENCE!>InnerA<!><!>
}
private enum class Some {
@@ -17,7 +17,7 @@ class Property {
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var var1: String<!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var var2: String<!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var var3: Int<!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var <!EXPOSED_PROPERTY_TYPE!>var4<!>: <!INVISIBLE_REFERENCE!>A.AInnerPrivate<!><!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var <!EXPOSED_PROPERTY_TYPE!>var4<!>: A.<!INVISIBLE_REFERENCE!>AInnerPrivate<!><!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var var5: A.AInnerPublic<!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var <!EXPOSED_PROPERTY_TYPE!>var6<!>: <!INVISIBLE_REFERENCE!>A.AInnerProtectedEnum<!><!>
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var <!EXPOSED_PROPERTY_TYPE!>var6<!>: A.<!INVISIBLE_REFERENCE!>AInnerProtectedEnum<!><!>
}
@@ -46,7 +46,7 @@ class Test3 : C.CPublicI, <!EXPOSED_SUPER_CLASS, FINAL_SUPERTYPE, SUPERTYPE_NOT_
}
class Test4 : E, <!INVISIBLE_REFERENCE!>A.AProtectedI<!> {
class Test4 : E, A.<!INVISIBLE_REFERENCE!>AProtectedI<!> {
}
@@ -2,7 +2,7 @@ class A {
private inner class Inner
}
class B {
<!TOPLEVEL_TYPEALIASES_ONLY!>typealias <!EXPOSED_TYPEALIAS_EXPANDED_TYPE!>AInner<!> = <!INVISIBLE_REFERENCE!>A.Inner<!><!>
<!TOPLEVEL_TYPEALIASES_ONLY!>typealias <!EXPOSED_TYPEALIAS_EXPANDED_TYPE!>AInner<!> = A.<!INVISIBLE_REFERENCE!>Inner<!><!>
inner class Inner
}
@@ -10,7 +10,7 @@ class C {
<!TOPLEVEL_TYPEALIASES_ONLY!>typealias BInner = B.Inner<!>
}
typealias <!EXPOSED_TYPEALIAS_EXPANDED_TYPE!>AInner0<!> = <!INVISIBLE_REFERENCE!>A.Inner<!>
typealias <!EXPOSED_TYPEALIAS_EXPANDED_TYPE!>AInner0<!> = A.<!INVISIBLE_REFERENCE!>Inner<!>
typealias BInner0 = B.Inner
private typealias MyString = String
@@ -15,7 +15,7 @@ public class Test1<T: <!EXPOSED_TYPE_PARAMETER_BOUND!>A<!>>
public class Test2<T: C, P: E>
// invalid, D is protected
public class Test3<T: E, P: <!EXPOSED_TYPE_PARAMETER_BOUND, INVISIBLE_REFERENCE!>C.D<!>>
public class Test3<T: E, P: <!EXPOSED_TYPE_PARAMETER_BOUND!>C.<!INVISIBLE_REFERENCE!>D<!><!>>
// valid, B is internal
internal class Test4<T: B>
@@ -32,4 +32,4 @@ public class Container : <!SUPERTYPE_NOT_INITIALIZED!>C<!> {
}
// invalid, A is private, B is internal, D is protected
public interface Test8<T: <!EXPOSED_TYPE_PARAMETER_BOUND!>A<!>, P: <!EXPOSED_TYPE_PARAMETER_BOUND!>B<!>, F: C, N: <!EXPOSED_TYPE_PARAMETER_BOUND, INVISIBLE_REFERENCE!>C.D<!>, M: E>
public interface Test8<T: <!EXPOSED_TYPE_PARAMETER_BOUND!>A<!>, P: <!EXPOSED_TYPE_PARAMETER_BOUND!>B<!>, F: C, N: <!EXPOSED_TYPE_PARAMETER_BOUND!>C.<!INVISIBLE_REFERENCE!>D<!><!>, M: E>
@@ -91,6 +91,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
)
}
is ConeTypeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(smallestUnresolvablePrefix.last().source)
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<*>)
@@ -175,7 +176,7 @@ private val KtFakeSourceElementKind.canBeIgnored: Boolean
get() = this == KtFakeSourceElementKind.DelegatingConstructorCall
|| this == KtFakeSourceElementKind.ErrorTypeRef
fun FirBasedSymbol<*>.toInvisibleReferenceDiagnostic(source: KtSourceElement?): KtDiagnostic? = when (val symbol = this) {
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")
@@ -20,8 +20,10 @@ import org.jetbrains.kotlin.fir.recordTypeLookup
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.FirTypeResolutionResult
import org.jetbrains.kotlin.fir.resolve.SupertypeSupplier
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeVisibilityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedTypeQualifierError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnsupportedDefaultValueInFunctionType
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeVisibilityError
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.typeResolver
@@ -148,16 +150,10 @@ class FirSpecificTypeResolverTransformer(
): FirResolvedTypeRef {
return when {
resolvedType is ConeErrorType -> {
buildErrorType(typeRef, resolvedType, scopeClassDeclaration)
buildErrorType(typeRef, resolvedType, resolvedType.diagnostic, scopeClassDeclaration)
}
diagnostic != null -> {
buildErrorTypeRef {
source = typeRef.source
this.diagnostic = diagnostic
type = resolvedType
delegatedTypeRef = typeRef
partiallyResolvedTypeRef = tryCalculatingPartiallyResolvedTypeRef(typeRef, scopeClassDeclaration)
}
buildErrorType(typeRef, resolvedType, diagnostic, scopeClassDeclaration)
}
else -> {
buildResolvedTypeRef {
@@ -172,11 +168,11 @@ class FirSpecificTypeResolverTransformer(
private fun buildErrorType(
typeRef: FirTypeRef,
resolvedType: ConeErrorType,
resolvedType: ConeKotlinType,
diagnostic: ConeDiagnostic,
scopeClassDeclaration: ScopeClassDeclaration,
): FirErrorTypeRef {
return buildErrorTypeRef {
var diagnostic = resolvedType.diagnostic
val typeRefSourceKind = typeRef.source?.kind
val diagnosticSource = (diagnostic as? ConeUnexpectedTypeArgumentsError)?.source
@@ -192,22 +188,45 @@ class FirSpecificTypeResolverTransformer(
delegatedTypeRef = typeRef
type = resolvedType
val partiallyResolvedTypeRef = tryCalculatingPartiallyResolvedTypeRef(typeRef, scopeClassDeclaration)
this.partiallyResolvedTypeRef = partiallyResolvedTypeRef
if (diagnostic is ConeUnresolvedTypeQualifierError) {
val totalQualifierCount = diagnostic.qualifiers.size
val resolvedQualifierCount = (partiallyResolvedTypeRef?.delegatedTypeRef as? FirUserTypeRef)?.qualifier?.size
?: calculatePartiallyResolvablePackageSegments(diagnostic.qualifiers)
val unresolvedQualifierCount = totalQualifierCount - resolvedQualifierCount
if (unresolvedQualifierCount > 1) {
diagnostic = ConeUnresolvedTypeQualifierError(diagnostic.qualifiers.dropLast(unresolvedQualifierCount - 1), false)
this.diagnostic = when {
diagnostic is ConeUnresolvedTypeQualifierError -> {
ConeUnresolvedTypeQualifierError(smallestUnresolvablePrefix(diagnostic.qualifiers, partiallyResolvedTypeRef), diagnostic.isNullable)
}
diagnostic is ConeVisibilityError && typeRef is FirUserTypeRef -> {
ConeTypeVisibilityError(diagnostic.symbol, smallestUnresolvablePrefix(typeRef.qualifier, partiallyResolvedTypeRef))
}
else -> diagnostic
}
}
}
this.diagnostic = diagnostic
/**
* Returns the smallest non-resolvable prefix of the given [qualifiers].
*
* Examples:
*
* - Given `A.B.C` and `A.B` can be resolved, then `A.B.C` will be returned
* - Given `A.B.C` and `A` cannot be resolved, then `A` will be returned
* - Given `a.b.C` and package `a` exists but package `a.b` doesn't exist, `a.b.` will be returned.
*/
private fun smallestUnresolvablePrefix(
qualifiers: List<FirQualifierPart>,
partiallyResolvedTypeRef: FirResolvedTypeRef?,
): List<FirQualifierPart> {
val totalQualifierCount = qualifiers.size
val resolvedQualifierCount = (partiallyResolvedTypeRef?.delegatedTypeRef as? FirUserTypeRef)?.qualifier?.size
?: calculatePartiallyResolvablePackageSegments(qualifiers)
val unresolvedQualifierCount = totalQualifierCount - resolvedQualifierCount
return if (unresolvedQualifierCount > 1) {
qualifiers.dropLast(unresolvedQualifierCount - 1)
} else {
qualifiers
}
}
@@ -104,12 +104,17 @@ class ConeHiddenCandidateError(
override val reason: String get() = "HIDDEN: ${describeSymbol(candidateSymbol)} is deprecated with DeprecationLevel.HIDDEN"
}
class ConeVisibilityError(
open class ConeVisibilityError(
override val symbol: FirBasedSymbol<*>
) : ConeDiagnosticWithSymbol<FirBasedSymbol<*>> {
override val reason: String get() = "HIDDEN: ${describeSymbol(symbol)} is invisible"
}
class ConeTypeVisibilityError(
symbol: FirBasedSymbol<*>,
val smallestUnresolvablePrefix: List<FirQualifierPart>,
) : ConeVisibilityError(symbol)
class ConeInapplicableWrongReceiver(override val candidates: Collection<AbstractCandidate>) : ConeDiagnosticWithCandidates {
override val reason: String
get() = "None of the following candidates is applicable because of receiver type mismatch: ${
@@ -1,14 +0,0 @@
package p
import p.Foo.Nested
open class Foo {
protected class Nested
}
class Bar: Foo() {
protected fun foo(): Nested? = null
}
private fun foo(): <!INVISIBLE_REFERENCE!>Nested<!>? = null
private fun bar(): <!INVISIBLE_REFERENCE!>p.Foo.Nested<!>? = null
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
package p
import p.Foo.Nested
@@ -1,7 +1,7 @@
// !CHECK_TYPE
@<!INVISIBLE_REFERENCE!>kotlin.internal.<!INVISIBLE_REFERENCE!>InlineOnly<!><!>
@kotlin.internal.<!INVISIBLE_REFERENCE, INVISIBLE_REFERENCE!>InlineOnly<!>
public inline fun <C, R> C.ifEmpty(f: () -> R): R where C : Collection<*>, C : R = if (isEmpty()) f() else this
public fun <T> listOf(t: T): List<T> = TODO()
@@ -1,32 +0,0 @@
// FILE: a/M.java
package a;
public class M {
public class Inner {
}
public static class Nested {
}
private class PrInner {
}
private static class PrNested {
}
}
// FILE: b.kt
package b
fun f() {
val c1: a.M.Inner
val c2: a.M.Nested
val c3: <!INVISIBLE_REFERENCE!>a.M.PrInner<!>
val c4: <!INVISIBLE_REFERENCE!>a.M.PrNested<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// FILE: a/M.java
package a;
@@ -1,19 +0,0 @@
// FILE: p/Foo.java
package p;
public class Foo {
protected static class Nested {}
}
// FILE: foo.kt
package a
import p.Foo
import p.Foo.Nested
class Bar : Foo() {
protected fun foo(): Nested? = null
}
private fun foo(): <!INVISIBLE_REFERENCE!>Nested<!>? = null
private fun bar(): <!INVISIBLE_REFERENCE!>p.Foo.Nested<!>? = null
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// FILE: p/Foo.java
package p;
@@ -16,4 +16,4 @@ class Bar : <!EXPOSED_SUPER_CLASS, INVISIBLE_REFERENCE, INVISIBLE_REFERENCE!>Foo
}
private fun foo(): <!INVISIBLE_REFERENCE!>Nested<!>? = null
private fun bar(): <!INVISIBLE_REFERENCE!>p.Foo.Nested<!>? = null
private fun bar(): p.<!INVISIBLE_REFERENCE!>Foo<!>.Nested? = null
@@ -30,7 +30,7 @@ fun test() {
<!INVISIBLE_REFERENCE!>foo<!>()
val u : <!INVISIBLE_REFERENCE!>A<!> = <!INVISIBLE_REFERENCE!>A<!>()
val a : <!INVISIBLE_REFERENCE!>java.util.Arrays.ArrayList<Int><!>;
val a : java.util.Arrays.<!INVISIBLE_REFERENCE!>ArrayList<!><Int>;
val po = <!INVISIBLE_REFERENCE!>PO<!>
}
@@ -30,5 +30,5 @@ fun baz() {
a.A3("") checkType { _<String>() }
val x: <!INVISIBLE_REFERENCE!>a.A2<!> = B("") // A2 is unresolved because it's private in file, OK
val x: a.<!INVISIBLE_REFERENCE!>A2<!> = B("") // A2 is unresolved because it's private in file, OK
}
@@ -6,7 +6,7 @@ private class C {
private typealias TA = C
private val test1: <!INVISIBLE_REFERENCE!>C<!> = <!INVISIBLE_REFERENCE!>C<!>()
private val test1co: <!INVISIBLE_REFERENCE!>C.Companion<!> = <!INITIALIZER_TYPE_MISMATCH, INVISIBLE_REFERENCE, NO_COMPANION_OBJECT!>C<!>
private val test1co: <!INVISIBLE_REFERENCE!>C<!>.Companion = <!INITIALIZER_TYPE_MISMATCH, INVISIBLE_REFERENCE, NO_COMPANION_OBJECT!>C<!>
private val test2: <!INVISIBLE_REFERENCE!>TA<!> = <!INVISIBLE_REFERENCE!>TA<!>()
private val test2co = <!INVISIBLE_REFERENCE!>TA<!>
@@ -18,7 +18,7 @@ private val test2co = TA
// FILE: file2.kt
private val test1: <!INVISIBLE_REFERENCE!>C<!> = <!INVISIBLE_REFERENCE!>C<!>()
private val test1co: <!INVISIBLE_REFERENCE!>C.Companion<!> = <!INVISIBLE_REFERENCE!>C<!>
private val test1co: <!INVISIBLE_REFERENCE!>C<!>.Companion = <!INVISIBLE_REFERENCE!>C<!>
private val test2: <!INVISIBLE_REFERENCE!>TA<!> = <!INVISIBLE_REFERENCE!>TA<!>()
private val test2co = <!INVISIBLE_REFERENCE!>TA<!>
@@ -8,6 +8,6 @@ private class Private {
import <!INVISIBLE_REFERENCE!>Private<!>.Public
private fun test_1(x: <!INVISIBLE_REFERENCE!>Private.Public<!>, y: <!INVISIBLE_REFERENCE!>Public<!>) {
private fun test_1(x: <!INVISIBLE_REFERENCE!>Private<!>.Public, y: <!INVISIBLE_REFERENCE!>Public<!>) {
}
@@ -1,15 +0,0 @@
// SKIP_TXT
// FILE: a.kt
package p
class FilteringSequence
// FILE: b.kt
package kotlin.sequences
import p.*
interface I {
val v1: FilteringSequence
val <!EXPOSED_PROPERTY_TYPE!>v2<!>: <!INVISIBLE_REFERENCE!>IndexingSequence<String><!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// SKIP_TXT
// FILE: a.kt
package p