[Analysis API FIR] preserve type annotations on asPsiType conversion

^KT-66603 Fixed
This commit is contained in:
Dmitrii Gridin
2024-03-14 18:31:58 +01:00
committed by Space Team
parent 3c8a95e623
commit 96575a0bdb
18 changed files with 136 additions and 48 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -65,6 +65,23 @@ internal class KtFe10PsiTypeProvider(
)
}
override fun asPsiType(
type: KtType,
useSitePosition: PsiElement,
allowErrorTypes: Boolean,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
suppressWildcards: Boolean?,
preserveAnnotations: Boolean
): PsiType? = asPsiTypeElement(
type = type,
useSitePosition = useSitePosition,
mode = mode,
isAnnotationMethod = isAnnotationMethod,
suppressWildcards = suppressWildcards,
allowErrorTypes = allowErrorTypes,
)?.type
private fun KtTypeMappingMode.toTypeMappingMode(
type: KtType,
isAnnotationMethod: Boolean,
+1 -1
View File
@@ -33,6 +33,7 @@ dependencies {
implementation(project(":analysis:analysis-api-providers"))
implementation(project(":analysis:analysis-internal-utils"))
implementation(project(":analysis:kt-references"))
implementation(project(":analysis:symbol-light-classes"))
testImplementation(projectTests(":analysis:low-level-api-fir"))
testImplementation(project(":analysis:analysis-api-standalone:analysis-api-standalone-base"))
@@ -53,7 +54,6 @@ dependencies {
testApi(platform(libs.junit.bom))
testImplementation(libs.junit.jupiter.api)
testRuntimeOnly(libs.junit.jupiter.engine)
testImplementation(project(":analysis:symbol-light-classes"))
}
sourceSets {
@@ -13,6 +13,7 @@ import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.psi.impl.compiled.StubBuildingVisitor
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.analysis.api.KtAnalysisNonPublicApi
import org.jetbrains.kotlin.analysis.api.components.KtPsiTypeProvider
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.analysis.api.fir.findPsi
@@ -43,6 +44,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.jvm.buildJavaTypeRef
import org.jetbrains.kotlin.light.classes.symbol.annotations.annotateByKtType
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
@@ -96,6 +98,38 @@ internal class KtFirPsiTypeProvider(
)
}
override fun asPsiType(
type: KtType,
useSitePosition: PsiElement,
allowErrorTypes: Boolean,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
suppressWildcards: Boolean?,
preserveAnnotations: Boolean,
): PsiType? {
val typeElement = asPsiTypeElement(
type = type,
useSitePosition = useSitePosition,
mode = mode,
isAnnotationMethod = isAnnotationMethod,
suppressWildcards = suppressWildcards,
allowErrorTypes = allowErrorTypes,
) ?: return null
val psiType = typeElement.type
if (!preserveAnnotations) return psiType
@OptIn(KtAnalysisNonPublicApi::class)
return with(analysisSession) {
annotateByKtType(
psiType = psiType,
ktType = type,
psiContext = typeElement,
modifierListAsParent = null,
)
}
}
private fun KtTypeMappingMode.toTypeMappingMode(
type: KtType,
isAnnotationMethod: Boolean,
@@ -195,7 +229,7 @@ internal class KtFirPsiTypeProvider(
private fun ConeKotlinType.simplifyType(
session: FirSession,
useSitePosition: PsiElement,
visited: MutableSet<ConeKotlinType> = mutableSetOf()
visited: MutableSet<ConeKotlinType> = mutableSetOf(),
): ConeKotlinType {
// E.g., Wrapper<T> : Comparable<Wrapper<T>>
if (!visited.add(this)) return this
@@ -235,7 +269,7 @@ private fun ConeKotlinType.needLocalTypeApproximation(
visibilityForApproximation: Visibility,
isInlineFunction: Boolean,
session: FirSession,
useSitePosition: PsiElement
useSitePosition: PsiElement,
): Boolean {
if (!shouldApproximateAnonymousTypesOfNonLocalDeclaration(visibilityForApproximation, isInlineFunction)) return false
val localTypes: List<ConeKotlinType> = if (isLocal(session)) listOf(this) else {
@@ -309,7 +343,7 @@ private fun ConeKotlinType.asPsiTypeElement(
session: FirSession,
mode: TypeMappingMode,
useSitePosition: PsiElement,
allowErrorTypes: Boolean
allowErrorTypes: Boolean,
): PsiTypeElement? {
if (this !is SimpleTypeMarker) return null
@@ -373,7 +407,7 @@ private class AnonymousTypesSubstitutor(
}
private fun ConeKotlinType.hasRecursiveTypeArgument(
visited: MutableSet<ConeKotlinType> = mutableSetOf()
visited: MutableSet<ConeKotlinType> = mutableSetOf(),
): Boolean {
if (typeArguments.isEmpty()) return false
if (!visited.add(this)) return true
@@ -22,6 +22,16 @@ public abstract class KtPsiTypeProvider : KtAnalysisSessionComponent() {
allowErrorTypes: Boolean,
): PsiTypeElement?
public abstract fun asPsiType(
type: KtType,
useSitePosition: PsiElement,
allowErrorTypes: Boolean,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
suppressWildcards: Boolean?,
preserveAnnotations: Boolean,
): PsiType?
public abstract fun asKtType(
psiType: PsiType,
useSitePosition: PsiElement
@@ -54,6 +64,10 @@ public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
* `null` is no-op by default, i.e., their suppression/appearance is determined by type annotations.
*
* Note: [PsiTypeElement] is JVM conception, so this method will return `null` for non-JVM platforms.
*
* @return [PsiTypeElement] without type annotations if mapping is successful
*
* @see asPsiType
*/
public fun KtType.asPsiTypeElement(
useSitePosition: PsiElement,
@@ -63,23 +77,30 @@ public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
suppressWildcards: Boolean? = null,
): PsiTypeElement? = withValidityAssertion {
analysisSession.psiTypeProvider.asPsiTypeElement(
this,
useSitePosition,
mode,
isAnnotationMethod,
suppressWildcards,
allowErrorTypes,
type = this,
useSitePosition = useSitePosition,
mode = mode,
isAnnotationMethod = isAnnotationMethod,
suppressWildcards = suppressWildcards,
allowErrorTypes = allowErrorTypes,
)
}
/**
* Converts the given [KtType] to [PsiType] under [useSitePosition] context.
*
* This simply unwraps [PsiTypeElement] returned from [asPsiTypeElement].
* Use this version if type annotation is not required. Otherwise, use [asPsiTypeElement] to get [PsiTypeElement] as an owner of
* annotations on [PsiType] and annotate the resulting [PsiType] with proper [PsiAnnotation][com.intellij.psi.PsiAnnotation].
*
* Note: [PsiType] is JVM conception, so this method will return `null` for non-JVM platforms.
*
* @receiver type to convert
*
* @param useSitePosition is used to determine if the given [KtType] needs to be approximated.
* For instance, if the given type is local yet available in the same scope of use site,
* we can still use such a local type.
* Otherwise, e.g., exposed to public as a return type, the resulting type will be approximated accordingly.
*
* @param allowErrorTypes if **false** the result will be null in the case of an error type inside the [type][this]
*
* @param preserveAnnotations if **true** the result [PsiType] will have converted annotations from the original [type][this]
*/
public fun KtType.asPsiType(
useSitePosition: PsiElement,
@@ -87,14 +108,18 @@ public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
mode: KtTypeMappingMode = KtTypeMappingMode.DEFAULT,
isAnnotationMethod: Boolean = false,
suppressWildcards: Boolean? = null,
): PsiType? =
asPsiTypeElement(
useSitePosition,
allowErrorTypes,
mode,
isAnnotationMethod,
suppressWildcards,
)?.type
preserveAnnotations: Boolean = true,
): PsiType? = withValidityAssertion {
analysisSession.psiTypeProvider.asPsiType(
type = this,
useSitePosition = useSitePosition,
allowErrorTypes = allowErrorTypes,
mode = mode,
isAnnotationMethod = isAnnotationMethod,
suppressWildcards = suppressWildcards,
preserveAnnotations = preserveAnnotations,
)
}
/**
* Converts given [PsiType] to [KtType].
@@ -0,0 +1,2 @@
KtType: @foo.MyAnno(s = "outer") kotlin.collections.List<@foo.MyAnno(s = "middle") kotlin.collections.List<@foo.AnotherAnnotation(k = foo.Nested::class) kotlin.String>>
PsiType: java.util.List<? extends java.util.List<? extends java.lang.String>>
@@ -1,2 +1,2 @@
KtType: @foo.MyAnno(s = "outer") kotlin.collections.List<@foo.MyAnno(s = "middle") kotlin.collections.List<@foo.AnotherAnnotation(k = foo.Nested::class) kotlin.String>>
PsiType: java.util.List<? extends java.util.List<? extends java.lang.String>>
PsiType: java.util.@foo.MyAnno("outer") List<@foo.MyAnno("middle") ? extends java.util.List<? extends java.lang.String>>
@@ -0,0 +1,2 @@
KtType: @foo.MyAnno(s = "str1") kotlin.String
PsiType: java.lang.String
@@ -1,2 +1,2 @@
KtType: @foo.MyAnno(s = "str1") kotlin.String
PsiType: java.lang.String
PsiType: java.lang.@foo.MyAnno("str" + "1") String
@@ -1,2 +1,2 @@
KtType: @kotlin.jvm.JvmSuppressWildcards(suppress = false) (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)?
PsiType: kotlin.jvm.functions.Function1<? super AnimatedContentTransitionScope<NavBackStackEntry>,? extends EnterTransition>
PsiType: kotlin.jvm.functions.@kotlin.jvm.JvmSuppressWildcards(suppress = false) Function1<? super AnimatedContentTransitionScope<NavBackStackEntry>,? extends EnterTransition>
@@ -1,2 +1,2 @@
KtType: @kotlin.jvm.JvmSuppressWildcards(suppress = true) (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)?
PsiType: kotlin.jvm.functions.Function1<AnimatedContentTransitionScope<NavBackStackEntry>,EnterTransition>
PsiType: kotlin.jvm.functions.@kotlin.jvm.JvmSuppressWildcards(suppress = true) Function1<AnimatedContentTransitionScope<NavBackStackEntry>,EnterTransition>
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.light.classes.symbol.annotations
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightReferenceListBuilder
import org.jetbrains.kotlin.analysis.api.KtAnalysisNonPublicApi
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.*
import org.jetbrains.kotlin.analysis.api.annotations.KtKClassAnnotationValue.KtNonLocalKClassAnnotationValue
@@ -161,7 +162,8 @@ internal fun KtAnnotatedSymbol.computeThrowsList(
}
context(KtAnalysisSession)
internal fun annotateByKtType(
@KtAnalysisNonPublicApi
fun annotateByKtType(
psiType: PsiType,
ktType: KtType,
psiContext: PsiTypeElement,
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.light.classes.symbol.methods
import com.intellij.psi.*
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.mutate
import org.jetbrains.kotlin.analysis.api.KtAnalysisNonPublicApi
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.hasAnnotation
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
@@ -236,6 +237,7 @@ internal class SymbolLightSimpleMethod(
this@SymbolLightSimpleMethod.containingClass.isAnnotationType,
suppressWildcards = suppressWildcards(),
)?.let {
@OptIn(KtAnalysisNonPublicApi::class)
annotateByKtType(it.type, ktType, it, modifierList)
}
} ?: nonExistentType()
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.light.classes.symbol.parameters
import com.intellij.psi.*
import org.jetbrains.kotlin.analysis.api.KtAnalysisNonPublicApi
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
import org.jetbrains.kotlin.analysis.api.symbols.pointers.KtSymbolPointer
@@ -84,6 +85,7 @@ internal abstract class SymbolLightParameterCommon(
ktType.typeMappingMode(),
suppressWildcards = parameterSymbol.suppressWildcardMode(),
)?.let {
@OptIn(KtAnalysisNonPublicApi::class)
annotateByKtType(it.type, ktType, it, modifierList)
}
} ?: nonExistentType()
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiIdentifier
import com.intellij.psi.PsiModifierList
import com.intellij.psi.PsiType
import com.intellij.psi.util.TypeConversionUtil
import org.jetbrains.kotlin.analysis.api.KtAnalysisNonPublicApi
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtReceiverParameterSymbol
@@ -93,8 +94,10 @@ internal class SymbolLightParameterForReceiver private constructor(
ktType.typeMappingMode(),
suppressWildcards = receiver.suppressWildcard() ?: method.suppressWildcards(),
)?.let {
@OptIn(KtAnalysisNonPublicApi::class)
annotateByKtType(it.type, ktType, it, modifierList)
}
if (method is SymbolLightAnnotationsMethod) {
val erased = TypeConversionUtil.erasure(psiType)
val name = erased.canonicalText
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -51,14 +51,13 @@ internal fun KtAnalysisSession.mapType(
psiContext: PsiElement,
mode: KtTypeMappingMode
): PsiClassType? {
val psiTypeElement = type.asPsiTypeElement(
val psiType = type.asPsiType(
psiContext,
allowErrorTypes = true,
mode,
)
return (psiTypeElement?.type as? PsiClassType)?.let {
annotateByKtType(it, type, psiTypeElement, modifierListAsParent = null) as? PsiClassType
}
return psiType as? PsiClassType
}
internal enum class NullabilityType {
@@ -1,12 +1,12 @@
public final class Nested /* foo.Nested*/ {
@org.jetbrains.annotations.Nullable()
private java.util.List<? extends java.util.List<java.lang.String>> property = null /* initializer type: null */;
private @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") ? extends java.util.List<java.lang.String>> property = null /* initializer type: null */;
@org.jetbrains.annotations.Nullable()
public final @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") java.util.List<@foo.AnotherAnnotation(k = foo.Nested.class) java.lang.String>> function(@org.jetbrains.annotations.NotNull() @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") ? extends java.util.List<java.lang.String>>, @org.jetbrains.annotations.NotNull() @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") ? extends java.util.List<java.lang.String>>);// function(@foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") ? extends java.util.List<java.lang.String>>, @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") ? extends java.util.List<java.lang.String>>)
@org.jetbrains.annotations.Nullable()
public final java.util.List<java.util.List<java.lang.String>> getProperty();// getProperty()
public final @foo.MyAnno(s = "outer") java.util.List<@foo.MyAnno(s = "middle") java.util.List<@foo.AnotherAnnotation(k = foo.Nested.class) java.lang.String>> getProperty();// getProperty()
public Nested();// .ctor()
@@ -45,22 +45,22 @@ public final class ContainerForPropertyAndAccessors /* ContainerForPropertyAndAc
private Out<? extends Open> bar;
@org.jetbrains.annotations.NotNull()
private final In<? super java.lang.Object> simpleIn;
private final In<@kotlin.jvm.JvmWildcard() ? super java.lang.Object> simpleIn;
@org.jetbrains.annotations.NotNull()
private final Out<? extends Final> simpleOut;
private final Out<@kotlin.jvm.JvmWildcard() ? extends Final> simpleOut;
@org.jetbrains.annotations.NotNull()
private final Out<Out<Out<Open>>> deepOpen;
@org.jetbrains.annotations.NotNull()
public final In<? super java.lang.Object> getSimpleIn();// getSimpleIn()
public final @kotlin.jvm.JvmSuppressWildcards(suppress = false) Out<? extends Open> getZoo(@org.jetbrains.annotations.NotNull() Out<? extends Out<? extends Out<? extends Open>>>);// getZoo(Out<? extends Out<? extends Out<? extends Open>>>)
@org.jetbrains.annotations.NotNull()
public final Out<? extends Final> getSimpleOut();// getSimpleOut()
public final In<@kotlin.jvm.JvmWildcard() ? super java.lang.Object> getSimpleIn();// getSimpleIn()
@org.jetbrains.annotations.NotNull()
public final Out<? extends Open> getZoo(@org.jetbrains.annotations.NotNull() Out<? extends Out<? extends Out<? extends Open>>>);// getZoo(Out<? extends Out<? extends Out<? extends Open>>>)
public final Out<@kotlin.jvm.JvmWildcard() ? extends Final> getSimpleOut();// getSimpleOut()
@org.jetbrains.annotations.NotNull()
public final Out<Open> getBar();// getBar()
@@ -53,17 +53,17 @@ public final class Y /* Y*/ {
public final class klass /* klass*/ {
@org.jetbrains.annotations.Nullable()
private final java.util.List<java.lang.Integer> y = null /* initializer type: null */;
private final java.util.List<@A0() java.lang.Integer> y = null /* initializer type: null */;
private final int x = 2 /* initializer type: int */;
private final @A0() int x = 2 /* initializer type: int */;
@org.jetbrains.annotations.NotNull()
public final @A6() X annotatedMethod(@org.jetbrains.annotations.NotNull() @A0() P<@A1() X, P<@A2() @A3() X, @A4() Y>>, @org.jetbrains.annotations.NotNull() @A5() Y[]);// annotatedMethod(@A0() P<@A1() X, P<@A2() @A3() X, @A4() Y>>, @A5() Y[])
@org.jetbrains.annotations.Nullable()
public final java.util.List<java.lang.Integer> getY();// getY()
public final java.util.List<@A0() java.lang.Integer> getY();// getY()
public klass();// .ctor()
public final int getX();// getX()
public final @A0() int getX();// getX()
}