AA: relax the assertion about referenced ClassId and qualified access
If an import alias is involved, they could be not identical. Since an import alias ends with a simple identifer, we can simply drop the first segment of qualified access and try the assertion again.
This commit is contained in:
committed by
Ilya Kirillov
parent
7fbfb1bd95
commit
a68fd25982
+26
-4
@@ -468,9 +468,31 @@ internal object FirReferenceResolveHelper {
|
|||||||
referencedClass.classId
|
referencedClass.classId
|
||||||
}
|
}
|
||||||
val qualifiedAccessSegments = qualifiedAccess.fqNameSegments() ?: return referencedSymbolsByFir
|
val qualifiedAccessSegments = qualifiedAccess.fqNameSegments() ?: return referencedSymbolsByFir
|
||||||
assert(referencedClassId.asSingleFqName().pathSegments().takeLast(qualifiedAccessSegments.size)
|
|
||||||
.map { it.identifierOrNullIfSpecial } == qualifiedAccessSegments) {
|
fun referencedClassIdAndQualifiedAccessMatch(
|
||||||
"Referenced classId $referencedClassId should end with qualifiedAccess expression ${qualifiedAccess.text} "
|
qualifiedAccessSegments: List<String>
|
||||||
|
): Boolean {
|
||||||
|
val referencedClassIdSegments =
|
||||||
|
referencedClassId.asSingleFqName().pathSegments()
|
||||||
|
.takeLast(qualifiedAccessSegments.size)
|
||||||
|
.map { it.identifierOrNullIfSpecial }
|
||||||
|
return referencedClassIdSegments == qualifiedAccessSegments
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!referencedClassIdAndQualifiedAccessMatch(qualifiedAccessSegments)) {
|
||||||
|
// Referenced ClassId and qualified access (from source PSI) could be not identical if an import alias is involved.
|
||||||
|
// E.g., test.pkg.R.string.hello v.s. coreR.string.hello where test.pkg.R is imported as coreR
|
||||||
|
// Since an import alias ends with a simple identifier (i.e., can't be non-trivial dotted qualifier), we can safely assume
|
||||||
|
// that the first segment of the qualified access could be the import alias if any. Then, we can still compare the
|
||||||
|
// remaining parts.
|
||||||
|
// E.g., coreR.string.hello
|
||||||
|
// -> string.hello (drop the first segment)
|
||||||
|
// test.pkg.R.string.hello
|
||||||
|
// -> string.hello (take last two segments, where the size is determined by the size of qualified access minus 1)
|
||||||
|
qualifiedAccessSegments.removeAt(0)
|
||||||
|
assert(referencedClassIdAndQualifiedAccessMatch(qualifiedAccessSegments)) {
|
||||||
|
"Referenced classId $referencedClassId should end with qualifiedAccess expression ${qualifiedAccess.text} "
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the code below, we always maintain the contract that `classId` and `qualifiedAccess` should stay "in-sync", i.e. they
|
// In the code below, we always maintain the contract that `classId` and `qualifiedAccess` should stay "in-sync", i.e. they
|
||||||
@@ -534,7 +556,7 @@ internal object FirReferenceResolveHelper {
|
|||||||
* Returns the segments of a qualified access PSI. For example, given `foo.bar.OuterClass.InnerClass`, this returns `["foo", "bar",
|
* Returns the segments of a qualified access PSI. For example, given `foo.bar.OuterClass.InnerClass`, this returns `["foo", "bar",
|
||||||
* "OuterClass", "InnerClass"]`.
|
* "OuterClass", "InnerClass"]`.
|
||||||
*/
|
*/
|
||||||
private fun KtDotQualifiedExpression.fqNameSegments(): List<String>? {
|
private fun KtDotQualifiedExpression.fqNameSegments(): MutableList<String>? {
|
||||||
val result: MutableList<String> = mutableListOf()
|
val result: MutableList<String> = mutableListOf()
|
||||||
var current: KtExpression = this
|
var current: KtExpression = this
|
||||||
while (current is KtDotQualifiedExpression) {
|
while (current is KtDotQualifiedExpression) {
|
||||||
|
|||||||
+6
@@ -184,6 +184,12 @@ public class FirLibrarySourceReferenceResolveTestGenerated extends AbstractRefer
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/implicitFunctionalInterfaceInvoke.kt");
|
runTest("analysis/analysis-api/testData/referenceResolve/implicitFunctionalInterfaceInvoke.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("importAlias.kt")
|
||||||
|
public void testImportAlias() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/referenceResolve/importAlias.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ImportFromRootScope.kt")
|
@TestMetadata("ImportFromRootScope.kt")
|
||||||
public void testImportFromRootScope() throws Exception {
|
public void testImportFromRootScope() throws Exception {
|
||||||
|
|||||||
+6
@@ -184,6 +184,12 @@ public class FirSourceReferenceResolveTestGenerated extends AbstractReferenceRes
|
|||||||
runTest("analysis/analysis-api/testData/referenceResolve/implicitFunctionalInterfaceInvoke.kt");
|
runTest("analysis/analysis-api/testData/referenceResolve/implicitFunctionalInterfaceInvoke.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("importAlias.kt")
|
||||||
|
public void testImportAlias() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/referenceResolve/importAlias.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("ImportFromRootScope.kt")
|
@TestMetadata("ImportFromRootScope.kt")
|
||||||
public void testImportFromRootScope() throws Exception {
|
public void testImportFromRootScope() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// FILE: R.kt
|
||||||
|
package test.pkg
|
||||||
|
|
||||||
|
class R {
|
||||||
|
class string {
|
||||||
|
companion object {
|
||||||
|
val hello : Int = 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
package test.pkg
|
||||||
|
|
||||||
|
import test.pkg.R as coreR
|
||||||
|
|
||||||
|
fun box() {
|
||||||
|
val s = core<caret>R.string.hello
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
Resolved to:
|
||||||
|
0: (in test.pkg) class R
|
||||||
Reference in New Issue
Block a user