FIR. Add visibility/deprecation filtering to type resolve

This commit is contained in:
Simon Ogorodnik
2022-02-22 23:15:24 +03:00
committed by Space
parent f5b49e48ad
commit 4ba5a55626
8 changed files with 276 additions and 78 deletions
@@ -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?>
@@ -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()
}
}
}
@@ -145,7 +145,7 @@ class FirSpecificTypeResolverTransformer(
}
delegatedTypeRef = typeRef
type = resolvedType
type = resolvedType
this.diagnostic = resolvedType.diagnostic
}
@@ -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()
}
@@ -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<!>
@@ -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