[ULC] Add constant evaluation to light classes service
This commit is contained in:
+9
-6
@@ -24,9 +24,6 @@ import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder
|
|||||||
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
|
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
|
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolderImpl
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForScript
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.*
|
import org.jetbrains.kotlin.asJava.classes.*
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
|
||||||
@@ -36,7 +33,6 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
|||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings
|
import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -44,9 +40,9 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as ResolveEvaluator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
|
* This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
|
||||||
@@ -64,12 +60,17 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) :
|
|||||||
|
|
||||||
private val ultraLightSupport = object : KtUltraLightSupport {
|
private val ultraLightSupport = object : KtUltraLightSupport {
|
||||||
|
|
||||||
|
//TODO: languageVersionSettings is always default
|
||||||
private val languageVersionSettings: LanguageVersionSettings
|
private val languageVersionSettings: LanguageVersionSettings
|
||||||
get() = getContext().languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT
|
get() = getContext().languageVersionSettings
|
||||||
|
?: LanguageVersionSettingsImpl.DEFAULT
|
||||||
|
|
||||||
override val isReleasedCoroutine
|
override val isReleasedCoroutine
|
||||||
get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||||
|
|
||||||
|
override fun getConstantEvaluator(expression: KtExpression): org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator =
|
||||||
|
ResolveEvaluator(moduleDescriptor, languageVersionSettings, expression.project)
|
||||||
|
|
||||||
override val moduleDescriptor get() = traceHolder.module
|
override val moduleDescriptor get() = traceHolder.module
|
||||||
|
|
||||||
override val moduleName: String get() = JvmCodegenUtil.getModuleName(moduleDescriptor)
|
override val moduleName: String get() = JvmCodegenUtil.getModuleName(moduleDescriptor)
|
||||||
@@ -174,4 +175,6 @@ class CliLightClassGenerationSupport(private val traceHolder: CliTraceHolder) :
|
|||||||
override fun analyzeAnnotation(element: KtAnnotationEntry) = traceHolder.bindingContext.get(BindingContext.ANNOTATION, element)
|
override fun analyzeAnnotation(element: KtAnnotationEntry) = traceHolder.bindingContext.get(BindingContext.ANNOTATION, element)
|
||||||
|
|
||||||
override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext
|
override fun analyzeWithContent(element: KtClassOrObject) = traceHolder.bindingContext
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.asJava
|
|||||||
|
|
||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiConstantEvaluationHelper
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.psi.util.CachedValue
|
import com.intellij.psi.util.CachedValue
|
||||||
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
|
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ import org.jetbrains.annotations.TestOnly
|
|||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||||
import java.util.concurrent.ConcurrentMap
|
|
||||||
|
|
||||||
interface KtUltraLightSupport {
|
interface KtUltraLightSupport {
|
||||||
val moduleName: String
|
val moduleName: String
|
||||||
@@ -23,6 +21,7 @@ interface KtUltraLightSupport {
|
|||||||
val moduleDescriptor: ModuleDescriptor
|
val moduleDescriptor: ModuleDescriptor
|
||||||
val isReleasedCoroutine: Boolean
|
val isReleasedCoroutine: Boolean
|
||||||
fun hasAlias(file: KtFile, shortName: Name?): Boolean = false
|
fun hasAlias(file: KtFile, shortName: Name?): Boolean = false
|
||||||
|
fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported
|
// This property may be removed once IntelliJ versions earlier than 2018.3 become unsupported
|
||||||
@@ -52,4 +51,7 @@ class UltraLightSupportViaService(private val ktElement: KtElement) : KtUltraLig
|
|||||||
|
|
||||||
override val isReleasedCoroutine: Boolean
|
override val isReleasedCoroutine: Boolean
|
||||||
get() = serviceProvidedSupport.isReleasedCoroutine
|
get() = serviceProvidedSupport.isReleasedCoroutine
|
||||||
|
|
||||||
|
override fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator =
|
||||||
|
serviceProvidedSupport.getConstantEvaluator(expression)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -13,6 +13,7 @@ import com.intellij.psi.impl.light.LightTypeElement
|
|||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.asJava.classes.cannotModify
|
import org.jetbrains.kotlin.asJava.classes.cannotModify
|
||||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||||
|
import org.jetbrains.kotlin.asJava.computeExpression
|
||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -39,8 +40,7 @@ open class KtLightPsiLiteral(
|
|||||||
private val lightParent: PsiElement
|
private val lightParent: PsiElement
|
||||||
) : KtLightElementBase(lightParent), PsiLiteralExpression {
|
) : KtLightElementBase(lightParent), PsiLiteralExpression {
|
||||||
|
|
||||||
override fun getValue(): Any? =
|
override fun getValue(): Any? = computeExpression(this)
|
||||||
LanguageConstantExpressionEvaluator.INSTANCE.forLanguage(kotlinOrigin.language)?.computeConstantExpression(this, false)
|
|
||||||
|
|
||||||
override fun getType(): PsiType? {
|
override fun getType(): PsiType? {
|
||||||
val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin)
|
val bindingContext = LightClassGenerationSupport.getInstance(this.project).analyze(kotlinOrigin)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtUltraLightElementWithNullabilityAnnotation
|
import org.jetbrains.kotlin.asJava.classes.KtUltraLightElementWithNullabilityAnnotation
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.runReadAction
|
||||||
import org.jetbrains.kotlin.asJava.elements.PsiElementWithOrigin
|
import org.jetbrains.kotlin.asJava.elements.PsiElementWithOrigin
|
||||||
import org.jetbrains.kotlin.asJava.elements.*
|
import org.jetbrains.kotlin.asJava.elements.*
|
||||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||||
@@ -32,6 +33,10 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be null in scripts and for elements from non-jvm modules.
|
* Can be null in scripts and for elements from non-jvm modules.
|
||||||
@@ -249,3 +254,30 @@ fun fastCheckIsNullabilityApplied(lightElement: KtLightElement<*, PsiModifierLis
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun computeExpression(expression: PsiElement): Any? {
|
||||||
|
fun evalConstantValue(constantValue: ConstantValue<*>): Any? {
|
||||||
|
return if (constantValue is ArrayValue) {
|
||||||
|
val items = constantValue.value.map { evalConstantValue(it) }
|
||||||
|
items.singleOrNull() ?: items
|
||||||
|
} else constantValue.value
|
||||||
|
}
|
||||||
|
|
||||||
|
val expressionToCompute = when (expression) {
|
||||||
|
is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val generationSupport = LightClassGenerationSupport.getInstance(expressionToCompute.project)
|
||||||
|
val evaluator = generationSupport
|
||||||
|
.getUltraLightClassSupport(expressionToCompute)
|
||||||
|
.getConstantEvaluator(expressionToCompute)
|
||||||
|
|
||||||
|
val constant = runReadAction {
|
||||||
|
val evaluatorTrace = DelegatingBindingTrace(generationSupport.analyze(expressionToCompute), "Evaluating annotation argument")
|
||||||
|
evaluator.evaluateExpression(expressionToCompute, evaluatorTrace)
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
if (constant.isError) return null
|
||||||
|
return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE))
|
||||||
|
}
|
||||||
+10
-3
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
|
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
|
||||||
@@ -46,12 +47,18 @@ import java.util.concurrent.ConcurrentMap
|
|||||||
|
|
||||||
class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
|
class IDELightClassGenerationSupport(private val project: Project) : LightClassGenerationSupport() {
|
||||||
|
|
||||||
private inner class KtUltraLightSupportImpl(private val element: KtElement) : KtUltraLightSupport {
|
private class KtUltraLightSupportImpl(private val element: KtElement) : KtUltraLightSupport {
|
||||||
|
|
||||||
private val module = ModuleUtilCore.findModuleForPsiElement(element)
|
private val module = ModuleUtilCore.findModuleForPsiElement(element)
|
||||||
|
|
||||||
|
private val languageVersionSettings
|
||||||
|
get() = module?.languageVersionSettings ?: KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT
|
||||||
|
|
||||||
override val isReleasedCoroutine
|
override val isReleasedCoroutine
|
||||||
get() = module?.languageVersionSettings?.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true
|
get() = languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) ?: true
|
||||||
|
|
||||||
|
override fun getConstantEvaluator(expression: KtExpression): ConstantExpressionEvaluator =
|
||||||
|
ConstantExpressionEvaluator(moduleDescriptor, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, expression.project)
|
||||||
|
|
||||||
private val resolutionFacade get() = element.getResolutionFacade()
|
private val resolutionFacade get() = element.getResolutionFacade()
|
||||||
|
|
||||||
@@ -68,7 +75,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
|
|||||||
override val typeMapper: KotlinTypeMapper by lazyPub {
|
override val typeMapper: KotlinTypeMapper by lazyPub {
|
||||||
KotlinTypeMapper(
|
KotlinTypeMapper(
|
||||||
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
|
BindingContext.EMPTY, ClassBuilderMode.LIGHT_CLASSES,
|
||||||
moduleName, KotlinTypeMapper.LANGUAGE_VERSION_SETTINGS_DEFAULT, // TODO use proper LanguageVersionSettings
|
moduleName, languageVersionSettings,
|
||||||
jvmTarget = JvmTarget.JVM_1_8,
|
jvmTarget = JvmTarget.JVM_1_8,
|
||||||
typePreprocessor = KotlinType::cleanFromAnonymousTypes,
|
typePreprocessor = KotlinType::cleanFromAnonymousTypes,
|
||||||
namePreprocessor = ::tryGetPredefinedName
|
namePreprocessor = ::tryGetPredefinedName
|
||||||
|
|||||||
@@ -19,24 +19,10 @@ package org.jetbrains.kotlin.idea
|
|||||||
import com.intellij.psi.PsiConstantEvaluationHelper
|
import com.intellij.psi.PsiConstantEvaluationHelper
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.impl.ConstantExpressionEvaluator
|
import com.intellij.psi.impl.ConstantExpressionEvaluator
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElementBase
|
import org.jetbrains.kotlin.asJava.computeExpression
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator
|
|
||||||
|
|
||||||
class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
||||||
private fun evalConstantValue(constantValue: ConstantValue<*>): Any? {
|
|
||||||
return if (constantValue is ArrayValue) {
|
|
||||||
val items = constantValue.value.map { evalConstantValue(it) }
|
|
||||||
items.singleOrNull() ?: items
|
|
||||||
} else constantValue.value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? {
|
override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? {
|
||||||
return computeExpression(expression, throwExceptionOnOverflow, null)
|
return computeExpression(expression, throwExceptionOnOverflow, null)
|
||||||
@@ -46,23 +32,5 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
|
|||||||
expression: PsiElement,
|
expression: PsiElement,
|
||||||
throwExceptionOnOverflow: Boolean,
|
throwExceptionOnOverflow: Boolean,
|
||||||
auxEvaluator: PsiConstantEvaluationHelper.AuxEvaluator?
|
auxEvaluator: PsiConstantEvaluationHelper.AuxEvaluator?
|
||||||
): Any? {
|
): Any? = computeExpression(expression)
|
||||||
val expressionToCompute = when (expression) {
|
|
||||||
is KtLightElementBase -> expression.kotlinOrigin as? KtExpression ?: return null
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val resolutionFacade = expressionToCompute.getResolutionFacade()
|
|
||||||
val evaluator = FrontendConstantExpressionEvaluator(
|
|
||||||
resolutionFacade.moduleDescriptor, expressionToCompute.languageVersionSettings, resolutionFacade.project
|
|
||||||
)
|
|
||||||
|
|
||||||
val constant = runReadAction {
|
|
||||||
val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument")
|
|
||||||
evaluator.evaluateExpression(expressionToCompute, evaluatorTrace)
|
|
||||||
} ?: return null
|
|
||||||
|
|
||||||
if (constant.isError) return null
|
|
||||||
return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user