From 973e8d2a1bfca47e02955f23e92ad34c8d630cde Mon Sep 17 00:00:00 2001 From: "andrey.matveev" Date: Wed, 22 Jul 2020 18:53:19 +0700 Subject: [PATCH] [PY-17901] Fix deleting code with side effects GitOrigin-RevId: 099724adfd94a160e9a40df78dd621ca4f8f18e6 --- ...moveAssignmentStatementTargetQuickFix.java | 47 +++++++++++++++++++ .../PyUnusedLocalInspectionVisitor.java | 14 +++++- .../removeAssignmentStatementTarget.py | 4 ++ .../removeAssignmentStatementTarget_after.py | 4 ++ .../PyRemoveUnusedLocalQuickFixTest.java | 7 +++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 python/python-psi-impl/src/com/jetbrains/python/inspections/quickfix/PyRemoveAssignmentStatementTargetQuickFix.java create mode 100644 python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget.py create mode 100644 python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget_after.py diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/quickfix/PyRemoveAssignmentStatementTargetQuickFix.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/quickfix/PyRemoveAssignmentStatementTargetQuickFix.java new file mode 100644 index 000000000000..24d7b6496777 --- /dev/null +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/quickfix/PyRemoveAssignmentStatementTargetQuickFix.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2014 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 com.jetbrains.python.inspections.quickfix; + +import com.intellij.codeInsight.intention.HighPriorityAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.PyPsiBundle; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; + +public class PyRemoveAssignmentStatementTargetQuickFix implements LocalQuickFix, HighPriorityAction { + @NotNull + @Override + public String getFamilyName() { + return PyPsiBundle.message("QFIX.NAME.remove.target.expr"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class); + if (assignmentStatement == null) return; + final PyExpression expression = assignmentStatement.getAssignedValue(); + if (expression == null) return; + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + PyExpressionStatement statement = elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpressionStatement.class, + expression.getText()); + assignmentStatement.replace(statement); + } +} diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java index d0865f17988e..e3c517b000b7 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java @@ -4,6 +4,7 @@ package com.jetbrains.python.inspections.unusedLocal; import com.google.common.collect.ImmutableMap; import com.intellij.codeInsight.controlflow.ControlFlowUtil; import com.intellij.codeInsight.controlflow.Instruction; +import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.codeInspection.*; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.project.Project; @@ -424,7 +425,18 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { continue; } - registerWarning(element, warningMsg, new PyRemoveStatementQuickFix()); + final PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class); + if (assignmentStatement != null && PsiTreeUtil.isAncestor(assignmentStatement.getLeftHandSideExpression(), element, false)) { + if (assignmentStatement.getRawTargets().length > 1) { + // TODO: consider assignmentStatement.getRawTargets().length > 1 in PY-28782 + continue; + } + if (assignmentStatement.getLeftHandSideExpression() != element) { + registerWarning(element, warningMsg, new ReplaceWithWildCard()); + continue; + } + registerWarning(element, warningMsg, new PyRemoveAssignmentStatementTargetQuickFix(), new PyRemoveStatementQuickFix()); + } } } } diff --git a/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget.py b/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget.py new file mode 100644 index 000000000000..3d9c7879aeee --- /dev/null +++ b/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget.py @@ -0,0 +1,4 @@ +class MyClass(object): + def __init__(self, **kwargs): + thing = kwargs.pop("stuff", None) + print("that's all folks!") \ No newline at end of file diff --git a/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget_after.py b/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget_after.py new file mode 100644 index 000000000000..794b6c4b3d90 --- /dev/null +++ b/python/testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/removeAssignmentStatementTarget_after.py @@ -0,0 +1,4 @@ +class MyClass(object): + def __init__(self, **kwargs): + kwargs.pop("stuff", None) + print("that's all folks!") \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyRemoveUnusedLocalQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/PyRemoveUnusedLocalQuickFixTest.java index 769f9aaaff9c..ed187db02826 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/PyRemoveUnusedLocalQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/PyRemoveUnusedLocalQuickFixTest.java @@ -59,6 +59,13 @@ public class PyRemoveUnusedLocalQuickFixTest extends PyQuickFixTestCase { }); } + // PY-17901 + public void testRemoveAssignmentStatementTarget() { + runWithLanguageLevel(LanguageLevel.getLatest(), () -> { + doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("QFIX.NAME.remove.target.expr")); + }); + } + // PY-32037 public void testGeneratorIterator() { doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("INSP.unused.locals.replace.with.wildcard"));