From 250456ab4b9768fa136c1e6954bfbdde800c1c97 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 25 Sep 2015 14:36:54 +0200 Subject: [PATCH] add quickfix for NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION --- .../kotlin/resolve/ConstModifierChecker.kt | 41 ++++++++++-------- .../inspections/KotlinCleanupInspection.kt | 1 + .../kotlin/idea/quickfix/AddModifierFix.java | 2 +- .../kotlin/idea/quickfix/ConstFixFactory.kt | 42 +++++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 2 + idea/testData/inspections/cleanup/cleanup.kt | 6 +++ .../inspections/cleanup/cleanup.kt.after | 6 +++ idea/testData/quickfix/modifiers/constVal.kt | 6 +++ .../quickfix/modifiers/constVal.kt.after | 6 +++ .../idea/quickfix/QuickFixTestGenerated.java | 6 +++ 10 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt create mode 100644 idea/testData/quickfix/modifiers/constVal.kt create mode 100644 idea/testData/quickfix/modifiers/constVal.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ConstModifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ConstModifierChecker.kt index 2d269bc607d..182a6df6a7b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ConstModifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ConstModifierChecker.kt @@ -16,11 +16,14 @@ package org.jetbrains.kotlin.resolve +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.JetTokens -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.JetDeclaration +import org.jetbrains.kotlin.psi.JetProperty public object ConstModifierChecker : DeclarationChecker { override fun check( @@ -31,44 +34,48 @@ public object ConstModifierChecker : DeclarationChecker { ) { if (descriptor !is VariableDescriptor || !declaration.hasModifier(JetTokens.CONST_KEYWORD)) return - val containingDeclaration = descriptor.containingDeclaration val constModifierPsiElement = declaration.modifierList!!.getModifier(JetTokens.CONST_KEYWORD)!! + val diagnostic = checkCanBeConst(declaration, constModifierPsiElement, descriptor) + if (diagnostic != null) { + diagnosticHolder.report(diagnostic) + } + } + + fun checkCanBeConst(declaration: JetDeclaration, + constModifierPsiElement: PsiElement, + descriptor: VariableDescriptor): Diagnostic? { if (descriptor.isVar) { - diagnosticHolder.report(Errors.WRONG_MODIFIER_TARGET.on(constModifierPsiElement, JetTokens.CONST_KEYWORD, "vars")) - return + return Errors.WRONG_MODIFIER_TARGET.on(constModifierPsiElement, JetTokens.CONST_KEYWORD, "vars") } + val containingDeclaration = descriptor.containingDeclaration if (containingDeclaration is ClassDescriptor && containingDeclaration.kind != ClassKind.OBJECT) { - diagnosticHolder.report(Errors.CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT.on(constModifierPsiElement)) - return + return Errors.CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT.on(constModifierPsiElement) } - if (declaration !is JetProperty || descriptor !is PropertyDescriptor) return + if (declaration !is JetProperty || descriptor !is PropertyDescriptor) return null if (declaration.hasDelegate()) { - diagnosticHolder.report(Errors.CONST_VAL_WITH_DELEGATE.on(declaration.delegate!!)) - return + return Errors.CONST_VAL_WITH_DELEGATE.on(declaration.delegate!!) } if (descriptor is PropertyDescriptor && !descriptor.getter!!.isDefault) { - diagnosticHolder.report(Errors.CONST_VAL_WITH_GETTER.on(declaration.getter!!)) - return + return Errors.CONST_VAL_WITH_GETTER.on(declaration.getter!!) } if (!descriptor.type.canBeUsedForConstVal()) { - diagnosticHolder.report(Errors.TYPE_CANT_BE_USED_FOR_CONST_VAL.on(constModifierPsiElement, descriptor.type)) - return + return Errors.TYPE_CANT_BE_USED_FOR_CONST_VAL.on(constModifierPsiElement, descriptor.type) } if (declaration.initializer == null) { - diagnosticHolder.report(Errors.CONST_VAL_WITHOUT_INITIALIZER.on(constModifierPsiElement)) - return + return Errors.CONST_VAL_WITHOUT_INITIALIZER.on(constModifierPsiElement) } if (descriptor.compileTimeInitializer == null) { - diagnosticHolder.report(Errors.CONST_VAL_WITH_NON_CONST_INITIALIZER.on(declaration.initializer!!)) - return + return Errors.CONST_VAL_WITH_NON_CONST_INITIALIZER.on(declaration.initializer!!) } + + return null } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt index 7cf3f445b69..718bee4b7ca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -90,6 +90,7 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, Errors.DEPRECATED_SYMBOL_WITH_MESSAGE, Errors.ACCESS_TO_PRIVATE_TOP_LEVEL_FROM_ANOTHER_FILE, + Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION, Errors.BACKING_FIELD_SYNTAX_DEPRECATED ) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddModifierFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddModifierFix.java index c0408822a98..1c4f904ebe4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddModifierFix.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddModifierFix.java @@ -38,7 +38,7 @@ import static org.jetbrains.kotlin.lexer.JetTokens.ABSTRACT_KEYWORD; public class AddModifierFix extends JetIntentionAction { @NotNull private final JetModifierKeywordToken modifier; - private AddModifierFix(@NotNull JetModifierListOwner element, @NotNull JetModifierKeywordToken modifier) { + public AddModifierFix(@NotNull JetModifierListOwner element, @NotNull JetModifierKeywordToken modifier) { super(element); this.modifier = modifier; } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt new file mode 100644 index 00000000000..64609f0d25d --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConstFixFactory.kt @@ -0,0 +1,42 @@ +/* + * 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/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index e19e1fd786f..726f45505f1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -321,6 +321,8 @@ public class QuickFixRegistrar : QuickFixContributor { UPPER_BOUND_VIOLATED.registerFactory(AddGenericUpperBoundFix.Factory) TYPE_INFERENCE_UPPER_BOUND_VIOLATED.registerFactory(AddGenericUpperBoundFix.Factory) + NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION.registerFactory(ConstFixFactory) + BACKING_FIELD_SYNTAX_DEPRECATED.registerFactory(MigrateBackingFieldSyntaxFix) BACKING_FIELD_USAGE_DEPRECATED.registerFactory(MigrateBackingFieldUsageFix) BACKING_FIELD_USAGE_DEPRECATED.registerFactory(IntroduceBackingPropertyFix) diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt index e2dd14caf2a..7f41c6c0039 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -36,6 +36,12 @@ fun unnecessaryElvis(x: String) = x ?: "" @JavaAnn(1, "abc") class MyClass +val i = 1 + +annotation class Fancy(val param: Int) + +@Fancy(i) class D + class Foo { var x: Int = 0 get = $x diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after index 11f81d0e5e2..4639aefc233 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt.after +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -35,6 +35,12 @@ fun unnecessaryElvis(x: String) = x @JavaAnn(1, arg1 = "abc") class MyClass +const val i = 1 + +annotation class Fancy(val param: Int) + +@Fancy(i) class D + class Foo { var x: Int = 0 get = field diff --git a/idea/testData/quickfix/modifiers/constVal.kt b/idea/testData/quickfix/modifiers/constVal.kt new file mode 100644 index 00000000000..eb272f68d64 --- /dev/null +++ b/idea/testData/quickfix/modifiers/constVal.kt @@ -0,0 +1,6 @@ +// "Add 'const' modifier" "true" +val i = 1 + +annotation class Fancy(val param: Int) + +@Fancy(i) class D diff --git a/idea/testData/quickfix/modifiers/constVal.kt.after b/idea/testData/quickfix/modifiers/constVal.kt.after new file mode 100644 index 00000000000..2c1df035f46 --- /dev/null +++ b/idea/testData/quickfix/modifiers/constVal.kt.after @@ -0,0 +1,6 @@ +// "Add 'const' modifier" "true" +const val i = 1 + +annotation class Fancy(val param: Int) + +@Fancy(i) class D diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index a58b13ce1e7..d9689356325 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -4223,6 +4223,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("constVal.kt") + public void testConstVal() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/modifiers/constVal.kt"); + doTest(fileName); + } + @TestMetadata("finalTrait.kt") public void testFinalTrait() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/modifiers/finalTrait.kt");