FIR. Add visibility/deprecation filtering to type resolve
This commit is contained in:
@@ -19,6 +19,7 @@ abstract class FirTypeResolver : FirSessionComponent {
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
areBareTypesAllowed: Boolean,
|
||||
isOperandOfIsOperator: Boolean,
|
||||
// Note: sometimes we don't have useSiteFile in IDE context
|
||||
useSiteFile: FirFile?,
|
||||
supertypeSupplier: SupertypeSupplier
|
||||
): Pair<ConeKotlinType, ConeDiagnostic?>
|
||||
|
||||
+101
-67
@@ -29,11 +29,11 @@ import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
|
||||
import org.jetbrains.kotlin.fir.visibilityChecker
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
|
||||
|
||||
@ThreadSafeMutableState
|
||||
class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
@@ -98,74 +98,81 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveToSymbol(
|
||||
typeRef: FirTypeRef,
|
||||
fun resolveUserTypeToSymbol(
|
||||
typeRef: FirUserTypeRef,
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
useSiteFile: FirFile?,
|
||||
supertypeSupplier: SupertypeSupplier
|
||||
): Triple<FirBasedSymbol<*>?, ConeSubstitutor?, ConeDiagnostic?> {
|
||||
return when (typeRef) {
|
||||
is FirResolvedTypeRef -> {
|
||||
val resultSymbol = typeRef.coneTypeSafe<ConeLookupTagBasedType>()?.lookupTag?.let(symbolProvider::getSymbolByLookupTag)
|
||||
Triple(resultSymbol, null, null)
|
||||
): TypeResolutionResult {
|
||||
val qualifierResolver = session.qualifierResolver
|
||||
var applicability: CandidateApplicability? = null
|
||||
|
||||
val candidates = mutableSetOf<TypeCandidate>()
|
||||
val qualifier = typeRef.qualifier
|
||||
val scopes = scopeClassDeclaration.scopes
|
||||
val containingDeclarations = scopeClassDeclaration.containingDeclarations
|
||||
|
||||
fun processCandidate(symbol: FirBasedSymbol<*>, substitutor: ConeSubstitutor?) {
|
||||
var symbolApplicability = CandidateApplicability.RESOLVED
|
||||
var diagnostic: ConeDiagnostic? = null
|
||||
|
||||
if (!symbol.isVisible(useSiteFile, containingDeclarations, supertypeSupplier)) {
|
||||
symbolApplicability = minOf(CandidateApplicability.VISIBILITY_ERROR, symbolApplicability)
|
||||
diagnostic = ConeVisibilityError(symbol)
|
||||
}
|
||||
|
||||
is FirUserTypeRef -> {
|
||||
val qualifierResolver = session.qualifierResolver
|
||||
var acceptedSymbol: FirBasedSymbol<*>? = null
|
||||
var substitutor: ConeSubstitutor? = null
|
||||
val notApplicableSymbols = mutableListOf<Pair<FirBasedSymbol<*>, ConeSubstitutor>>()
|
||||
val qualifier = typeRef.qualifier
|
||||
val scopes = scopeClassDeclaration.scopes
|
||||
val containingDeclarations = scopeClassDeclaration.containingDeclarations
|
||||
|
||||
for (scope in scopes) {
|
||||
if (acceptedSymbol != null) {
|
||||
break
|
||||
}
|
||||
val collectNonApplicable = notApplicableSymbols.isEmpty()
|
||||
scope.processClassifiersByNameWithSubstitution(qualifier.first().name) { symbol, substitutorFromScope ->
|
||||
if (acceptedSymbol != null) return@processClassifiersByNameWithSubstitution
|
||||
val resolvedSymbol = resolveSymbol(symbol, qualifier, qualifierResolver)
|
||||
?: return@processClassifiersByNameWithSubstitution
|
||||
|
||||
if (resolvedSymbol.isVisible(useSiteFile, containingDeclarations, supertypeSupplier)) {
|
||||
acceptedSymbol = resolvedSymbol
|
||||
substitutor = substitutorFromScope
|
||||
} else if (collectNonApplicable) {
|
||||
resolvedSymbol.isVisible(useSiteFile, containingDeclarations, supertypeSupplier)
|
||||
notApplicableSymbols += resolvedSymbol to substitutorFromScope
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
acceptedSymbol != null -> Triple(acceptedSymbol, substitutor, null)
|
||||
|
||||
notApplicableSymbols.size == 1 -> {
|
||||
val (notApplicableSymbol, resultingSubstitutor) = notApplicableSymbols.single()
|
||||
Triple(notApplicableSymbol, resultingSubstitutor, ConeVisibilityError(notApplicableSymbol))
|
||||
}
|
||||
|
||||
else -> {
|
||||
val symbolFromQualifier = qualifierResolver.resolveSymbol(qualifier)
|
||||
val diagnostic = runIf(
|
||||
symbolFromQualifier != null &&
|
||||
!symbolFromQualifier.isVisible(useSiteFile, containingDeclarations, supertypeSupplier)
|
||||
) {
|
||||
ConeVisibilityError(symbolFromQualifier!!)
|
||||
}
|
||||
Triple(symbolFromQualifier, null, diagnostic)
|
||||
}
|
||||
}
|
||||
val deprecation = symbol.getDeprecation(useSiteFile)
|
||||
if (deprecation != null && deprecation.deprecationLevel == DeprecationLevelValue.HIDDEN) {
|
||||
symbolApplicability = minOf(CandidateApplicability.HIDDEN, symbolApplicability)
|
||||
diagnostic = null
|
||||
}
|
||||
|
||||
is FirImplicitBuiltinTypeRef -> {
|
||||
Triple(resolveBuiltInQualified(typeRef.id, session), null, null)
|
||||
if (applicability == null || symbolApplicability > applicability!!) {
|
||||
applicability = symbolApplicability
|
||||
candidates.clear()
|
||||
}
|
||||
if (symbolApplicability == applicability) {
|
||||
candidates.add(TypeCandidate(symbol, substitutor, diagnostic))
|
||||
}
|
||||
|
||||
else -> Triple(null, null, null)
|
||||
}
|
||||
|
||||
for (scope in scopes) {
|
||||
if (applicability == CandidateApplicability.RESOLVED) break
|
||||
scope.processClassifiersByNameWithSubstitution(qualifier.first().name) { symbol, substitutorFromScope ->
|
||||
val resolvedSymbol = resolveSymbol(symbol, qualifier, qualifierResolver)
|
||||
?: return@processClassifiersByNameWithSubstitution
|
||||
|
||||
processCandidate(resolvedSymbol, substitutorFromScope)
|
||||
}
|
||||
}
|
||||
|
||||
if (applicability != CandidateApplicability.RESOLVED) {
|
||||
val symbol = qualifierResolver.resolveSymbol(qualifier)
|
||||
if (symbol != null) {
|
||||
processCandidate(symbol, null)
|
||||
}
|
||||
}
|
||||
|
||||
val candidateCount = candidates.size
|
||||
return when {
|
||||
candidateCount == 1 -> {
|
||||
val candidate = candidates.single()
|
||||
TypeResolutionResult.Resolved(candidate)
|
||||
}
|
||||
candidateCount > 1 -> {
|
||||
TypeResolutionResult.Ambiguity
|
||||
}
|
||||
candidateCount == 0 -> {
|
||||
TypeResolutionResult.Unresolved
|
||||
}
|
||||
else -> error("Unexpected")
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TypeResolutionResult {
|
||||
object Ambiguity : TypeResolutionResult()
|
||||
object Unresolved : TypeResolutionResult()
|
||||
class Resolved(val typeCandidate: TypeCandidate) : TypeResolutionResult()
|
||||
}
|
||||
|
||||
private fun resolveLocalClassChain(symbol: FirClassLikeSymbol<*>, qualifier: List<FirQualifierPart>): FirRegularClassSymbol? {
|
||||
@@ -210,12 +217,20 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun resolveUserType(
|
||||
typeRef: FirUserTypeRef,
|
||||
symbol: FirBasedSymbol<*>?,
|
||||
substitutor: ConeSubstitutor?,
|
||||
result: TypeResolutionResult,
|
||||
areBareTypesAllowed: Boolean,
|
||||
topContainer: FirDeclaration?,
|
||||
isOperandOfIsOperator: Boolean
|
||||
): ConeKotlinType {
|
||||
|
||||
val (symbol, substitutor) = when (result) {
|
||||
is TypeResolutionResult.Resolved -> {
|
||||
result.typeCandidate.symbol to result.typeCandidate.substitutor
|
||||
}
|
||||
TypeResolutionResult.Ambiguity -> null to null
|
||||
TypeResolutionResult.Unresolved -> null to null
|
||||
}
|
||||
|
||||
if (symbol == null || symbol !is FirClassifierSymbol<*>) {
|
||||
val diagnostic = if (symbol?.fir is FirEnumEntry) {
|
||||
if (isOperandOfIsOperator) {
|
||||
@@ -473,15 +488,14 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
return when (typeRef) {
|
||||
is FirResolvedTypeRef -> error("Do not resolve, resolved type-refs")
|
||||
is FirUserTypeRef -> {
|
||||
val (symbol, substitutor, diagnostic) = resolveToSymbol(typeRef, scopeClassDeclaration, useSiteFile, supertypeSupplier)
|
||||
val result = resolveUserTypeToSymbol(typeRef, scopeClassDeclaration, useSiteFile, supertypeSupplier)
|
||||
resolveUserType(
|
||||
typeRef,
|
||||
symbol,
|
||||
substitutor,
|
||||
result,
|
||||
areBareTypesAllowed,
|
||||
scopeClassDeclaration.topContainer ?: scopeClassDeclaration.containingDeclarations.lastOrNull(),
|
||||
isOperandOfIsOperator
|
||||
) to diagnostic
|
||||
) to (result as? TypeResolutionResult.Resolved)?.typeCandidate?.diagnostic
|
||||
}
|
||||
is FirFunctionTypeRef -> createFunctionalType(typeRef) to null
|
||||
is FirDynamicTypeRef -> ConeErrorType(ConeUnsupportedDynamicType()) to null
|
||||
@@ -499,4 +513,24 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
||||
else -> error(typeRef.render())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TypeCandidate(
|
||||
val symbol: FirBasedSymbol<*>,
|
||||
val substitutor: ConeSubstitutor?,
|
||||
val diagnostic: ConeDiagnostic?
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is TypeCandidate) return false
|
||||
|
||||
if (symbol != other.symbol) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return symbol.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
}
|
||||
|
||||
delegatedTypeRef = typeRef
|
||||
type = resolvedType
|
||||
type = resolvedType
|
||||
|
||||
this.diagnostic = resolvedType.diagnostic
|
||||
}
|
||||
|
||||
Vendored
+5
-5
@@ -22,16 +22,16 @@ class A {
|
||||
import p1.*
|
||||
import p2.*
|
||||
|
||||
fun test(a: <!UNRESOLVED_REFERENCE!>A<!>) {
|
||||
<!CONFLICTING_OVERLOADS!>fun test(a: A)<!> {
|
||||
a.<!UNRESOLVED_REFERENCE!>m1<!>()
|
||||
a.<!UNRESOLVED_REFERENCE!>m2<!>()
|
||||
a.m2()
|
||||
}
|
||||
|
||||
// FILE: explicitlyImportP1.kt
|
||||
import <!DEPRECATION_ERROR!>p1.A<!>
|
||||
import p2.*
|
||||
|
||||
fun test(a: <!DEPRECATION_ERROR!>A<!>) {
|
||||
a.m1()
|
||||
a.<!UNRESOLVED_REFERENCE!>m2<!>()
|
||||
<!CONFLICTING_OVERLOADS!>fun test(a: A)<!> {
|
||||
a.<!UNRESOLVED_REFERENCE!>m1<!>()
|
||||
a.m2()
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -28,9 +28,9 @@ import p1.*
|
||||
import <!DEPRECATION_ERROR!>p2.A<!>
|
||||
import p3.*
|
||||
|
||||
fun test(a: <!DEPRECATION_ERROR!>A<!>) {
|
||||
<!CONFLICTING_OVERLOADS!>fun test(a: <!UNRESOLVED_REFERENCE!>A<!>)<!> {
|
||||
a.<!UNRESOLVED_REFERENCE!>v1<!>
|
||||
a.v2
|
||||
a.<!UNRESOLVED_REFERENCE!>v2<!>
|
||||
a.<!UNRESOLVED_REFERENCE!>v3<!>
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import p1.*
|
||||
import p2.*
|
||||
import p3.*
|
||||
|
||||
fun test(a: <!UNRESOLVED_REFERENCE!>A<!>) {
|
||||
<!CONFLICTING_OVERLOADS!>fun test(a: <!UNRESOLVED_REFERENCE!>A<!>)<!> {
|
||||
a.<!UNRESOLVED_REFERENCE!>v1<!>
|
||||
a.<!UNRESOLVED_REFERENCE!>v2<!>
|
||||
a.<!UNRESOLVED_REFERENCE!>v3<!>
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@ fun <<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> Pair() {}
|
||||
|
||||
class P<<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> {}
|
||||
|
||||
val <<!REDECLARATION!>T<!>, <!REDECLARATION!>T<!>> T.foo : Int
|
||||
val <<!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>, <!REDECLARATION, TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER!>T<!>> <!UNRESOLVED_REFERENCE!>T<!>.foo : Int
|
||||
get() = 1
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: main1.kt
|
||||
package abc1
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main2.kt
|
||||
package abc2
|
||||
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main3.kt
|
||||
package abc3
|
||||
|
||||
import kotlin.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main4.kt
|
||||
package abc4
|
||||
|
||||
import kotlin.<!CONFLICTING_IMPORT!>Throws<!>
|
||||
import kotlin.jvm.<!CONFLICTING_IMPORT!>Throws<!>
|
||||
|
||||
@<!UNRESOLVED_REFERENCE!>Throws<!>(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: <!UNRESOLVED_REFERENCE!>Throws<!>) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main5.kt
|
||||
package abc5
|
||||
|
||||
import kotlin.jvm.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main6.kt
|
||||
package abc6
|
||||
|
||||
import kotlin.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main7.kt
|
||||
package abc7
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.jvm.*
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main8.kt
|
||||
package abc8
|
||||
|
||||
import kotlin.*
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
|
||||
// FILE: main9.kt
|
||||
package abc9
|
||||
|
||||
import kotlin.jvm.*
|
||||
import kotlin.Throws
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun foo1() {}
|
||||
|
||||
@kotlin.Throws(Exception::class)
|
||||
fun foo2() {}
|
||||
|
||||
@kotlin.jvm.Throws(Exception::class)
|
||||
fun foo3() {}
|
||||
|
||||
fun foo5(x: Throws) {}
|
||||
fun foo6(x: kotlin.Throws) {}
|
||||
fun foo7(x: kotlin.jvm.Throws) {}
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// FILE: main1.kt
|
||||
package abc1
|
||||
|
||||
Reference in New Issue
Block a user