[Analysis API] rework the resolve of the nested name references
To properly resolve qualifier parts in the middle, we need to resolve the whole qualifier to understand which parts of the qualifier are package or class qualifiers. And then we will be able to resolve the qualifier selected by the user to the proper class, package or callable. ^KT-59189
This commit is contained in:
committed by
Space Team
parent
785778511b
commit
724386a5f1
+32
@@ -1408,6 +1408,22 @@ public class Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exte
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ConflictResolution {
|
||||
@Test
|
||||
public void testAllFilesPresentInConflictResolution() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("resolveToPackage.kt")
|
||||
public void testResolveToPackage() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution/resolveToPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/fromOtherFile")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@@ -1512,6 +1528,22 @@ public class Fe10IdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class WithErrors {
|
||||
@Test
|
||||
public void testAllFilesPresentInWithErrors() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("errorInLatestQualifer.kt")
|
||||
public void testErrorInLatestQualifer() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors/errorInLatestQualifer.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+54
-1
@@ -22,8 +22,61 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||
|
||||
internal object KDocReferenceResolver {
|
||||
|
||||
/**
|
||||
* Resolves the [selectedFqName] of KDoc
|
||||
*
|
||||
* To properly resolve qualifier parts in the middle,
|
||||
* we need to resolve the whole qualifier to understand which parts of the qualifier are package or class qualifiers.
|
||||
* And then we will be able to resolve the qualifier selected by the user to the proper class, package or callable.
|
||||
*
|
||||
* It's possible that the whole qualifier is invalid, in this case we still want to resolve our [selectedFqName].
|
||||
* To do this, we are trying to resolve the whole qualifier until we succeed.
|
||||
*
|
||||
* @param selectedFqName the selected fully qualified name of the KDoc
|
||||
* @param fullFqName the whole fully qualified name of the KDoc
|
||||
* @param contextElement the context element in which the KDoc is defined
|
||||
*
|
||||
* @return the collection of KtSymbol(s) resolved from the fully qualified name
|
||||
* based on the selected FqName and context element
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
internal fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection<KtSymbol> {
|
||||
internal fun resolveKdocFqName(selectedFqName: FqName, fullFqName: FqName, contextElement: KtElement): Collection<KtSymbol> {
|
||||
val fullSymbolsResolved = resolveKdocFqName(fullFqName, contextElement)
|
||||
if (selectedFqName == fullFqName) return fullSymbolsResolved
|
||||
if (fullSymbolsResolved.isEmpty()) {
|
||||
val parent = fullFqName.parent()
|
||||
return resolveKdocFqName(selectedFqName = selectedFqName, fullFqName = parent, contextElement = contextElement)
|
||||
}
|
||||
val goBackSteps = fullFqName.pathSegments().size - selectedFqName.pathSegments().size
|
||||
check(goBackSteps > 0) {
|
||||
"Selected FqName ($selectedFqName) should be smaller than the whole FqName ($fullFqName)"
|
||||
}
|
||||
return fullSymbolsResolved.mapNotNullTo(mutableSetOf()) { findParentSymbol(it, goBackSteps, selectedFqName) }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the parent symbol of the given KtSymbol by traversing back up the symbol hierarchy a certain number of steps,
|
||||
* or until the containing class or object symbol is found.
|
||||
*
|
||||
* @param symbol The KtSymbol whose parent symbol needs to be found.
|
||||
* @param goBackSteps The number of steps to go back up the symbol hierarchy.
|
||||
* @param selectedFqName The fully qualified name of the selected package.
|
||||
* @return The [goBackSteps]-th parent [KtSymbol]
|
||||
*/
|
||||
context(KtAnalysisSession)
|
||||
private fun findParentSymbol(symbol: KtSymbol, goBackSteps: Int, selectedFqName: FqName): KtSymbol? {
|
||||
if (symbol !is KtDeclarationSymbol) return null
|
||||
var currentSymbol: KtDeclarationSymbol? = symbol
|
||||
repeat(goBackSteps) {
|
||||
currentSymbol = currentSymbol?.getContainingSymbol() as? KtClassOrObjectSymbol
|
||||
}
|
||||
currentSymbol?.let { return it }
|
||||
return getPackageSymbolIfPackageExists(selectedFqName)
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun resolveKdocFqName(fqName: FqName, contextElement: KtElement): Collection<KtSymbol> {
|
||||
getSymbolsFromParentMemberScopes(fqName, contextElement).ifNotEmpty { return this }
|
||||
val importScopeContext = contextElement.containingKtFile.getImportingScopeContext()
|
||||
getSymbolsFromImportingScope(importScopeContext, fqName, KtScopeKind.ExplicitSimpleImportingScope::class).ifNotEmpty { return this }
|
||||
|
||||
+3
-1
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
||||
|
||||
internal class KtFirKDocReference(element: KDocName) : KDocReference(element), KtFirReference {
|
||||
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
|
||||
return KDocReferenceResolver.resolveKdocFqName(element.getQualifiedNameAsFqName(), element)
|
||||
val fullFqName = generateSequence(element) { it.parent as? KDocName }.last().getQualifiedNameAsFqName()
|
||||
val selectedFqName = element.getQualifiedNameAsFqName()
|
||||
return KDocReferenceResolver.resolveKdocFqName(selectedFqName, fullFqName, element)
|
||||
}
|
||||
}
|
||||
+32
@@ -1408,6 +1408,22 @@ public class FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated ex
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ConflictResolution {
|
||||
@Test
|
||||
public void testAllFilesPresentInConflictResolution() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("resolveToPackage.kt")
|
||||
public void testResolveToPackage() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution/resolveToPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/fromOtherFile")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@@ -1512,6 +1528,22 @@ public class FirIdeDependentAnalysisSourceModuleReferenceResolveTestGenerated ex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class WithErrors {
|
||||
@Test
|
||||
public void testAllFilesPresentInWithErrors() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("errorInLatestQualifer.kt")
|
||||
public void testErrorInLatestQualifer() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors/errorInLatestQualifer.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+16
@@ -1292,6 +1292,22 @@ public class FirIdeNormalAnalysisLibrarySourceModuleReferenceResolveTestGenerate
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified"), Pattern.compile("^([^.]+)\\.kt$"), null, true, "withErrors");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ConflictResolution {
|
||||
@Test
|
||||
public void testAllFilesPresentInConflictResolution() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true, "withErrors");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("resolveToPackage.kt")
|
||||
public void testResolveToPackage() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution/resolveToPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/fromOtherFile")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+32
@@ -1408,6 +1408,22 @@ public class FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exten
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ConflictResolution {
|
||||
@Test
|
||||
public void testAllFilesPresentInConflictResolution() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("resolveToPackage.kt")
|
||||
public void testResolveToPackage() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution/resolveToPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/fromOtherFile")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@@ -1512,6 +1528,22 @@ public class FirIdeNormalAnalysisSourceModuleReferenceResolveTestGenerated exten
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class WithErrors {
|
||||
@Test
|
||||
public void testAllFilesPresentInWithErrors() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("errorInLatestQualifer.kt")
|
||||
public void testErrorInLatestQualifer() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors/errorInLatestQualifer.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+32
@@ -1408,6 +1408,22 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerate
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class ConflictResolution {
|
||||
@Test
|
||||
public void testAllFilesPresentInConflictResolution() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("resolveToPackage.kt")
|
||||
public void testResolveToPackage() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/conflictResolution/resolveToPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/qualified/fromOtherFile")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@@ -1512,6 +1528,22 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceResolveTestGenerate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class WithErrors {
|
||||
@Test
|
||||
public void testAllFilesPresentInWithErrors() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("errorInLatestQualifer.kt")
|
||||
public void testErrorInLatestQualifer() throws Exception {
|
||||
runTest("analysis/analysis-api/testData/referenceResolve/kDoc/withErrors/errorInLatestQualifer.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// FILE: main.kt
|
||||
val p = 10
|
||||
|
||||
/**
|
||||
* [p<caret>p].bar]
|
||||
*/
|
||||
fun foo() {}
|
||||
|
||||
// FILE: pp/dependent.kt
|
||||
package pp
|
||||
|
||||
fun bar(){}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: package pp
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* [A.<caret>BB.C]
|
||||
*/
|
||||
fun foo() {}
|
||||
|
||||
|
||||
class A {
|
||||
class BB
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Resolved to:
|
||||
0: (in A) class BB
|
||||
Reference in New Issue
Block a user