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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user