[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
+70
@@ -2158,5 +2158,75 @@ public class Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exte
|
||||
public void testWrongNumberOfTypeArgumentsInSupertype() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/WrongNumberOfTypeArgumentsInSupertype.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class PartiallyUnresolvedTypeQualifier {
|
||||
@Test
|
||||
public void testAllFilesPresentInPartiallyUnresolvedTypeQualifier() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClasses.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClasses() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesWithDot.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
-7
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.references
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||
@@ -15,9 +16,12 @@ import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirPackageSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirSafe
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
|
||||
import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError
|
||||
import org.jetbrains.kotlin.fir.FirPackageDirective
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.ROOT_PREFIX_FOR_IDE_RESOLUTION_MODE
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildImport
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
@@ -28,10 +32,9 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnmatchedTypeArgumentsError
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.FirImportResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
||||
@@ -39,6 +42,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirExplicitSimpleImportingScope
|
||||
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -48,6 +52,7 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
|
||||
internal object FirReferenceResolveHelper {
|
||||
fun FirResolvedTypeRef.toTargetSymbol(session: FirSession, symbolBuilder: KtSymbolByFirBuilder): KtSymbol? {
|
||||
@@ -471,14 +476,62 @@ internal object FirReferenceResolveHelper {
|
||||
session: FirSession,
|
||||
symbolBuilder: KtSymbolByFirBuilder,
|
||||
): Collection<KtSymbol> {
|
||||
if (!expression.isPartOfUserTypeRefQualifier()) {
|
||||
return listOfNotNull(fir.toTargetSymbol(session, symbolBuilder))
|
||||
}
|
||||
|
||||
val isPossiblyPackage = fir is FirErrorTypeRef && expression.isPartOfUserTypeRefQualifier()
|
||||
if (fir is FirErrorTypeRef) {
|
||||
tryGettingSymbolFromPartiallyResolvedType(fir, expression, session, symbolBuilder).ifNotEmpty { return this }
|
||||
return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = true))
|
||||
}
|
||||
|
||||
val resultSymbol =
|
||||
if (isPossiblyPackage) getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = true)
|
||||
else fir.toTargetSymbol(session, symbolBuilder)
|
||||
fun unwrapType(type: PsiElement?): KtTypeElement = when (type) {
|
||||
is KtTypeReference -> unwrapType(type.typeElement)
|
||||
is KtNullableType -> type.innerType
|
||||
is KtTypeElement -> type
|
||||
is KtNameReferenceExpression -> unwrapType(type.parent)
|
||||
else -> unexpectedElementError<PsiElement>(type)
|
||||
} ?: unexpectedElementError<PsiElement>(type)
|
||||
|
||||
return listOfNotNull(resultSymbol)
|
||||
val ktTypeElementFromFirType = unwrapType(fir.psi)
|
||||
|
||||
val classifiersToSkip = expression.parents.takeWhile { it != ktTypeElementFromFirType }.count()
|
||||
var classifier: FirClassLikeSymbol<*>? = fir.type.toRegularClassSymbol(session)
|
||||
repeat(classifiersToSkip) {
|
||||
classifier = classifier?.getContainingClassSymbol(session)
|
||||
}
|
||||
|
||||
val firClassSymbol = classifier
|
||||
?: return listOfNotNull(getPackageSymbolFor(expression, symbolBuilder, forQualifiedType = true))
|
||||
return listOf(symbolBuilder.classifierBuilder.buildClassifierSymbol(firClassSymbol))
|
||||
}
|
||||
|
||||
private tailrec fun tryGettingSymbolFromPartiallyResolvedType(
|
||||
typeRef: FirTypeRef?,
|
||||
expression: KtSimpleNameExpression,
|
||||
session: FirSession,
|
||||
symbolBuilder: KtSymbolByFirBuilder,
|
||||
): Collection<KtSymbol> {
|
||||
return when (typeRef) {
|
||||
null -> emptyList()
|
||||
is FirErrorTypeRef -> {
|
||||
tryGettingSymbolFromPartiallyResolvedType(
|
||||
typeRef.partiallyResolvedTypeRef,
|
||||
expression,
|
||||
session,
|
||||
symbolBuilder,
|
||||
)
|
||||
}
|
||||
is FirResolvedTypeRef -> {
|
||||
return getSymbolsForResolvedTypeRef(typeRef, expression, session, symbolBuilder)
|
||||
}
|
||||
else -> throwUnexpectedFirElementError(
|
||||
typeRef,
|
||||
ktElement = null,
|
||||
FirErrorTypeRef::class,
|
||||
FirResolvedTypeRef::class
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSymbolsForResolvedQualifier(
|
||||
|
||||
+70
@@ -2158,5 +2158,75 @@ public class FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated ex
|
||||
public void testWrongNumberOfTypeArgumentsInSupertype() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/WrongNumberOfTypeArgumentsInSupertype.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class PartiallyUnresolvedTypeQualifier {
|
||||
@Test
|
||||
public void testAllFilesPresentInPartiallyUnresolvedTypeQualifier() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClasses.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClasses() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesWithDot.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -2158,5 +2158,75 @@ public class FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exten
|
||||
public void testWrongNumberOfTypeArgumentsInSupertype() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/WrongNumberOfTypeArgumentsInSupertype.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class PartiallyUnresolvedTypeQualifier {
|
||||
@Test
|
||||
public void testAllFilesPresentInPartiallyUnresolvedTypeQualifier() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClasses.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClasses() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesWithDot.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -2158,5 +2158,75 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerate
|
||||
public void testWrongNumberOfTypeArgumentsInSupertype() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/WrongNumberOfTypeArgumentsInSupertype.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class PartiallyUnresolvedTypeQualifier {
|
||||
@Test
|
||||
public void testAllFilesPresentInPartiallyUnresolvedTypeQualifier() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClasses.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClasses() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesTwoResolved() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesTwoResolved.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameBeforeTwoUnsresolvedClassesWithDot.kt")
|
||||
public void testClassNameBeforeTwoUnsresolvedClassesWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameBeforeTwoUnsresolvedClassesWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClass() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithDot.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt")
|
||||
public void testClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/withErrors/partiallyUnresolvedTypeQualifier/ClassNameSecondQualifierBeforeOneUnsresolvedClassWithTwoResolvedWithDot.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
class Foo
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
class Foo
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.Baz
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.Baz.
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
class Foo
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.Baz
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.Baz.Bazzzz
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
class Foo
|
||||
|
||||
class C {
|
||||
val prop: <caret>Foo.Bar.Baz.
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in ROOT) class Foo
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: Foo.<caret>Bar.Baz
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in Foo) class Bar
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: Foo.<caret>Bar.Baz.
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in Foo) class Bar
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
class Foo {
|
||||
class Bar
|
||||
}
|
||||
|
||||
class C {
|
||||
val prop: Foo.<caret>Bar.Baz.
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in Foo) class Bar
|
||||
+1
-1
@@ -60,7 +60,7 @@ internal class LLFirResolveSessionDepended(
|
||||
|
||||
override fun getOrBuildFirFor(element: KtElement): FirElement? {
|
||||
val psi = FirElementBuilder.getPsiAsFirElementSource(element) ?: return null
|
||||
ktToFirMapping?.getFirOfClosestParent(psi, this)?.let { return it }
|
||||
ktToFirMapping?.getFirOfClosestParent(psi)?.let { return it }
|
||||
return originalFirResolveSession.getOrBuildFirFor(element = element)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ internal class FirElementBuilder(
|
||||
val structureElement = fileStructure.getStructureElementFor(element)
|
||||
val psi = getPsiAsFirElementSource(element) ?: return null
|
||||
val mappings = structureElement.mappings
|
||||
return mappings.getFirOfClosestParent(psi, firResolveSession)
|
||||
return mappings.getFirOfClosestParent(psi)
|
||||
?: firResolveSession.getOrBuildFirFile(firFile)
|
||||
}
|
||||
|
||||
|
||||
+9
-21
@@ -8,13 +8,17 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.*
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignation
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDesignationWithFile
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDesignation
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.ClassDiagnosticRetriever
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileDiagnosticRetriever
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.FileStructureElementDiagnostics
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.diagnostics.SingleNonLocalDeclarationDiagnosticRetriever
|
||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirNonLocalDeclarationBuilder
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.correspondingProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.FirBackingFieldBuilder
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.FirFunctionBuilder
|
||||
@@ -26,7 +30,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import org.jetbrains.kotlin.fir.correspondingProperty
|
||||
|
||||
internal sealed class FileStructureElement(val firFile: FirFile, protected val moduleComponents: LLFirModuleResolveComponents) {
|
||||
abstract val psi: KtAnnotated
|
||||
@@ -36,31 +39,16 @@ internal sealed class FileStructureElement(val firFile: FirFile, protected val m
|
||||
|
||||
internal class KtToFirMapping(firElement: FirElement, recorder: FirElementsRecorder) {
|
||||
private val mapping = FirElementsRecorder.recordElementsFrom(firElement, recorder)
|
||||
private val userTypeMapping = ConcurrentHashMap<KtUserType, FirElement>()
|
||||
|
||||
fun getElement(ktElement: KtElement, firResolveSession: LLFirResolveSession): FirElement? {
|
||||
mapping[ktElement]?.let { return it }
|
||||
|
||||
val userType = when (ktElement) {
|
||||
is KtUserType -> ktElement
|
||||
is KtNameReferenceExpression -> ktElement.parent as? KtUserType
|
||||
else -> null
|
||||
} ?: return null
|
||||
|
||||
//This is for not inner KtUserType
|
||||
if (userType.parent is KtTypeReference) return null
|
||||
|
||||
return userTypeMapping.getOrPut(userType) {
|
||||
val typeReference = KtPsiFactory(ktElement.project).createType(userType.text)
|
||||
LowLevelFirApiFacadeForResolveOnAir.onAirResolveTypeInPlace(ktElement, typeReference, firResolveSession)
|
||||
}
|
||||
fun getElement(ktElement: KtElement): FirElement? {
|
||||
return mapping[ktElement]
|
||||
}
|
||||
|
||||
fun getFirOfClosestParent(element: KtElement, firResolveSession: LLFirResolveSession): FirElement? {
|
||||
fun getFirOfClosestParent(element: KtElement): FirElement? {
|
||||
var current: PsiElement? = element
|
||||
while (current != null && current !is KtFile) {
|
||||
if (current is KtElement) {
|
||||
getElement(current, firResolveSession)?.let { return it }
|
||||
getElement(current)?.let { return it }
|
||||
}
|
||||
current = current.parent
|
||||
}
|
||||
|
||||
+13
@@ -105,11 +105,13 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
|
||||
|
||||
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: MutableMap<KtElement, FirElement>) {
|
||||
super.visitResolvedTypeRef(errorTypeRef, data)
|
||||
recordTypeQualifiers(errorTypeRef, data)
|
||||
errorTypeRef.delegatedTypeRef?.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitResolvedTypeRef(resolvedTypeRef: FirResolvedTypeRef, data: MutableMap<KtElement, FirElement>) {
|
||||
super.visitResolvedTypeRef(resolvedTypeRef, data)
|
||||
recordTypeQualifiers(resolvedTypeRef, data)
|
||||
resolvedTypeRef.delegatedTypeRef?.accept(this, data)
|
||||
}
|
||||
|
||||
@@ -219,6 +221,17 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordTypeQualifiers(resolvedTypeRef: FirResolvedTypeRef, data: MutableMap<KtElement, FirElement>) {
|
||||
val userTypeRef = resolvedTypeRef.delegatedTypeRef as? FirUserTypeRef ?: return
|
||||
val qualifiers = userTypeRef.qualifier
|
||||
if (qualifiers.size <= 1) return
|
||||
qualifiers.forEachIndexed { index, qualifierPart ->
|
||||
if (index == qualifiers.lastIndex) return@forEachIndexed
|
||||
val source = qualifierPart.source?.psi as? KtElement ?: return@forEachIndexed
|
||||
cache(source, resolvedTypeRef, data)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun recordElementsFrom(firElement: FirElement, recorder: FirElementsRecorder): Map<KtElement, FirElement> =
|
||||
buildMap { firElement.accept(recorder, this) }
|
||||
|
||||
-1
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirTowerDataCo
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.platformSupertypeUpdater
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
internal object LLFirSupertypeLazyResolver : LLFirLazyResolver(FirResolvePhase.SUPER_TYPES) {
|
||||
override fun resolve(
|
||||
|
||||
+12
@@ -3781,6 +3781,18 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag
|
||||
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 LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated
|
||||
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 {
|
||||
|
||||
+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