[Analysis API] Ignore FirClassUseSiteMemberScope scopes in KtFirReferenceShortener

Those scopes always contain all nested classifiers,
while only some of them are available without
explicit import. There are other, more reliable
scopes (like FirNestedClassifierScopeWithSubstitution)
which are stricter about which classifiers
they recognise as valid

^KTIJ-24684 Fixed
^KTIJ-24662 Fixed
This commit is contained in:
Ilya Kirillov
2022-10-28 10:35:23 +02:00
committed by teamcity
parent d783bbe5e9
commit 3ec032212c
15 changed files with 190 additions and 3 deletions
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
@@ -601,8 +600,15 @@ private class ElementsToShortenCollector(
// If its parent has a type parameter, we cannot shorten it because it will lose its type parameter.
if (classSymbol.hasTypeParameterFromParent()) return false
/**
* Class use-site member scopes may contain classifiers which are not actually available without explicit import.
*
* See KTIJ-24684 and KTIJ-24662 for examples.
*/
val scopes = positionScopes.filterNot { it is FirClassUseSiteMemberScope }
val name = classId.shortClassName
val availableClassifiers = shorteningContext.findClassifiersInScopesByName(positionScopes, name)
val availableClassifiers = shorteningContext.findClassifiersInScopesByName(scopes, name)
val matchingAvailableSymbol = availableClassifiers.firstOrNull { it.availableSymbol.symbol == classId }
val scopeForClass = matchingAvailableSymbol?.scope ?: return false
@@ -618,7 +624,7 @@ private class ElementsToShortenCollector(
* my.component.foo::class.java // If we drop `my.component`, it will reference `B` instead of `A`
* }
*/
if (shorteningContext.findPropertiesInScopes(positionScopes, name).isNotEmpty()) {
if (shorteningContext.findPropertiesInScopes(scopes, name).isNotEmpty()) {
val firForElement = element.getOrBuildFir(firResolveSession) as? FirQualifiedAccessExpression
val typeArguments = firForElement?.typeArguments ?: emptyList()
val qualifiedAccessCandidates = findCandidatesForPropertyAccess(classSymbol.annotations, typeArguments, name, element)
@@ -196,6 +196,42 @@ public class FirIdeNormalAnalysisSourceModuleReferenceShortenerTestGenerated ext
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClass3.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes.kt")
public void testNestedClassFromSupertypes() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes2.kt")
public void testNestedClassFromSupertypes2() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes2.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes3.kt")
public void testNestedClassFromSupertypes3() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes3.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes4.kt")
public void testNestedClassFromSupertypes4() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes4.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes5.kt")
public void testNestedClassFromSupertypes5() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes5.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes6.kt")
public void testNestedClassFromSupertypes6() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes6.kt");
}
@Test
@TestMetadata("parameterTypeTopLevelTypeLoses.kt")
public void testParameterTypeTopLevelTypeLoses() throws Exception {
@@ -196,6 +196,42 @@ public class FirStandaloneNormalAnalysisSourceModuleReferenceShortenerTestGenera
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClass3.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes.kt")
public void testNestedClassFromSupertypes() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes2.kt")
public void testNestedClassFromSupertypes2() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes2.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes3.kt")
public void testNestedClassFromSupertypes3() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes3.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes4.kt")
public void testNestedClassFromSupertypes4() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes4.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes5.kt")
public void testNestedClassFromSupertypes5() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes5.kt");
}
@Test
@TestMetadata("nestedClassFromSupertypes6.kt")
public void testNestedClassFromSupertypes6() throws Exception {
runTest("analysis/analysis-api/testData/components/referenceShortener/referenceShortener/nestedClassFromSupertypes6.kt");
}
@Test
@TestMetadata("parameterTypeTopLevelTypeLoses.kt")
public void testParameterTypeTopLevelTypeLoses() throws Exception {
@@ -0,0 +1,9 @@
// FILE: main.kt
interface MyInterface {
class Nested
}
class Foo : MyInterface {
<expr>val prop: MyInterface.Nested = MyInterface.Nested()</expr>
}
@@ -0,0 +1,9 @@
Before shortening: val prop: MyInterface.Nested = MyInterface.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
@@ -0,0 +1,10 @@
// FILE: main.kt
import MyInterface.Nested
interface MyInterface {
class Nested
}
class Foo : MyInterface {
<expr>val prop: MyInterface.Nested = MyInterface.Nested()</expr>
}
@@ -0,0 +1,11 @@
Before shortening: val prop: MyInterface.Nested = MyInterface.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
@@ -0,0 +1,8 @@
// FILE: main.kt
open class MyBaseClass {
class Nested
}
class Foo : MyBaseClass() {
<expr>val prop: MyBaseClass.Nested = MyBaseClass.Nested()</expr>
}
@@ -0,0 +1,11 @@
Before shortening: val prop: MyBaseClass.Nested = MyBaseClass.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[type] MyBaseClass.Nested
[qualifier] MyBaseClass.Nested()
with SHORTEN_AND_IMPORT:
[type] MyBaseClass.Nested
[qualifier] MyBaseClass.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyBaseClass.Nested
[qualifier] MyBaseClass.Nested()
@@ -0,0 +1,6 @@
// FILE: main.kt
interface MyInterface {
class Nested
<expr>val prop: MyInterface.Nested get() = MyInterface.Nested()</expr>
}
@@ -0,0 +1,11 @@
Before shortening: val prop: MyInterface.Nested get() = MyInterface.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
@@ -0,0 +1,8 @@
// FILE: main.kt
interface MyInterface {
class Nested
}
fun MyInterface.foo() {
<expr>val prop: MyInterface.Nested = MyInterface.Nested()</expr>
}
@@ -0,0 +1,9 @@
Before shortening: val prop: MyInterface.Nested = MyInterface.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyInterface.Nested
[qualifier] MyInterface.Nested()
@@ -0,0 +1,8 @@
// FILE: main.kt
open class MyBaseClass {
class Nested
}
fun MyBaseClass.foo() {
<expr>val prop: MyBaseClass.Nested = MyBaseClass.Nested()</expr>
}
@@ -0,0 +1,9 @@
Before shortening: val prop: MyBaseClass.Nested = MyBaseClass.Nested()
with DO_NOT_SHORTEN:
with SHORTEN_IF_ALREADY_IMPORTED:
with SHORTEN_AND_IMPORT:
[type] MyBaseClass.Nested
[qualifier] MyBaseClass.Nested()
with SHORTEN_AND_STAR_IMPORT:
[type] MyBaseClass.Nested
[qualifier] MyBaseClass.Nested()