From 4ba5a55626a1e02c8474cf1c31fd2508c86c6019 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 22 Feb 2022 23:15:24 +0300 Subject: [PATCH] FIR. Add visibility/deprecation filtering to type resolve --- .../kotlin/fir/resolve/FirTypeResolver.kt | 1 + .../providers/impl/FirTypeResolverImpl.kt | 168 +++++++++++------- .../FirSpecificTypeResolverTransformer.kt | 2 +- .../deprecatedHiddenImportPriority.fir.kt | 10 +- .../deprecatedHiddenMultipleClasses.fir.kt | 6 +- .../RedeclaredTypeParameters.fir.kt | 2 +- .../testsWithStdLib/annotations/throws.fir.kt | 164 +++++++++++++++++ .../testsWithStdLib/annotations/throws.kt | 1 - 8 files changed, 276 insertions(+), 78 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/throws.fir.kt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirTypeResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirTypeResolver.kt index 0a23e964f90..88fd608ec78 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirTypeResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirTypeResolver.kt @@ -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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt index 7ddbb7741fd..a6b4618ff3c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirTypeResolverImpl.kt @@ -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?, ConeSubstitutor?, ConeDiagnostic?> { - return when (typeRef) { - is FirResolvedTypeRef -> { - val resultSymbol = typeRef.coneTypeSafe()?.lookupTag?.let(symbolProvider::getSymbolByLookupTag) - Triple(resultSymbol, null, null) + ): TypeResolutionResult { + val qualifierResolver = session.qualifierResolver + var applicability: CandidateApplicability? = null + + val candidates = mutableSetOf() + 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, 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): 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() + } + } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSpecificTypeResolverTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSpecificTypeResolverTransformer.kt index 96c16c7d20a..aad08eef486 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSpecificTypeResolverTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirSpecificTypeResolverTransformer.kt @@ -145,7 +145,7 @@ class FirSpecificTypeResolverTransformer( } delegatedTypeRef = typeRef - type = resolvedType + type = resolvedType this.diagnostic = resolvedType.diagnostic } diff --git a/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenImportPriority.fir.kt b/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenImportPriority.fir.kt index 366b26b26b6..e6229f7a115 100644 --- a/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenImportPriority.fir.kt +++ b/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenImportPriority.fir.kt @@ -22,16 +22,16 @@ class A { import p1.* import p2.* -fun test(a: A) { +fun test(a: A) { a.m1() - a.m2() + a.m2() } // FILE: explicitlyImportP1.kt import p1.A import p2.* -fun test(a: A) { - a.m1() - a.m2() +fun test(a: A) { + a.m1() + a.m2() } diff --git a/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenMultipleClasses.fir.kt b/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenMultipleClasses.fir.kt index 0b117e87baf..7dcc9a702c6 100644 --- a/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenMultipleClasses.fir.kt +++ b/compiler/testData/diagnostics/tests/multimodule/hiddenClass/deprecatedHiddenMultipleClasses.fir.kt @@ -28,9 +28,9 @@ import p1.* import p2.A import p3.* -fun test(a: A) { +fun test(a: A) { a.v1 - a.v2 + a.v2 a.v3 } @@ -39,7 +39,7 @@ import p1.* import p2.* import p3.* -fun test(a: A) { +fun test(a: A) { a.v1 a.v2 a.v3 diff --git a/compiler/testData/diagnostics/tests/redeclarations/RedeclaredTypeParameters.fir.kt b/compiler/testData/diagnostics/tests/redeclarations/RedeclaredTypeParameters.fir.kt index 0199df540d7..2c1feba0c40 100644 --- a/compiler/testData/diagnostics/tests/redeclarations/RedeclaredTypeParameters.fir.kt +++ b/compiler/testData/diagnostics/tests/redeclarations/RedeclaredTypeParameters.fir.kt @@ -2,5 +2,5 @@ fun <T, T> Pair() {} class P<T, T> {} -val <T, T> T.foo : Int +val <T, T> T.foo : Int get() = 1 diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.fir.kt new file mode 100644 index 00000000000..fe98cd6b29a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.fir.kt @@ -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.Throws +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: 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) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt index fb974ebb414..841a2de9bae 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/throws.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER // FILE: main1.kt package abc1