[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:
Ilya Kirillov
2023-05-27 15:11:20 +02:00
committed by Space Team
parent 51cf0ce3e5
commit 481962d5f6
42 changed files with 600 additions and 33 deletions
@@ -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");
@@ -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!><!>) {}
@@ -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| {
}
@@ -0,0 +1,3 @@
class A<T>
fun foo(a : A<<!UNRESOLVED_REFERENCE!>Unresolved<!>>.<!SYNTAX!><!>) {}
@@ -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 {
@@ -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 {
@@ -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()
}
}
}
}
@@ -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
}