[LL FIR] get rid of on-air resolve of partial type qualifiers
It's very slow and leads to performance problems (see KT-58125) Instead, we do the following: - For a fully resolved type qualifier, when we want to resolve its part, we are looking for the corresponding symbol by traversing nested classes bottom up. - For an error qualifier, we are trying to resolve the maximum possible qualifier in the types transformer where all the type scopes are already available. ^KT-58125 fixed
This commit is contained in:
committed by
Space Team
parent
51cf0ce3e5
commit
481962d5f6
+10
@@ -3326,6 +3326,16 @@ public class LazyBodyIsNotTouchedTestGenerated extends AbstractLazyBodyIsNotTouc
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incompleteUserType.kt")
|
||||
public void testIncompleteUserType() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incompleteUserTypeWithUnresovledTypeArgument.kt")
|
||||
public void testIncompleteUserTypeWithUnresovledTypeArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserTypeWithUnresovledTypeArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerClassHierarchy.kt")
|
||||
public void testInnerClassHierarchy() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/innerClassHierarchy.kt");
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
FILE: incompleteUserType.kt
|
||||
public final fun foo(a: <ERROR TYPE REF: Incomplete user type>): R|kotlin/Unit| {
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(a : <!UNRESOLVED_REFERENCE!>Unresolved<!>.<!SYNTAX!><!>) {}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
FILE: incompleteUserTypeWithUnresovledTypeArgument.kt
|
||||
public final class A<T> : R|kotlin/Any| {
|
||||
public constructor<T>(): R|A<T>| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final fun foo(a: <ERROR TYPE REF: Incomplete user type>): R|kotlin/Unit| {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class A<T>
|
||||
|
||||
fun foo(a : A<<!UNRESOLVED_REFERENCE!>Unresolved<!>>.<!SYNTAX!><!>) {}
|
||||
+12
@@ -3781,6 +3781,18 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incompleteUserType.kt")
|
||||
public void testIncompleteUserType() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incompleteUserTypeWithUnresovledTypeArgument.kt")
|
||||
public void testIncompleteUserTypeWithUnresovledTypeArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserTypeWithUnresovledTypeArgument.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerClassHierarchy.kt")
|
||||
public void testInnerClassHierarchy() throws Exception {
|
||||
|
||||
+12
@@ -3781,6 +3781,18 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incompleteUserType.kt")
|
||||
public void testIncompleteUserType() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserType.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incompleteUserTypeWithUnresovledTypeArgument.kt")
|
||||
public void testIncompleteUserTypeWithUnresovledTypeArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/problems/incompleteUserTypeWithUnresovledTypeArgument.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerClassHierarchy.kt")
|
||||
public void testInnerClassHierarchy() throws Exception {
|
||||
|
||||
+11
-1
@@ -2168,11 +2168,21 @@ class DeclarationsConverter(
|
||||
}
|
||||
}
|
||||
|
||||
if (identifier == null)
|
||||
if (identifier == null) {
|
||||
return buildErrorTypeRef {
|
||||
source = typeRefSource
|
||||
diagnostic = ConeSyntaxDiagnostic("Incomplete user type")
|
||||
simpleFirUserType?.let { qualifierPart ->
|
||||
if (qualifierPart.qualifier.isNotEmpty()) {
|
||||
partiallyResolvedTypeRef = buildUserTypeRef {
|
||||
source = qualifierPart.qualifier.last().source
|
||||
isMarkedNullable = false
|
||||
this.qualifier.addAll(qualifierPart.qualifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val qualifierPart = FirQualifierPartImpl(
|
||||
identifierSource!!,
|
||||
|
||||
@@ -1979,6 +1979,17 @@ open class RawFirBuilder(
|
||||
FirErrorTypeRefBuilder().apply {
|
||||
this.source = source
|
||||
diagnostic = ConeSyntaxDiagnostic("Incomplete user type")
|
||||
|
||||
val qualifier = unwrappedElement.qualifier
|
||||
val reference = qualifier?.referenceExpression
|
||||
if (qualifier != null && reference != null) {
|
||||
partiallyResolvedTypeRef = convertKtTypeElement(
|
||||
qualifier.toFirSourceElement(),
|
||||
isNullable = false,
|
||||
qualifier,
|
||||
reference
|
||||
).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-1
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.fir.resolve.typeResolver
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildUserTypeRef
|
||||
|
||||
class FirSpecificTypeResolverTransformer(
|
||||
override val session: FirSession,
|
||||
@@ -82,7 +83,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
typeRef.transformChildren(this, data)
|
||||
}
|
||||
val (resolvedType, diagnostic) = resolveType(typeRef, data)
|
||||
return transformType(typeRef, resolvedType, diagnostic)
|
||||
return transformType(typeRef, resolvedType, diagnostic, data)
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
@@ -135,6 +136,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
typeRef: FirTypeRef,
|
||||
resolvedType: ConeKotlinType,
|
||||
diagnostic: ConeDiagnostic?,
|
||||
scopeClassDeclaration: ScopeClassDeclaration,
|
||||
): FirResolvedTypeRef {
|
||||
return when {
|
||||
resolvedType is ConeErrorType -> {
|
||||
@@ -154,6 +156,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
|
||||
delegatedTypeRef = typeRef
|
||||
type = resolvedType
|
||||
partiallyResolvedTypeRef = tryCalculatingPartiallyResolvedTypeRef(typeRef, scopeClassDeclaration)
|
||||
|
||||
this.diagnostic = resolvedType.diagnostic
|
||||
}
|
||||
@@ -164,6 +167,7 @@ class FirSpecificTypeResolverTransformer(
|
||||
this.diagnostic = diagnostic
|
||||
type = resolvedType
|
||||
delegatedTypeRef = typeRef
|
||||
partiallyResolvedTypeRef = tryCalculatingPartiallyResolvedTypeRef(typeRef, scopeClassDeclaration)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -177,6 +181,41 @@ class FirSpecificTypeResolverTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to calculate a partially resolved type reference for a type reference which was resolved to an error type.
|
||||
* It will attempt to resolve the type with a decreasing number of qualifiers until it succeeds, allowing
|
||||
* partial resolution in case of errors in the type reference.
|
||||
*
|
||||
* This is useful for providing better IDE support when resolving partially incorrect types.
|
||||
*
|
||||
* @param typeRef The type reference for which to try to calculate a partially resolved type reference.
|
||||
* @param data The scope class declaration containing relevant information for resolving the reference.
|
||||
* @return A partially resolved type reference if it was resolved, or `null` otherwise.
|
||||
*/
|
||||
private fun tryCalculatingPartiallyResolvedTypeRef(typeRef: FirTypeRef, data: ScopeClassDeclaration): FirTypeRef? {
|
||||
if (typeRef !is FirUserTypeRef) return null
|
||||
val qualifiers = typeRef.qualifier
|
||||
if (qualifiers.size <= 1) {
|
||||
return null
|
||||
}
|
||||
val qualifiersToTry = qualifiers.toMutableList()
|
||||
while (qualifiersToTry.size > 1) {
|
||||
qualifiersToTry.removeLast()
|
||||
val typeRefToTry = buildUserTypeRef {
|
||||
qualifier += qualifiersToTry
|
||||
isMarkedNullable = false
|
||||
}
|
||||
val (resolvedType, diagnostic) = resolveType(typeRefToTry, data)
|
||||
if (resolvedType is ConeErrorType || diagnostic != null) continue
|
||||
return buildResolvedTypeRef {
|
||||
source = qualifiersToTry.last().source
|
||||
type = resolvedType
|
||||
delegatedTypeRef = typeRefToTry
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.takeIfAcceptable(): ConeKotlinType? = this.takeUnless {
|
||||
!errorTypeAsResolved && it is ConeErrorType
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user