diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt index 537e912efe4..7bab817abba 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.kt @@ -33,10 +33,7 @@ import org.jetbrains.kotlin.descriptors.PackageViewDescriptor import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils @@ -141,6 +138,8 @@ class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSup return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) } + override fun analyze(element: KtElement) = bindingContext + override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection { val filesForFacade = findFilesForFacade(facadeFqName, scope) if (filesForFacade.isEmpty()) return emptyList() diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt index 1da859eacee..58f886912a9 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightAnnotation.kt @@ -17,22 +17,81 @@ package org.jetbrains.kotlin.asJava import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiAnnotation -import com.intellij.psi.PsiAnnotationOwner -import com.intellij.psi.PsiElement +import com.intellij.psi.* import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument +import org.jetbrains.kotlin.resolve.source.getPsi class KtLightAnnotation( override val clsDelegate: PsiAnnotation, override val kotlinOrigin: KtAnnotationEntry, private val owner: PsiAnnotationOwner ) : PsiAnnotation by clsDelegate, KtLightElement { + inner class LightExpressionValue(private val delegate: PsiExpression) : PsiAnnotationMemberValue, PsiExpression by delegate { + val originalExpression: KtExpression? by lazy { + val nameAndValue = getStrictParentOfType() ?: return@lazy null + val annotationEntry = this@KtLightAnnotation.kotlinOrigin + val context = LightClassGenerationSupport.getInstance(project).analyze(annotationEntry) + val resolvedCall = annotationEntry.getResolvedCall(context) ?: return@lazy null + val annotationConstructor = resolvedCall.resultingDescriptor + val parameterName = nameAndValue.name ?: "value" + val parameter = annotationConstructor.valueParameters.singleOrNull { it.name.asString() == parameterName } + ?: return@lazy null + val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return@lazy null + when (resolvedArgument) { + is DefaultValueArgument -> { + (parameter.source.getPsi() as? KtParameter)?.defaultValue + } + + is ExpressionValueArgument -> { + resolvedArgument.valueArgument?.getArgumentExpression() + } + + is VarargValueArgument -> { + val arrayInitializer = parent as? PsiArrayInitializerMemberValue ?: return@lazy null + val exprIndex = arrayInitializer.initializers.indexOf(delegate as PsiAnnotationMemberValue) + if (exprIndex < 0) return@lazy null + resolvedArgument.arguments[exprIndex].getArgumentExpression() + } + + else -> null + } + } + + override fun getLanguage() = KotlinLanguage.INSTANCE + } + + inner class LightArrayInitializerValue(private val delegate: PsiArrayInitializerMemberValue) : PsiArrayInitializerMemberValue by delegate { + private val _initializers by lazy { delegate.initializers.map { wrapAnnotationValue(it) }.toTypedArray() } + + override fun getInitializers() = _initializers + override fun getLanguage() = KotlinLanguage.INSTANCE + } + + private fun wrapAnnotationValue(value: PsiAnnotationMemberValue): PsiAnnotationMemberValue { + return when (value) { + is PsiExpression -> LightExpressionValue(value) + is PsiArrayInitializerMemberValue -> LightArrayInitializerValue(value) + else -> value + } + } + override fun getName() = null override fun setName(newName: String) = throw IncorrectOperationException() override fun getOwner() = owner + override fun findAttributeValue(name: String?) = clsDelegate.findAttributeValue(name)?.let { wrapAnnotationValue(it) } + override fun findDeclaredAttributeValue(name: String?) = clsDelegate.findDeclaredAttributeValue(name)?.let { wrapAnnotationValue(it) } + override fun getText() = kotlinOrigin.text ?: "" override fun getTextRange() = kotlinOrigin.textRange ?: TextRange.EMPTY_RANGE diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java index 9ec68d37c08..d694ef94d64 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java @@ -27,7 +27,9 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtDeclaration; +import org.jetbrains.kotlin.psi.KtElement; import org.jetbrains.kotlin.psi.KtFile; +import org.jetbrains.kotlin.resolve.BindingContext; import java.util.Collection; @@ -72,6 +74,9 @@ public abstract class LightClassGenerationSupport { @Nullable public abstract DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration); + @NotNull + public abstract BindingContext analyze(@NotNull KtElement element); + @NotNull public abstract Collection getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt index 3c791096d90..53fce433d18 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException import org.jetbrains.kotlin.resolve.lazy.ResolveSession @@ -225,6 +226,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG } } + override fun analyze(element: KtElement) = element.analyze(BodyResolveMode.PARTIAL) + override fun getFacadeNames(packageFqName: FqName, scope: GlobalSearchScope): Collection { val facadeFilesInPackage = KotlinFileFacadeClassByPackageIndex.getInstance().get(packageFqName.asString(), project, scope) return facadeFilesInPackage.map { it.javaFileFacadeFqName.shortName().asString() } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 0137191399c..5c9c3c2d7c9 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -641,6 +641,10 @@ + + org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt new file mode 100644 index 00000000000..eac57fc24ab --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinLightConstantExpressionEvaluator.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea + +import com.intellij.psi.PsiConstantEvaluationHelper +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.ConstantExpressionEvaluator +import org.jetbrains.kotlin.asJava.KtLightAnnotation +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.resolve.DelegatingBindingTrace +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator as FrontendConstantExpressionEvaluator + +class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator { + override fun computeConstantExpression(expression: PsiElement, throwExceptionOnOverflow: Boolean): Any? { + return computeExpression(expression, throwExceptionOnOverflow, null) + } + + override fun computeExpression( + expression: PsiElement, + throwExceptionOnOverflow: Boolean, + auxEvaluator: PsiConstantEvaluationHelper.AuxEvaluator? + ): Any? { + if (expression !is KtLightAnnotation.LightExpressionValue) return null + val expressionToCompute = expression.originalExpression ?: return null + val resolutionFacade = expressionToCompute.getResolutionFacade() + val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns) + val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument") + val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null + if (constant.isError) return null + return constant.getValue(TypeUtils.NO_EXPECTED_TYPE) + } +} \ No newline at end of file