FIR IDE: null out for erroneous PsiType conversion

This commit is contained in:
Jinseong Jeon
2021-08-27 16:53:27 -07:00
committed by Ilya Kirillov
parent 8e242655f9
commit 5446a8ad10
10 changed files with 44 additions and 22 deletions
@@ -67,6 +67,7 @@ internal class FirLightFieldForEnumEntry(
private val _type: PsiType by lazyPub { private val _type: PsiType by lazyPub {
analyzeWithSymbolAsContext(enumEntrySymbol) { analyzeWithSymbolAsContext(enumEntrySymbol) {
enumEntrySymbol.annotatedType.type.asPsiType(this@FirLightFieldForEnumEntry) enumEntrySymbol.annotatedType.type.asPsiType(this@FirLightFieldForEnumEntry)
?: this@FirLightFieldForEnumEntry.nonExistentType()
} }
} }
@@ -41,6 +41,7 @@ internal class FirLightFieldForObjectSymbol(
private val _type: PsiType by lazyPub { private val _type: PsiType by lazyPub {
analyzeWithSymbolAsContext(objectSymbol) { analyzeWithSymbolAsContext(objectSymbol) {
objectSymbol.buildSelfClassType().asPsiType(this@FirLightFieldForObjectSymbol) objectSymbol.buildSelfClassType().asPsiType(this@FirLightFieldForObjectSymbol)
?: this@FirLightFieldForObjectSymbol.nonExistentType()
} }
} }
@@ -30,6 +30,7 @@ internal class FirLightFieldForPropertySymbol(
private val _returnedType: PsiType by lazyPub { private val _returnedType: PsiType by lazyPub {
analyzeWithSymbolAsContext(propertySymbol) { analyzeWithSymbolAsContext(propertySymbol) {
propertySymbol.annotatedType.type.asPsiType(this@FirLightFieldForPropertySymbol) propertySymbol.annotatedType.type.asPsiType(this@FirLightFieldForPropertySymbol)
?: this@FirLightFieldForPropertySymbol.nonExistentType()
} }
} }
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.light.classes.symbol package org.jetbrains.kotlin.light.classes.symbol
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProvider import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSessionProvider
@@ -32,3 +34,6 @@ internal inline fun <R> Project.analyzeWithSymbolAsContext(
): R { ): R {
return KtAnalysisSessionProvider.getInstance(this).analyzeWithSymbolAsContext(contextSymbol, action) return KtAnalysisSessionProvider.getInstance(this).analyzeWithSymbolAsContext(contextSymbol, action)
} }
internal fun PsiElement.nonExistentType(): PsiType =
JavaPsiFacade.getElementFactory(project).createTypeFromText("error.NonExistentClass", this)
@@ -149,14 +149,15 @@ internal class FirLightAccessorMethodForSymbol(
override fun getNameIdentifier(): PsiIdentifier = _identifier override fun getNameIdentifier(): PsiIdentifier = _identifier
private val _returnedType: PsiType? by lazyPub { private val _returnedType: PsiType by lazyPub {
if (!isGetter) return@lazyPub PsiType.VOID if (!isGetter) return@lazyPub PsiType.VOID
analyzeWithSymbolAsContext(containingPropertySymbol) { analyzeWithSymbolAsContext(containingPropertySymbol) {
containingPropertySymbol.annotatedType.type.asPsiType(this@FirLightAccessorMethodForSymbol) containingPropertySymbol.annotatedType.type.asPsiType(this@FirLightAccessorMethodForSymbol)
?: this@FirLightAccessorMethodForSymbol.nonExistentType()
} }
} }
override fun getReturnType(): PsiType? = _returnedType override fun getReturnType(): PsiType = _returnedType
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
this === other || this === other ||
@@ -131,6 +131,7 @@ internal class FirLightSimpleMethodForSymbol(
if (isVoidReturnType) return@lazyPub PsiType.VOID if (isVoidReturnType) return@lazyPub PsiType.VOID
analyzeWithSymbolAsContext(functionSymbol) { analyzeWithSymbolAsContext(functionSymbol) {
functionSymbol.annotatedType.type.asPsiType(this@FirLightSimpleMethodForSymbol) functionSymbol.annotatedType.type.asPsiType(this@FirLightSimpleMethodForSymbol)
?: this@FirLightSimpleMethodForSymbol.nonExistentType()
} }
} }
@@ -48,6 +48,7 @@ internal abstract class FirLightParameterBaseForSymbol(
private val _type by lazyPub { private val _type by lazyPub {
val convertedType = analyzeWithSymbolAsContext(parameterSymbol) { val convertedType = analyzeWithSymbolAsContext(parameterSymbol) {
parameterSymbol.annotatedType.type.asPsiType(this@FirLightParameterBaseForSymbol) parameterSymbol.annotatedType.type.asPsiType(this@FirLightParameterBaseForSymbol)
?: this@FirLightParameterBaseForSymbol.nonExistentType()
} }
if (convertedType is PsiArrayType && parameterSymbol.isVararg) { if (convertedType is PsiArrayType && parameterSymbol.isVararg) {
PsiEllipsisType(convertedType.componentType, convertedType.annotationProvider) PsiEllipsisType(convertedType.componentType, convertedType.annotationProvider)
@@ -72,6 +72,7 @@ internal class FirLightParameterForReceiver private constructor(
private val _type: PsiType by lazyPub { private val _type: PsiType by lazyPub {
analyzeWithSymbolAsContext(context) { analyzeWithSymbolAsContext(context) {
receiverTypeAndAnnotations.type.asPsiType(this@FirLightParameterForReceiver) receiverTypeAndAnnotations.type.asPsiType(this@FirLightParameterForReceiver)
?: this@FirLightParameterForReceiver.nonExistentType()
} }
} }
@@ -11,13 +11,21 @@ import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
public abstract class KtPsiTypeProvider : KtAnalysisSessionComponent() { public abstract class KtPsiTypeProvider : KtAnalysisSessionComponent() {
public abstract fun asPsiType(type: KtType, context: PsiElement, mode: TypeMappingMode): PsiType public abstract fun asPsiType(type: KtType, context: PsiElement, mode: TypeMappingMode): PsiType?
} }
public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn { public interface KtPsiTypeProviderMixIn : KtAnalysisSessionMixIn {
/**
* Converts the given [KtType] to [PsiType].
*
* Returns `null` if the conversion encounters any erroneous cases, e.g., errors in type arguments.
* A client can handle such case in its own way. For instance,
* * UAST will return `UastErrorType` as a default error type.
* * LC will return `NonExistentClass` from the [context].
*/
public fun KtType.asPsiType( public fun KtType.asPsiType(
context: PsiElement, context: PsiElement,
mode: TypeMappingMode = TypeMappingMode.DEFAULT mode: TypeMappingMode = TypeMappingMode.DEFAULT,
): PsiType = ): PsiType? =
analysisSession.psiTypeProvider.asPsiType(this, context, mode) analysisSession.psiTypeProvider.asPsiType(this, context, mode)
} }
@@ -43,15 +43,15 @@ internal class KtFirPsiTypeProvider(
override val token: ValidityToken, override val token: ValidityToken,
) : KtPsiTypeProvider(), KtFirAnalysisSessionComponent { ) : KtPsiTypeProvider(), KtFirAnalysisSessionComponent {
override fun asPsiType(type: KtType, context: PsiElement, mode: TypeMappingMode): PsiType = withValidityAssertion { override fun asPsiType(
type: KtType,
context: PsiElement,
mode: TypeMappingMode,
): PsiType? = withValidityAssertion {
type.coneType.asPsiType(rootModuleSession, analysisSession.firResolveState, mode, context) type.coneType.asPsiType(rootModuleSession, analysisSession.firResolveState, mode, context)
} }
} }
private fun PsiElement.nonExistentType() = JavaPsiFacade.getElementFactory(project)
.createTypeFromText("error.NonExistentClass", this)
private fun ConeKotlinType.simplifyType(session: FirSession, state: FirModuleResolveState): ConeKotlinType { private fun ConeKotlinType.simplifyType(session: FirSession, state: FirModuleResolveState): ConeKotlinType {
val substitutor = AnonymousTypesSubstitutor(session, state) val substitutor = AnonymousTypesSubstitutor(session, state)
var currentType = this var currentType = this
@@ -70,11 +70,12 @@ internal fun ConeKotlinType.asPsiType(
state: FirModuleResolveState, state: FirModuleResolveState,
mode: TypeMappingMode, mode: TypeMappingMode,
psiContext: PsiElement, psiContext: PsiElement,
): PsiType { ): PsiType? {
val correctedType = simplifyType(session, state) val correctedType = simplifyType(session, state)
if (correctedType is ConeClassErrorType || correctedType !is SimpleTypeMarker) return psiContext.nonExistentType() if (correctedType is ConeClassErrorType || correctedType !is SimpleTypeMarker) return null
if (correctedType.typeArguments.any { it is ConeClassErrorType }) return psiContext.nonExistentType()
if (correctedType.typeArguments.any { it is ConeClassErrorType }) return null
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS) val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS)
@@ -83,13 +84,14 @@ internal fun ConeKotlinType.asPsiType(
val canonicalSignature = signatureWriter.toString() val canonicalSignature = signatureWriter.toString()
if (canonicalSignature.contains("L<error>")) return psiContext.nonExistentType() if (canonicalSignature.contains("L<error>")) return null
require(!canonicalSignature.contains(SpecialNames.ANONYMOUS_STRING)) require(!canonicalSignature.contains(SpecialNames.ANONYMOUS_STRING))
val signature = StringCharacterIterator(canonicalSignature) val signature = StringCharacterIterator(canonicalSignature)
val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER) val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER)
val typeInfo = TypeInfo.fromString(javaType, false) val typeInfo = TypeInfo.fromString(javaType, false)
val typeText = TypeInfo.createTypeText(typeInfo) ?: return psiContext.nonExistentType() val typeText = TypeInfo.createTypeText(typeInfo) ?: return null
val typeElement = ClsTypeElementImpl(psiContext, typeText, '\u0000') val typeElement = ClsTypeElementImpl(psiContext, typeText, '\u0000')
return typeElement.type return typeElement.type