Quick Fixes: Implement "Remove unused assignment" quickfix
#KT-9931 Fixed
This commit is contained in:
@@ -213,6 +213,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
||||
##### New features
|
||||
|
||||
- [`KT-11525`](https://youtrack.jetbrains.com/issue/KT-11525) Implement "Create type parameter" quickfix
|
||||
- [`KT-9931`](https://youtrack.jetbrains.com/issue/KT-9931) Implement "Remove unused assignment" quickfix
|
||||
|
||||
#### Refactorings
|
||||
|
||||
|
||||
@@ -425,5 +425,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
TYPE_PARAMETER_AS_REIFIED.registerFactory(AddReifiedToTypeParameterOfFunctionFix)
|
||||
|
||||
TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL.registerFactory(TooLongCharLiteralToStringFix)
|
||||
|
||||
UNUSED_VALUE.registerFactory(RemoveUnusedValueFix)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.quickfix
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.sideEffectFree
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
class RemoveUnusedValueFix(expression: KtBinaryExpression) : KotlinQuickFixAction<KtBinaryExpression>(expression) {
|
||||
enum class RemoveMode {
|
||||
REMOVE_ALL, KEEP_INITIALIZE, CANCEL
|
||||
}
|
||||
|
||||
private fun showDialog(variable: KtProperty): RemoveMode {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return RemoveMode.KEEP_INITIALIZE
|
||||
|
||||
val message =
|
||||
"""<html>
|
||||
<body>
|
||||
There are possible side effects found in expressions assigned to the variable '${variable.name}'<br>
|
||||
You can:<br>
|
||||
- <b>Remove</b> the entire assignment, or<br>
|
||||
- <b>Transform</b> assignment right-hand side into the statement on its own.<br>
|
||||
</body>
|
||||
</html>"""
|
||||
val exitCode = Messages.showYesNoCancelDialog(
|
||||
variable.project,
|
||||
message,
|
||||
QuickFixBundle.message("side.effects.warning.dialog.title"),
|
||||
QuickFixBundle.message("side.effect.action.remove"),
|
||||
QuickFixBundle.message("side.effect.action.transform"),
|
||||
QuickFixBundle.message("side.effect.action.cancel"),
|
||||
Messages.getWarningIcon()
|
||||
)
|
||||
return RemoveMode.values()[exitCode]
|
||||
}
|
||||
|
||||
override fun getFamilyName() = "Remove redundant assignment"
|
||||
|
||||
override fun getText() = familyName
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val lhs = element.left as? KtSimpleNameExpression ?: return
|
||||
val rhs = element.right ?: return
|
||||
val variable = lhs.mainReference.resolve() as? KtProperty ?: return
|
||||
val pseudocode = rhs.getContainingPseudocode(element.analyze(BodyResolveMode.PARTIAL))
|
||||
val isSideEffectFree = pseudocode?.getElementValue(rhs)?.createdAt?.sideEffectFree ?: false
|
||||
var removeMode = RemoveMode.REMOVE_ALL
|
||||
if (!isSideEffectFree) {
|
||||
removeMode = showDialog(variable)
|
||||
}
|
||||
|
||||
when (removeMode) {
|
||||
RemoveMode.REMOVE_ALL -> element.delete()
|
||||
RemoveMode.KEEP_INITIALIZE -> element.replace(rhs)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val expression = Errors.UNUSED_VALUE.cast(diagnostic).psiElement
|
||||
if (!KtPsiUtil.isAssignment(expression)) return null
|
||||
if (expression.left !is KtSimpleNameExpression) return null
|
||||
return RemoveUnusedValueFix(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun test() {
|
||||
var i: Int
|
||||
<caret>i = 1
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun test() {
|
||||
var i: Int
|
||||
<caret>}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun foo() = 1
|
||||
|
||||
fun test() {
|
||||
var i: Int
|
||||
<caret>i = foo()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun foo() = 1
|
||||
|
||||
fun test() {
|
||||
var i: Int
|
||||
<caret>foo()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Remove redundant assignment" "false"
|
||||
fun foo(): Int {
|
||||
var i = 1
|
||||
<caret>i = 2
|
||||
return i
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun test(n: Int) {
|
||||
var i: Int
|
||||
<caret>i = n
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Remove redundant assignment" "true"
|
||||
fun test(n: Int) {
|
||||
var i: Int
|
||||
<caret>}
|
||||
@@ -1,4 +1,5 @@
|
||||
// "Make variable mutable" "false"
|
||||
// ACTION: Remove redundant assignment
|
||||
// ERROR: Val cannot be reassigned
|
||||
fun fun1(i: Int) {
|
||||
<caret>i = 2
|
||||
|
||||
@@ -7166,6 +7166,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/removeRedundantAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RemoveRedundantAssignment extends AbstractQuickFixTest {
|
||||
public void testAllFilesPresentInRemoveRedundantAssignment() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeRedundantAssignment"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("constant.kt")
|
||||
public void testConstant() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeRedundantAssignment/constant.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("functionCall.kt")
|
||||
public void testFunctionCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeRedundantAssignment/functionCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notRedundant.kt")
|
||||
public void testNotRedundant() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeRedundantAssignment/notRedundant.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleRef.kt")
|
||||
public void testSimpleRef() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeRedundantAssignment/simpleRef.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/removeToStringInStringTemplate")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user