AA: handle PsiType conversion for recursive type parameter case
^KT-59598 Fixed
This commit is contained in:
committed by
Ilya Kirillov
parent
2d132c6b73
commit
e0fc0d96e5
+28
-2
@@ -11,6 +11,7 @@ import com.intellij.psi.impl.cache.TypeInfo
|
|||||||
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
|
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
|
||||||
import com.intellij.psi.impl.compiled.SignatureParsing
|
import com.intellij.psi.impl.compiled.SignatureParsing
|
||||||
import com.intellij.psi.impl.compiled.StubBuildingVisitor
|
import com.intellij.psi.impl.compiled.StubBuildingVisitor
|
||||||
|
import java.text.StringCharacterIterator
|
||||||
import org.jetbrains.kotlin.analysis.api.components.KtPsiTypeProvider
|
import org.jetbrains.kotlin.analysis.api.components.KtPsiTypeProvider
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType
|
import org.jetbrains.kotlin.analysis.api.fir.types.KtFirType
|
||||||
@@ -42,7 +43,6 @@ import org.jetbrains.kotlin.psi
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||||
import java.text.StringCharacterIterator
|
|
||||||
|
|
||||||
internal class KtFirPsiTypeProvider(
|
internal class KtFirPsiTypeProvider(
|
||||||
override val analysisSession: KtFirAnalysisSession,
|
override val analysisSession: KtFirAnalysisSession,
|
||||||
@@ -238,7 +238,15 @@ private class AnonymousTypesSubstitutor(
|
|||||||
if (type !is ConeClassLikeType) return null
|
if (type !is ConeClassLikeType) return null
|
||||||
|
|
||||||
val hasStableName = type.classId?.isLocal == true
|
val hasStableName = type.classId?.isLocal == true
|
||||||
if (!hasStableName) return null
|
if (!hasStableName) {
|
||||||
|
// Make sure we're not going to expand type argument over and over again.
|
||||||
|
// If so, i.e., if there is a recursive type argument, return the current, non-null [type]
|
||||||
|
// to prevent the following [substituteTypeOr*] from proceeding to its own (recursive) substitution.
|
||||||
|
if (type.hasRecursiveTypeArgument()) return type
|
||||||
|
// Return `null` means we will use [fir.resolve.substitution.Substitutors]'s [substituteRecursive]
|
||||||
|
// that literally substitutes type arguments recursively.
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val firClassNode = type.lookupTag.toSymbol(session) as? FirClassSymbol
|
val firClassNode = type.lookupTag.toSymbol(session) as? FirClassSymbol
|
||||||
if (firClassNode != null) {
|
if (firClassNode != null) {
|
||||||
@@ -248,4 +256,22 @@ private class AnonymousTypesSubstitutor(
|
|||||||
return if (type.nullability.isNullable) session.builtinTypes.nullableAnyType.type
|
return if (type.nullability.isNullable) session.builtinTypes.nullableAnyType.type
|
||||||
else session.builtinTypes.anyType.type
|
else session.builtinTypes.anyType.type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ConeKotlinType.hasRecursiveTypeArgument(
|
||||||
|
visited: MutableSet<ConeKotlinType> = mutableSetOf()
|
||||||
|
): Boolean {
|
||||||
|
if (typeArguments.isEmpty()) return false
|
||||||
|
visited.add(this)
|
||||||
|
for (projection in typeArguments) {
|
||||||
|
// E.g., Test : Comparable<Test>
|
||||||
|
val type = (projection as? ConeKotlinTypeProjection)?.type ?: continue
|
||||||
|
// E.g., Comparable<Test>
|
||||||
|
val newType = substituteOrNull(type) ?: continue
|
||||||
|
if (newType in visited) return true
|
||||||
|
// Visit new type: e.g., Test, as a type argument, is substituted with Comparable<Test>, again.
|
||||||
|
if (newType.hasRecursiveTypeArgument(visited)) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -64,6 +64,12 @@ public class FirIdeDependentAnalysisSourceModuleAnalysisApiExpressionPsiTypeProv
|
|||||||
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("recursiveTypeParameter.kt")
|
||||||
|
public void testRecursiveTypeParameter() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/recursiveTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
||||||
public void testTypeParamFlexibleUpperBound() throws Exception {
|
public void testTypeParamFlexibleUpperBound() throws Exception {
|
||||||
|
|||||||
+6
@@ -64,6 +64,12 @@ public class FirIdeNormalAnalysisSourceModuleAnalysisApiExpressionPsiTypeProvide
|
|||||||
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("recursiveTypeParameter.kt")
|
||||||
|
public void testRecursiveTypeParameter() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/recursiveTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
||||||
public void testTypeParamFlexibleUpperBound() throws Exception {
|
public void testTypeParamFlexibleUpperBound() throws Exception {
|
||||||
|
|||||||
+6
-1
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.utils.printer.parentOfType
|
|||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtValueArgument
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.assertions
|
import org.jetbrains.kotlin.test.services.assertions
|
||||||
@@ -21,7 +22,11 @@ import org.jetbrains.kotlin.types.Variance
|
|||||||
|
|
||||||
abstract class AbstractAnalysisApiExpressionPsiTypeProviderTest : AbstractAnalysisApiSingleFileTest() {
|
abstract class AbstractAnalysisApiExpressionPsiTypeProviderTest : AbstractAnalysisApiSingleFileTest() {
|
||||||
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
|
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
|
||||||
val declarationAtCaret = testServices.expressionMarkerProvider.getSelectedElement(ktFile) as KtExpression
|
val declarationAtCaret = when (val element = testServices.expressionMarkerProvider.getSelectedElement(ktFile)) {
|
||||||
|
is KtExpression -> element
|
||||||
|
is KtValueArgument -> element.getArgumentExpression()!!
|
||||||
|
else -> error("Unexpected element: $element of ${element::class.java}")
|
||||||
|
}
|
||||||
val containingDeclaration = declarationAtCaret.parentOfType<KtDeclaration>()
|
val containingDeclaration = declarationAtCaret.parentOfType<KtDeclaration>()
|
||||||
?: error("Can't find containing declaration for $declarationAtCaret")
|
?: error("Can't find containing declaration for $declarationAtCaret")
|
||||||
val containingClass = getContainingKtLightClass(containingDeclaration, ktFile)
|
val containingClass = getContainingKtLightClass(containingDeclaration, ktFile)
|
||||||
|
|||||||
+6
@@ -64,6 +64,12 @@ public class FirStandaloneNormalAnalysisSourceModuleAnalysisApiExpressionPsiType
|
|||||||
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/localClassWithUnresolvedSuperType.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("recursiveTypeParameter.kt")
|
||||||
|
public void testRecursiveTypeParameter() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/psiTypeProvider/psiType/forExpression/recursiveTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
@TestMetadata("typeParamFlexibleUpperBound.kt")
|
||||||
public void testTypeParamFlexibleUpperBound() throws Exception {
|
public void testTypeParamFlexibleUpperBound() throws Exception {
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
fun testNotTransitive() {
|
||||||
|
class Test(val v: Int): Comparable<Test> {
|
||||||
|
override fun compareTo(other: Test): Int {
|
||||||
|
return v.compareTo(-other.v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val list = listOf(Test(1), Test(2), Test(3), Test(4))
|
||||||
|
checkTransitiveComparator(<expr>list</expr>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Comparable<T>> checkTransitiveComparator(list: List<T>) {
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
KtType: kotlin.collections.List<Test>
|
||||||
|
PsiType: PsiType:List<? extends Comparable<? super Test>>
|
||||||
Reference in New Issue
Block a user