diff --git a/idea/resources/intentionDescriptions/AddConstModifierIntention/after.kt.template b/idea/resources/intentionDescriptions/AddConstModifierIntention/after.kt.template new file mode 100644 index 00000000000..32a6931ca30 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddConstModifierIntention/after.kt.template @@ -0,0 +1 @@ +const val x = 1 diff --git a/idea/resources/intentionDescriptions/AddConstModifierIntention/before.kt.template b/idea/resources/intentionDescriptions/AddConstModifierIntention/before.kt.template new file mode 100644 index 00000000000..4568d4c3955 --- /dev/null +++ b/idea/resources/intentionDescriptions/AddConstModifierIntention/before.kt.template @@ -0,0 +1 @@ +val x = 1 diff --git a/idea/resources/intentionDescriptions/AddConstModifierIntention/description.html b/idea/resources/intentionDescriptions/AddConstModifierIntention/description.html new file mode 100644 index 00000000000..484f7b4645e --- /dev/null +++ b/idea/resources/intentionDescriptions/AddConstModifierIntention/description.html @@ -0,0 +1,3 @@ + +This expression allows to add the 'const' modifier to properties which can be valid compile-time constants. + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index caa8bb9df7e..4005251520f 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1028,6 +1028,11 @@ Kotlin + + org.jetbrains.kotlin.idea.quickfix.AddConstModifierIntention + Kotlin + + org.jetbrains.kotlin.idea.intentions.IntroduceBackingPropertyIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt new file mode 100644 index 00000000000..261aa56f203 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2015 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.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementFactory +import com.intellij.psi.PsiMethodCallExpression +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingIntention +import org.jetbrains.kotlin.idea.search.allScope +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.psi.JetReferenceExpression +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.ConstModifierChecker +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.source.PsiSourceElement + +public class AddConstModifierFix(val property: JetProperty) : AddModifierFix(property, JetTokens.CONST_KEYWORD), CleanupFix { + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + addConstModifier(property) + } + + companion object { + fun addConstModifier(property: JetProperty) { + val project = property.project + val getter = LightClassUtil.getLightClassPropertyMethods(property).getter + + val javaScope = GlobalSearchScope.getScopeRestrictedByFileTypes(project.allScope(), JavaFileType.INSTANCE) + val getterUsages = if (getter != null) + ReferencesSearch.search(getter, javaScope).findAll() + else + emptyList() + + property.addModifier(JetTokens.CONST_KEYWORD) + + val backingField = LightClassUtil.getLightClassPropertyMethods(property).backingField + if (backingField != null) { + val factory = PsiElementFactory.SERVICE.getInstance(project) + val fieldFQName = backingField.containingClass!!.qualifiedName + "." + backingField.name + + getterUsages.forEach { + val call = it.element.getNonStrictParentOfType() + if (call != null && it.element == call.methodExpression) { + val fieldRef = factory.createExpressionFromText(fieldFQName, it.element) + call.replace(fieldRef) + } + } + } + } + } +} + +public class AddConstModifierIntention : JetSelfTargetingIntention(javaClass(), "Add 'const' modifier") { + override fun applyTo(element: JetProperty, editor: Editor) { + AddConstModifierFix.addConstModifier(element) + } + + override fun isApplicableTo(element: JetProperty, caretOffset: Int): Boolean { + if (element.isVar || element.hasDelegate() || element.initializer == null || element.getter?.hasBody() == true) { + return false + } + val propertyDescriptor = element.descriptor as? VariableDescriptor ?: return false + return ConstModifierChecker.checkCanBeConst(element, element, propertyDescriptor) == null + } +} + + +public object ConstFixFactory : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val expr = diagnostic.psiElement as? JetReferenceExpression ?: return null + val bindingContext = expr.analyze(BodyResolveMode.PARTIAL) + val targetDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expr) as? VariableDescriptor ?: return null + val declaration = (targetDescriptor.source as? PsiSourceElement)?.psi as? JetProperty ?: return null + if (ConstModifierChecker.checkCanBeConst(declaration, declaration, targetDescriptor) == null) { + return AddConstModifierFix(declaration) + } + return null + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt deleted file mode 100644 index 64609f0d25d..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2015 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.quickfix - -import com.intellij.codeInsight.intention.IntentionAction -import org.jetbrains.kotlin.descriptors.VariableDescriptor -import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.psi.JetDeclaration -import org.jetbrains.kotlin.psi.JetReferenceExpression -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.ConstModifierChecker -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.source.PsiSourceElement - -public object ConstFixFactory : JetSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val expr = diagnostic.psiElement as? JetReferenceExpression ?: return null - val bindingContext = expr.analyze(BodyResolveMode.PARTIAL) - val targetDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expr) as? VariableDescriptor ?: return null - val declaration = (targetDescriptor.source as? PsiSourceElement)?.psi as? JetDeclaration ?: return null - if (ConstModifierChecker.checkCanBeConst(declaration, declaration, targetDescriptor) == null) { - return object : AddModifierFix(declaration, JetTokens.CONST_KEYWORD), CleanupFix {} - } - return null - } -} diff --git a/idea/testData/intentions/addConstModifier/.intention b/idea/testData/intentions/addConstModifier/.intention new file mode 100644 index 00000000000..2a5d87ff446 --- /dev/null +++ b/idea/testData/intentions/addConstModifier/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.quickfix.AddConstModifierIntention \ No newline at end of file