SLC: keep annotations on type when converting to PsiType

^KT-55815 Fixed
This commit is contained in:
Jinseong Jeon
2023-01-12 17:15:21 -08:00
committed by Ilya Kirillov
parent 120ca08740
commit 88b07f5287
13 changed files with 170 additions and 107 deletions
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.analysis.api.descriptors.components
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import com.intellij.psi.PsiTypeElement
import com.intellij.psi.impl.cache.TypeInfo
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.compiled.SignatureParsing
@@ -36,13 +36,13 @@ internal class KtFe10PsiTypeProvider(
private val typeMapper by lazy { KtFe10JvmTypeMapperContext(analysisContext.resolveSession) }
override fun asPsiType(
override fun asPsiTypeElement(
type: KtType,
useSitePosition: PsiElement,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
allowErrorTypes: Boolean
): PsiType? {
): PsiTypeElement? {
val kotlinType = (type as KtFe10Type).fe10Type
with(typeMapper.typeContext) {
@@ -51,7 +51,7 @@ internal class KtFe10PsiTypeProvider(
}
}
return asPsiType(simplifyType(kotlinType), useSitePosition, mode.toTypeMappingMode(type, isAnnotationMethod))
return asPsiTypeElement(simplifyType(kotlinType), useSitePosition, mode.toTypeMappingMode(type, isAnnotationMethod))
}
private fun KtTypeMappingMode.toTypeMappingMode(type: KtType, isAnnotationMethod: Boolean): TypeMappingMode {
@@ -82,7 +82,7 @@ internal class KtFe10PsiTypeProvider(
return result
}
private fun asPsiType(type: KotlinType, useSitePosition: PsiElement, mode: TypeMappingMode): PsiType? {
private fun asPsiTypeElement(type: KotlinType, useSitePosition: PsiElement, mode: TypeMappingMode): PsiTypeElement? {
if (type !is SimpleTypeMarker) return null
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS)
@@ -99,7 +99,6 @@ internal class KtFe10PsiTypeProvider(
val typeInfo = TypeInfo.fromString(javaType, false)
val typeText = TypeInfo.createTypeText(typeInfo) ?: return null
val typeElement = ClsTypeElementImpl(useSitePosition, typeText, '\u0000')
return typeElement.type
return ClsTypeElementImpl(useSitePosition, typeText, '\u0000')
}
}
@@ -49,13 +49,13 @@ internal class KtFirPsiTypeProvider(
override val token: KtLifetimeToken,
) : KtPsiTypeProvider(), KtFirAnalysisSessionComponent {
override fun asPsiType(
override fun asPsiTypeElement(
type: KtType,
useSitePosition: PsiElement,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
allowErrorTypes: Boolean
): PsiType? {
): PsiTypeElement? {
val coneType = type.coneType
with(rootModuleSession.typeContext) {
@@ -65,7 +65,7 @@ internal class KtFirPsiTypeProvider(
}
return coneType.simplifyType(rootModuleSession, useSitePosition)
.asPsiType(rootModuleSession, mode.toTypeMappingMode(type, isAnnotationMethod), useSitePosition, allowErrorTypes)
.asPsiTypeElement(rootModuleSession, mode.toTypeMappingMode(type, isAnnotationMethod), useSitePosition, allowErrorTypes)
}
private fun KtTypeMappingMode.toTypeMappingMode(type: KtType, isAnnotationMethod: Boolean): TypeMappingMode {
@@ -189,12 +189,12 @@ private fun ConeKotlinType.isLocalButAvailableAtPosition(
context.parents.any { it == localPsi }
}
private fun ConeKotlinType.asPsiType(
private fun ConeKotlinType.asPsiTypeElement(
session: FirSession,
mode: TypeMappingMode,
useSitePosition: PsiElement,
allowErrorTypes: Boolean
): PsiType? {
): PsiTypeElement? {
if (this !is SimpleTypeMarker) return null
if (!allowErrorTypes && (this is ConeErrorType)) return null
@@ -224,8 +224,7 @@ private fun ConeKotlinType.asPsiType(
val typeInfo = TypeInfo.fromString(javaType, false)
val typeText = TypeInfo.createTypeText(typeInfo) ?: return null
val typeElement = ClsTypeElementImpl(useSitePosition, typeText, '\u0000')
return typeElement.type
return ClsTypeElementImpl(useSitePosition, typeText, '\u0000')
}
private val PsiElement.containingKtFile: KtFile?
@@ -7,26 +7,27 @@ package org.jetbrains.kotlin.analysis.api.components
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import com.intellij.psi.PsiTypeElement
import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode
public abstract class KtPsiTypeProvider : KtAnalysisSessionComponent() {
public abstract fun asPsiType(
public abstract fun asPsiTypeElement(
type: KtType,
useSitePosition: PsiElement,
mode: KtTypeMappingMode,
isAnnotationMethod: Boolean,
allowErrorTypes: Boolean
): PsiType?
): PsiTypeElement?
}
public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
/**
* Converts the given [KtType] to [PsiType] under [useSitePosition] context.
* Converts the given [KtType] to [PsiTypeElement] under [useSitePosition] context.
*
* [useSitePosition] is used as the parent of the resulting [PsiType],
* which is in turn used to resolve [PsiType].
* [useSitePosition] is used as the parent of the resulting [PsiTypeElement],
* which is in turn used to resolve [PsiTypeElement].
*
* [useSitePosition] is also used to determine if the given [KtType] needs to be approximated.
* For example, if the given type is local yet available in the same scope of use site, we can
@@ -40,12 +41,27 @@ public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
*
* If [allowErrorTypes] set to true then erroneous types will be replaced with `error.NonExistentClass` type
*/
public fun KtType.asPsiTypeElement(
useSitePosition: PsiElement,
allowErrorTypes: Boolean,
mode: KtTypeMappingMode = KtTypeMappingMode.DEFAULT,
isAnnotationMethod: Boolean = false
): PsiTypeElement? = withValidityAssertion {
analysisSession.psiTypeProvider.asPsiTypeElement(this, useSitePosition, mode, isAnnotationMethod, 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].
*/
public fun KtType.asPsiType(
useSitePosition: PsiElement,
allowErrorTypes: Boolean,
mode: KtTypeMappingMode = KtTypeMappingMode.DEFAULT,
isAnnotationMethod: Boolean = false
): PsiType? = withValidityAssertion {
analysisSession.psiTypeProvider.asPsiType(this, useSitePosition, mode, isAnnotationMethod, allowErrorTypes)
}
): PsiType? =
asPsiTypeElement(useSitePosition, allowErrorTypes, mode, isAnnotationMethod)?.type
}
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.asJava.classes
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.PsiJavaCodeReferenceElement
import com.intellij.psi.PsiReferenceList
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightElement
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.analyzer.KotlinModificationTrackerService
@@ -68,3 +68,67 @@ fun KtClassOrObject.getExternalDependencies(): List<ModificationTracker> {
}
}
}
// There is no other known way found to make PSI types annotated for now (without creating a new instance).
// It seems we need for platform changes to do it more convenient way (KTIJ-141).
private val setPsiTypeAnnotationProvider: (PsiType, TypeAnnotationProvider) -> Unit by lazyPub {
val klass = PsiType::class.java
val providerField = try {
klass.getDeclaredField("myAnnotationProvider")
.also { it.isAccessible = true }
} catch (e: NoSuchFieldException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
} catch (e: SecurityException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
}
{ psiType, provider ->
providerField?.set(psiType, provider)
}
}
fun PsiType.annotateByTypeAnnotationProvider(
annotations: Sequence<List<PsiAnnotation>>,
): PsiType {
val annotationsIterator = annotations.iterator()
if (!annotationsIterator.hasNext()) return this
if (this is PsiPrimitiveType) {
val typeAnnotation = annotationsIterator.next()
return if (typeAnnotation.isEmpty()) {
this
} else {
val provider = TypeAnnotationProvider.Static.create(typeAnnotation.toTypedArray())
// NB: [PsiType#annotate] makes a clone of the given [PsiType] while manipulating the type annotation provider.
// For simple primitive type, it will be okay and intuitive (than reflection hack).
annotate(provider)
}
}
fun recursiveAnnotator(psiType: PsiType) {
if (!annotationsIterator.hasNext()) return
val typeAnnotations = annotationsIterator.next()
when (psiType) {
is PsiPrimitiveType -> return // Primitive type cannot be type parameter so we skip it
is PsiClassType -> {
for (parameterType in psiType.parameters) {
recursiveAnnotator(parameterType)
}
}
is PsiArrayType ->
recursiveAnnotator(psiType.componentType)
else -> {}
}
if (typeAnnotations.isEmpty()) return
val provider = TypeAnnotationProvider.Static.create(typeAnnotations.toTypedArray())
setPsiTypeAnnotationProvider(psiType, provider)
}
recursiveAnnotator(this)
return this
}
@@ -5,8 +5,7 @@
package org.jetbrains.kotlin.light.classes.symbol.annotations
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiElement
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightReferenceListBuilder
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.annotations.*
@@ -14,7 +13,10 @@ import org.jetbrains.kotlin.analysis.api.annotations.KtKClassAnnotationValue.KtN
import org.jetbrains.kotlin.analysis.api.components.buildClassType
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtAnnotatedSymbol
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode
import org.jetbrains.kotlin.asJava.classes.annotateByTypeAnnotationProvider
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.light.classes.symbol.classes.SymbolLightClassBase
@@ -126,3 +128,30 @@ internal fun KtAnnotatedSymbol.computeThrowsList(
annoApp.arguments.forEach { handleAnnotationValue(it.expression) }
}
context(KtAnalysisSession)
internal fun annotateByKtType(
psiType: PsiType,
ktType: KtType,
psiContext: PsiTypeElement,
): PsiType {
fun KtType.getAnnotationsSequence(): Sequence<List<PsiAnnotation>> =
sequence {
yield(annotations.map { annoApp ->
SymbolLightSimpleAnnotation(
annoApp.classId?.asFqNameString(),
psiContext,
annoApp.arguments,
annoApp.psi
)
})
(this@getAnnotationsSequence as? KtNonErrorClassType)?.ownTypeArguments?.forEach { typeProjection ->
typeProjection.type?.let {
yieldAll(it.getAnnotationsSequence())
}
}
}
return psiType.annotateByTypeAnnotationProvider(ktType.getAnnotationsSequence())
}
@@ -93,7 +93,9 @@ internal class SymbolLightFieldForEnumEntry(
private val _type: PsiType by lazyPub {
withEnumEntrySymbol { enumEntrySymbol ->
enumEntrySymbol.returnType.asPsiType(this@SymbolLightFieldForEnumEntry, allowErrorTypes = true) ?: nonExistentType()
enumEntrySymbol.returnType
.asPsiType(this@SymbolLightFieldForEnumEntry, allowErrorTypes = true)
?: nonExistentType()
}
}
@@ -66,23 +66,16 @@ internal class SymbolLightFieldForProperty private constructor(
private val _returnedType: PsiType by lazyPub {
withPropertySymbol { propertySymbol ->
val isDelegated = (propertySymbol as? KtKotlinPropertySymbol)?.isDelegatedProperty == true
when {
isDelegated ->
(kotlinOrigin as? KtProperty)?.delegateExpression?.let {
it.getKtType()?.asPsiType(
this@SymbolLightFieldForProperty,
allowErrorTypes = true,
KtTypeMappingMode.RETURN_TYPE
)
}
else -> propertySymbol.returnType.asPsiType(
this@SymbolLightFieldForProperty,
allowErrorTypes = true,
KtTypeMappingMode.RETURN_TYPE
)
} ?: nonExistentType()
}
val ktType = if (isDelegated)
(kotlinOrigin as? KtProperty)?.delegateExpression?.getKtType()
else
propertySymbol.returnType
ktType?.asPsiType(
this@SymbolLightFieldForProperty,
allowErrorTypes = true,
KtTypeMappingMode.RETURN_TYPE
)
} ?: nonExistentType()
}
private val _isDeprecated: Boolean by lazyPub {
@@ -187,12 +187,14 @@ internal class SymbolLightSimpleMethod(
functionSymbol.returnType.takeUnless { it.isVoidType } ?: return@withFunctionSymbol PsiType.VOID
}
ktType.asPsiType(
ktType.asPsiTypeElement(
this@SymbolLightSimpleMethod,
allowErrorTypes = true,
KtTypeMappingMode.RETURN_TYPE,
this@SymbolLightSimpleMethod.containingClass.isAnnotationType,
)
)?.let {
annotateByKtType(it.type, ktType, it)
}
} ?: nonExistentType()
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtTypeMappingMode
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.elements.KtLightIdentifier
import org.jetbrains.kotlin.light.classes.symbol.*
import org.jetbrains.kotlin.light.classes.symbol.annotations.annotateByKtType
import org.jetbrains.kotlin.light.classes.symbol.methods.SymbolLightMethodBase
import org.jetbrains.kotlin.psi.KtParameter
@@ -77,7 +78,13 @@ internal abstract class SymbolLightParameterCommon(
else -> KtTypeMappingMode.VALUE_PARAMETER
}
ktType.asPsiType(this@SymbolLightParameterCommon, allowErrorTypes = true, typeMappingMode)
ktType.asPsiTypeElement(
this@SymbolLightParameterCommon,
allowErrorTypes = true,
typeMappingMode
)?.let {
annotateByKtType(it.type, ktType, it)
}
} ?: nonExistentType()
if (parameterSymbol.isVararg) {
@@ -17,10 +17,7 @@ import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.light.classes.symbol.*
import org.jetbrains.kotlin.light.classes.symbol.annotations.GranularAnnotationsBox
import org.jetbrains.kotlin.light.classes.symbol.annotations.NullabilityAnnotationsProvider
import org.jetbrains.kotlin.light.classes.symbol.annotations.SymbolAnnotationsProvider
import org.jetbrains.kotlin.light.classes.symbol.annotations.toOptionalFilter
import org.jetbrains.kotlin.light.classes.symbol.annotations.*
import org.jetbrains.kotlin.light.classes.symbol.methods.SymbolLightMethodBase
import org.jetbrains.kotlin.light.classes.symbol.modifierLists.SymbolLightClassModifierList
import org.jetbrains.kotlin.psi.KtParameter
@@ -85,7 +82,10 @@ internal class SymbolLightParameterForReceiver private constructor(
private val _type: PsiType by lazyPub {
withReceiverSymbol { receiver ->
receiver.type.asPsiType(this, allowErrorTypes = true)
val ktType = receiver.type
ktType.asPsiTypeElement(this, allowErrorTypes = true)?.let {
annotateByKtType(it.type, ktType, it)
}
} ?: nonExistentType()
}
@@ -41,12 +41,14 @@ internal fun KtAnalysisSession.mapType(
psiContext: PsiElement,
mode: KtTypeMappingMode
): PsiClassType? {
val psiType = type.asPsiType(
val psiTypeElement = type.asPsiTypeElement(
psiContext,
allowErrorTypes = true,
mode,
)
return psiType as? PsiClassType
return (psiTypeElement?.type as? PsiClassType)?.let {
annotateByKtType(it, type, psiTypeElement) as? PsiClassType
}
}
@@ -165,30 +165,10 @@ internal fun KotlinType.asPsiType(
typeMapper.mapType(this, signatureWriter, mode)
}
// There is no other known way found to make PSI types annotated for now.
// It seems we need for platform changes to do it more convenient way (KTIJ-141).
private val setPsiTypeAnnotationProvider: (PsiType, TypeAnnotationProvider) -> Unit by lazyPub {
val klass = PsiType::class.java
val providerField = try {
klass.getDeclaredField("myAnnotationProvider")
.also { it.isAccessible = true }
} catch (e: NoSuchFieldException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
} catch (e: SecurityException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
}
{ psiType, provider ->
providerField?.set(psiType, provider)
}
}
private fun annotateByKotlinType(
psiType: PsiType,
kotlinType: KotlinType,
psiContext: PsiElement,
psiContext: PsiTypeElement,
): PsiType {
fun KotlinType.getAnnotationsSequence(): Sequence<List<PsiAnnotation>> =
@@ -199,37 +179,7 @@ private fun annotateByKotlinType(
}
}
val annotationsIterator = kotlinType.getAnnotationsSequence().iterator()
if (!annotationsIterator.hasNext()) return psiType
if (psiType is PsiPrimitiveType) {
val annotation = annotationsIterator.next()
val provider = TypeAnnotationProvider.Static.create(annotation.toTypedArray())
return psiType.annotate(provider)
}
fun recursiveAnnotator(psiType: PsiType) {
if (!annotationsIterator.hasNext()) return
val typeAnnotations = annotationsIterator.next()
if (psiType is PsiPrimitiveType) return //Primitive type cannot be type parameter so we skip it
if (psiType is PsiClassType) {
for (parameterType in psiType.parameters) {
recursiveAnnotator(parameterType)
}
} else if (psiType is PsiArrayType) {
recursiveAnnotator(psiType.componentType)
}
if (typeAnnotations.isEmpty()) return
val provider = TypeAnnotationProvider.Static.create(typeAnnotations.toTypedArray())
setPsiTypeAnnotationProvider(psiType, provider)
}
recursiveAnnotator(psiType)
return psiType
return psiType.annotateByTypeAnnotationProvider(kotlinType.getAnnotationsSequence())
}
internal fun KtUltraLightSupport.mapType(
@@ -58,7 +58,7 @@ public final class klass /* klass*/ {
private final int x = 2 /* initializer type: int */;
@org.jetbrains.annotations.NotNull()
public final X annotatedMethod(@org.jetbrains.annotations.NotNull() P<X, P<X, Y>>, @org.jetbrains.annotations.NotNull() Y[]);// annotatedMethod(P<X, P<X, Y>>, Y[])
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()