diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDestructuringDeclaration.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDestructuringDeclaration.java index 9c8804c5cf5..c86d5420adc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDestructuringDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtDestructuringDeclaration.java @@ -53,6 +53,11 @@ public class KtDestructuringDeclaration extends KtDeclarationImpl implements KtV return PsiTreeUtil.getNextSiblingOfType(eqNode.getPsi(), KtExpression.class); } + public boolean isVar() { + return getNode().findChildByType(KtTokens.VAR_KEYWORD) != null; + } + + @Override @Nullable public PsiElement getValOrVarKeyword() { return findChildByType(TokenSet.create(VAL_KEYWORD, VAR_KEYWORD)); diff --git a/idea/resources/inspectionDescriptions/CanBeVal.html b/idea/resources/inspectionDescriptions/CanBeVal.html new file mode 100644 index 00000000000..57a4e98079d --- /dev/null +++ b/idea/resources/inspectionDescriptions/CanBeVal.html @@ -0,0 +1,5 @@ + + +This inspection reports mutable local variables (declared with 'var' keyword) that can be made immutable. + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a38a663433f..784324fb2c9 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1391,6 +1391,13 @@ cleanupTool="false" level="WEAK WARNING"/> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeValInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeValInspection.kt new file mode 100644 index 00000000000..00e7c3478ea --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CanBeValInspection.kt @@ -0,0 +1,129 @@ +/* + * 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.inspections + +import com.intellij.codeInspection.IntentionWrapper +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode +import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil +import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode +import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction +import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionWithNext +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.ChangeVariableMutabilityFix +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.readWriteAccess +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import java.util.* + +class CanBeValInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object: KtVisitorVoid() { + private val pseudocodeCache = HashMap() + + override fun visitDeclaration(declaration: KtDeclaration) { + super.visitDeclaration(declaration) + + when (declaration) { + is KtProperty -> { + if (declaration.isVar && declaration.isLocal && canBeVal(declaration, declaration.hasInitializer(), listOf(declaration))) { + reportCanBeVal(declaration) + } + } + + is KtDestructuringDeclaration -> { + val entries = declaration.entries + if (declaration.isVar && entries.all { canBeVal(it, true, entries) }) { + reportCanBeVal(declaration) + } + } + } + } + + private fun canBeVal(declaration: KtVariableDeclaration, hasInitializer: Boolean, allDeclarations: Collection): Boolean { + if (allDeclarations.all { ReferencesSearch.search(it, it.useScope).none() }) { + // do not report for unused var's (otherwise we'll get it highlighted immediately after typing the declaration + return false + } + + if (hasInitializer) { + val hasWriteUsages = ReferencesSearch.search(declaration, declaration.useScope).any { + (it as? KtSimpleNameReference)?.element?.readWriteAccess(useResolveForReadWrite = true)?.isWrite == true + } + return !hasWriteUsages + } + else { + val bindingContext = declaration.analyze(BodyResolveMode.FULL) + val pseudocode = pseudocode(declaration, bindingContext) ?: return false + val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] ?: return false + + val writeInstructions = pseudocode.instructionsIncludingDeadCode + .filterIsInstance() + .filter { (it.target as? AccessTarget.Call)?.resolvedCall?.resultingDescriptor == descriptor } + .toSet() + if (writeInstructions.isEmpty()) return false // incorrect code - do not report + + return writeInstructions.none { canReach(it, writeInstructions) } + } + } + + private fun pseudocode(element: KtElement, bindingContext: BindingContext): Pseudocode? { + val declaration = element.containingDeclarationForPseudocode ?: return null + return pseudocodeCache.getOrPut(declaration) { PseudocodeUtil.generatePseudocode(declaration, bindingContext) } + } + + private fun canReach(from: Instruction, targets: Set, visited: HashSet = HashSet()): Boolean { + // special algorithm for linear code to avoid too deep recursion + var instruction = from + while (instruction is InstructionWithNext) { + val next = instruction.next ?: return false + if (next in visited) continue + if (next in targets) return true + visited.add(next) + instruction = next + } + + for (next in instruction.nextInstructions) { + if (next in visited) continue + if (next in targets) return true + visited.add(next) + if (canReach(next, targets)) return true + } + return false + } + + private fun reportCanBeVal(declaration: KtValVarKeywordOwner) { + val problemDescriptor = holder.manager.createProblemDescriptor( + declaration, + declaration.valOrVarKeyword!!, + "Can be declared as 'val'", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + isOnTheFly, + IntentionWrapper(ChangeVariableMutabilityFix(declaration, false), declaration.containingFile) + ) + holder.registerProblem(problemDescriptor) + } + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableMutabilityFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableMutabilityFix.kt index bf9ca7a7fec..a2029e3e3b8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableMutabilityFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableMutabilityFix.kt @@ -24,32 +24,23 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -class ChangeVariableMutabilityFix(element: KtNamedDeclaration, private val makeVar: Boolean) : KotlinQuickFixAction(element) { +class ChangeVariableMutabilityFix(element: KtValVarKeywordOwner, private val makeVar: Boolean) : KotlinQuickFixAction(element) { override fun getText() = if (makeVar) "Make variable mutable" else "Make variable immutable" override fun getFamilyName(): String = text override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { - return when (element) { - is KtProperty -> element.isVar != makeVar - is KtParameter -> - element.getStrictParentOfType()?.valueParameterList == element.parent - && (element.valOrVarKeyword == KtTokens.VAR_KEYWORD) != makeVar - else -> false - } + val valOrVar = element.valOrVarKeyword?.node?.elementType ?: return false + return (valOrVar == KtTokens.VAR_KEYWORD) != makeVar } override fun invoke(project: Project, editor: Editor?, file: KtFile) { val factory = KtPsiFactory(project) val newKeyword = if (makeVar) factory.createVarKeyword() else factory.createValKeyword() - when (element) { - is KtProperty -> element.valOrVarKeyword.replace(newKeyword) - is KtParameter -> element.valOrVarKeyword!!.replace(newKeyword) - } + element.valOrVarKeyword!!.replace(newKeyword) } companion object { @@ -63,7 +54,7 @@ class ChangeVariableMutabilityFix(element: KtNamedDeclaration, private val makeV val VAL_REASSIGNMENT_FACTORY: KotlinSingleIntentionActionFactory = object: KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { val propertyDescriptor = Errors.VAL_REASSIGNMENT.cast(diagnostic).a - val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) as? KtNamedDeclaration ?: return null + val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) as? KtValVarKeywordOwner ?: return null return ChangeVariableMutabilityFix(declaration, true) } } @@ -72,7 +63,7 @@ class ChangeVariableMutabilityFix(element: KtNamedDeclaration, private val makeV override fun createAction(diagnostic: Diagnostic): IntentionAction? { val element = diagnostic.psiElement return when (element) { - is KtProperty, is KtParameter -> ChangeVariableMutabilityFix(element as KtNamedDeclaration, true) + is KtProperty, is KtParameter -> ChangeVariableMutabilityFix(element as KtValVarKeywordOwner, true) else -> null } } diff --git a/idea/testData/inspections/canBeVal/alwaysAssigned.kt b/idea/testData/inspections/canBeVal/alwaysAssigned.kt new file mode 100644 index 00000000000..34d45fc7a7c --- /dev/null +++ b/idea/testData/inspections/canBeVal/alwaysAssigned.kt @@ -0,0 +1,6 @@ +fun foo(p: Int) { + var v1: Int + var v2: Int = 0 + if (p > 0) v1 = 1 else v1 = 2 + v2 = 1 +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/assignedInLoop.kt b/idea/testData/inspections/canBeVal/assignedInLoop.kt new file mode 100644 index 00000000000..45225c85362 --- /dev/null +++ b/idea/testData/inspections/canBeVal/assignedInLoop.kt @@ -0,0 +1,6 @@ +fun foo(p: Int) { + var v: Int + for (i in 1..10) { + v = i + } +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/assignedTwice.kt b/idea/testData/inspections/canBeVal/assignedTwice.kt new file mode 100644 index 00000000000..d43be3b4dbd --- /dev/null +++ b/idea/testData/inspections/canBeVal/assignedTwice.kt @@ -0,0 +1,5 @@ +fun foo(p: Int) { + var v: Int + v = 0 + if (p > 0) v = 1 +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/desctructuringDeclaration1.kt b/idea/testData/inspections/canBeVal/desctructuringDeclaration1.kt new file mode 100644 index 00000000000..2adcc1edf05 --- /dev/null +++ b/idea/testData/inspections/canBeVal/desctructuringDeclaration1.kt @@ -0,0 +1,6 @@ +fun foo(p: Int) { + var (v1, v2) = getPair() + print(v1) +} + +fun getPair(): Pair = 1 to "" \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/desctructuringDeclaration2.kt b/idea/testData/inspections/canBeVal/desctructuringDeclaration2.kt new file mode 100644 index 00000000000..eb5155f4ac4 --- /dev/null +++ b/idea/testData/inspections/canBeVal/desctructuringDeclaration2.kt @@ -0,0 +1,7 @@ +fun foo(p: Int) { + var (v1, v2) = getPair() + print(v1) + v2 = "" +} + +fun getPair(): Pair = 1 to "" \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/desctructuringDeclarationUnused.kt b/idea/testData/inspections/canBeVal/desctructuringDeclarationUnused.kt new file mode 100644 index 00000000000..9300ab09a34 --- /dev/null +++ b/idea/testData/inspections/canBeVal/desctructuringDeclarationUnused.kt @@ -0,0 +1,5 @@ +fun foo(p: Int) { + var (v1, v2) = getPair() +} + +fun getPair(): Pair = 1 to "" \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/inspectionData/expected.xml b/idea/testData/inspections/canBeVal/inspectionData/expected.xml new file mode 100644 index 00000000000..124baef166b --- /dev/null +++ b/idea/testData/inspections/canBeVal/inspectionData/expected.xml @@ -0,0 +1,55 @@ + + + withInitializer.kt + 4 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + + + alwaysAssigned.kt + 2 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + + + notAssignedWhenNotUsed.kt + 2 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + + + twoVariables.kt + 2 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + + + twoVariables.kt + 3 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + + + desctructuringDeclaration1.kt + 2 + light_idea_test_case + + Local 'var' can be declared as 'val' + Can be declared as 'val' + + diff --git a/idea/testData/inspections/canBeVal/inspectionData/inspections.test b/idea/testData/inspections/canBeVal/inspectionData/inspections.test new file mode 100644 index 00000000000..81d05f0cc24 --- /dev/null +++ b/idea/testData/inspections/canBeVal/inspectionData/inspections.test @@ -0,0 +1 @@ +// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.CanBeValInspection diff --git a/idea/testData/inspections/canBeVal/notAssignedWhenNotUsed.kt b/idea/testData/inspections/canBeVal/notAssignedWhenNotUsed.kt new file mode 100644 index 00000000000..24cf5cbc099 --- /dev/null +++ b/idea/testData/inspections/canBeVal/notAssignedWhenNotUsed.kt @@ -0,0 +1,7 @@ +fun foo(p: Int) { + var v: Int + if (p > 0) { + v = 1 + print(v) + } +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/onlyLocal.kt b/idea/testData/inspections/canBeVal/onlyLocal.kt new file mode 100644 index 00000000000..df559ba9399 --- /dev/null +++ b/idea/testData/inspections/canBeVal/onlyLocal.kt @@ -0,0 +1,10 @@ +var global = 1 + +class C { + var field = 2 + + fun foo() { + print(field) + print(global) + } +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/onlyVar.kt b/idea/testData/inspections/canBeVal/onlyVar.kt new file mode 100644 index 00000000000..dbb60432d7c --- /dev/null +++ b/idea/testData/inspections/canBeVal/onlyVar.kt @@ -0,0 +1,4 @@ +fun foo() { + val v = 1 + print(v) +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/twoVariables.kt b/idea/testData/inspections/canBeVal/twoVariables.kt new file mode 100644 index 00000000000..06515d1b089 --- /dev/null +++ b/idea/testData/inspections/canBeVal/twoVariables.kt @@ -0,0 +1,6 @@ +fun foo(p: Int) { + var v1: Int + var v2: Int + if (p > 0) v1 = 1 else v1 = 2 + v2 = 1 +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/unused.kt b/idea/testData/inspections/canBeVal/unused.kt new file mode 100644 index 00000000000..08b5be627cf --- /dev/null +++ b/idea/testData/inspections/canBeVal/unused.kt @@ -0,0 +1,3 @@ +fun foo() { + var v: Int +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/unusedWithInitializer.kt b/idea/testData/inspections/canBeVal/unusedWithInitializer.kt new file mode 100644 index 00000000000..aae64fd6b66 --- /dev/null +++ b/idea/testData/inspections/canBeVal/unusedWithInitializer.kt @@ -0,0 +1,3 @@ +fun foo() { + var v = 1 +} \ No newline at end of file diff --git a/idea/testData/inspections/canBeVal/withInitializer.kt b/idea/testData/inspections/canBeVal/withInitializer.kt new file mode 100644 index 00000000000..043426599b8 --- /dev/null +++ b/idea/testData/inspections/canBeVal/withInitializer.kt @@ -0,0 +1,8 @@ +fun foo() { + var v1 = 1 + var v2 = 2 + var v3 = 3 + v1 = 1 + v2++ + print(v3) +} \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/.inspection b/idea/testData/quickfix/variables/changeMutability/canBeVal/.inspection new file mode 100644 index 00000000000..cdd811ef674 --- /dev/null +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.CanBeValInspection \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt b/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt new file mode 100644 index 00000000000..952389e35c9 --- /dev/null +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt @@ -0,0 +1,9 @@ +// "Make variable immutable" "true" +fun foo(p: Int) { + var (v1, v2) = getPair()!! + v1 +} + +fun getPair(): Pair? = null + +data class Pair(val a: T1, val b: T2) \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt.after b/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt.after new file mode 100644 index 00000000000..f0eb7a7ec9e --- /dev/null +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt.after @@ -0,0 +1,9 @@ +// "Make variable immutable" "true" +fun foo(p: Int) { + val (v1, v2) = getPair()!! + v1 +} + +fun getPair(): Pair? = null + +data class Pair(val a: T1, val b: T2) \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt b/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt new file mode 100644 index 00000000000..81a90c537f1 --- /dev/null +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt @@ -0,0 +1,5 @@ +// "Make variable immutable" "true" +fun foo(p: Int) { + var v: Int + if (p > 0) v = 1 else v = 2 +} \ No newline at end of file diff --git a/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt.after b/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt.after new file mode 100644 index 00000000000..22c16345e99 --- /dev/null +++ b/idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt.after @@ -0,0 +1,5 @@ +// "Make variable immutable" "true" +fun foo(p: Int) { + val v: Int + if (p > 0) v = 1 else v = 2 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java index eb8c255a57c..1eb091d8af2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java @@ -112,6 +112,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest { doTest(fileName); } + @TestMetadata("canBeVal/inspectionData/inspections.test") + public void testCanBeVal_inspectionData_Inspections_test() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/canBeVal/inspectionData/inspections.test"); + doTest(fileName); + } + @TestMetadata("conflictingExtensionProperty/inspectionData/inspections.test") public void testConflictingExtensionProperty_inspectionData_Inspections_test() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 4ea4adb6f42..817a1aec359 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1581,6 +1581,16 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/variables"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true); } + @TestMetadata("idea/testData/quickfix/variables/changeMutability") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ChangeMutability extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInChangeMutability() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/variables/changeMutability"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true); + } + + } + } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 2d579f87e9b..3485aea4d04 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -7781,6 +7781,27 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/valWithSetter.kt"); doTest(fileName); } + + @TestMetadata("idea/testData/quickfix/variables/changeMutability/canBeVal") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CanBeVal extends AbstractQuickFixTest { + public void testAllFilesPresentInCanBeVal() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/variables/changeMutability/canBeVal"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("multiVariable.kt") + public void testMultiVariable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/canBeVal/multiVariable.kt"); + doTest(fileName); + } + + @TestMetadata("singleVariable.kt") + public void testSingleVariable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/canBeVal/singleVariable.kt"); + doTest(fileName); + } + } } @TestMetadata("idea/testData/quickfix/variables/changeToFunctionInvocation")