PY-28782 Fix remove assignment expression target inspection for multiple targets

(cherry picked from commit 2a8862d459955a23def7941229007e26d076c54e)

IJ-CR-4054

GitOrigin-RevId: 0068c0195a89e2272e9c632acc5494ea7ff0b120
This commit is contained in:
andrey.matveev
2020-12-17 12:54:21 +00:00
committed by intellij-monorepo-bot
parent 017d156ddf
commit 3b99571638
11 changed files with 90 additions and 9 deletions
@@ -22,6 +22,7 @@ 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.PyTokenTypes;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -37,11 +38,19 @@ public class PyRemoveAssignmentStatementTargetQuickFix implements LocalQuickFix,
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);
if (assignmentStatement.getRawTargets().length == 1) {
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);
}
else {
PsiElement possibleNextEq = PsiTreeUtil.nextVisibleLeaf(element);
if (possibleNextEq == null) return;
assert possibleNextEq.getNode().getElementType() == PyTokenTypes.EQ;
element.getParent().deleteChildRange(element, possibleNextEq);
}
}
}
@@ -12,6 +12,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyPsiBundle;
@@ -422,14 +423,18 @@ public final class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
continue;
}
// TODO: consider assignmentStatement.getRawTargets().length > 1 in PY-28782
final PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class);
if (assignmentStatement != null && assignmentStatement.getRawTargets().length == 1 &&
PsiTreeUtil.isAncestor(assignmentStatement.getLeftHandSideExpression(), element, false)) {
if (assignmentStatement != null && !PsiTreeUtil.isAncestor(assignmentStatement.getAssignedValue(), element, false)) {
if (assignmentStatement.getLeftHandSideExpression() == element) {
// Single assignment target (unused = value)
registerWarning(element, warningMsg, new PyRemoveAssignmentStatementTargetQuickFix(), new PyRemoveStatementQuickFix());
}
else if (ArrayUtil.contains(element, assignmentStatement.getRawTargets())) {
// Chained assignment target (used = unused = value)
registerWarning(element, warningMsg, new PyRemoveAssignmentStatementTargetQuickFix());
}
else {
// Unpacking (used, unused = value)
registerWarning(element, warningMsg, new ReplaceWithWildCard());
}
continue;
@@ -0,0 +1,3 @@
def f():
a<caret> = b = 0
return b
@@ -0,0 +1,3 @@
def f():
a = b<caret> = 0
return a
@@ -0,0 +1,3 @@
def f():
a = <caret>unused, b = 42, 42
return a, b
@@ -0,0 +1,3 @@
def f():
a = _, b = 42, 42
return a, b
@@ -0,0 +1,3 @@
def f():
a = b, <caret>unused = 42, 42
return a, b
@@ -0,0 +1,3 @@
def f():
a = b, _ = 42, 42
return a, b
@@ -15,11 +15,13 @@
*/
package com.jetbrains.python.quickFixes;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.testFramework.TestDataPath;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PyQuickFixTestCase;
import com.jetbrains.python.inspections.unusedLocal.PyUnusedLocalInspection;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NotNull;
@TestDataPath("$CONTENT_ROOT/../testData/quickFixes/PyRemoveUnusedLocalQuickFixTest/")
public class PyRemoveUnusedLocalQuickFixTest extends PyQuickFixTestCase {
@@ -66,6 +68,47 @@ public class PyRemoveUnusedLocalQuickFixTest extends PyQuickFixTestCase {
});
}
// PY-28782
public void testRemoveChainedAssignmentStatementFirstTarget() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("QFIX.NAME.remove.target.expr"));
});
}
// PY-28782
public void testRemoveChainedAssignmentStatementSecondTarget() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("QFIX.NAME.remove.target.expr"));
});
}
// PY-28782
public void testRemoveChainedAssignmentStatementUnpackingFirstTarget() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
doTestNotIgnoreTupleUnpacking(PyPsiBundle.message("INSP.unused.locals.replace.with.wildcard"));
});
}
// PY-28782
public void testRemoveChainedAssignmentStatementUnpackingSecondTarget() {
runWithLanguageLevel(LanguageLevel.getLatest(), () -> {
doTestNotIgnoreTupleUnpacking(PyPsiBundle.message("INSP.unused.locals.replace.with.wildcard"));
});
}
private void doTestNotIgnoreTupleUnpacking(@NotNull String hint) {
final String testFileName = getTestName(true);
final PyUnusedLocalInspection inspection = new PyUnusedLocalInspection();
inspection.ignoreTupleUnpacking = false;
myFixture.configureByFile(testFileName + ".py");
myFixture.enableInspections(inspection);
myFixture.checkHighlighting(true, false, false);
final IntentionAction intentionAction = myFixture.findSingleIntention(hint);
assertNotNull(intentionAction);
myFixture.launchAction(intentionAction);
myFixture.checkResultByFile(testFileName + "_after.py", true);
}
// PY-32037
public void testGeneratorIterator() {
doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("INSP.unused.locals.replace.with.wildcard"));