added PY-2952 "Unnecessary backslash" inspection

This commit is contained in:
Ekaterina Tuzova
2011-03-02 14:44:54 +03:00
parent 44828b4e79
commit bae885d945
11 changed files with 243 additions and 0 deletions
@@ -66,6 +66,9 @@ QFIX.introduce.variable=Introduce variable for statement
QFIX.unresolved.reference.add.future=Add 'from __future__ import with_statement''
# RemoveUnnecessaryBackslashQuickFix
QFIX.remove.unnecessary.backslash=Remove unnecessary backslash in expression
# Intentions: INTN
INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import'
INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module'
@@ -348,6 +351,8 @@ INSP.NAME.oldstyle.class=Old-style class contains new-style class features
# PyCompatibilityInspection
INSP.NAME.compatibility=Code compatibility inspection
# PyUnnecessaryBackslashInspection
INSP.NAME.unnecessary.backslash=Unnecessary backslash
# Refactoring
# introduce
@@ -0,0 +1,62 @@
package com.jetbrains.python.actions;
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.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.Stack;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
/**
* User: catherine
*
* QuickFix to remove all unnecessary backslashes in expression
*/
public class RemoveUnnecessaryBackslashQuickFix implements LocalQuickFix {
@NotNull
public String getName() {
return PyBundle.message("QFIX.remove.unnecessary.backslash");
}
@NotNull
public String getFamilyName() {
return PyBundle.message("INSP.GROUP.python");
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement problemElement = descriptor.getPsiElement();
if (problemElement != null) {
PyElement parent = PsiTreeUtil.getParentOfType(problemElement, PySequenceExpression.class, PyDictLiteralExpression.class,
PyParenthesizedExpression.class, PyArgumentList.class, PyParameterList.class);
removeBackSlash(parent);
}
}
private static void removeBackSlash(PyElement parent) {
if (parent != null) {
Stack<PsiElement> stack = new Stack<PsiElement>();
if (parent instanceof PyParenthesizedExpression)
stack.push(((PyParenthesizedExpression)parent).getContainedExpression());
else
stack.push(parent);
while (!stack.isEmpty()) {
PsiElement el = stack.pop();
PsiWhiteSpace[] children = PsiTreeUtil.getChildrenOfType(el, PsiWhiteSpace.class);
if (children != null) {
for (PsiWhiteSpace ws : children) {
if (ws.getText().contains("\\")) {
ws.delete();
}
}
}
for (PsiElement psiElement : el.getChildren()) {
stack.push(psiElement);
}
}
}
}
}
@@ -0,0 +1,89 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.Stack;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.actions.RemoveUnnecessaryBackslashQuickFix;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
/**
* User: catherine
*
* Inspection to highlight backslashes in places where line continuation is implicit (inside (), [], {}).
*/
public class PyUnnecessaryBackslashInspection extends PyInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return PyBundle.message("INSP.NAME.unnecessary.backslash");
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
return new Visitor(holder);
}
private static class Visitor extends PyInspectionVisitor {
public Visitor(ProblemsHolder holder) {
super(holder);
}
@Override
public void visitPyParameterList(final PyParameterList list) {
findProblem(list);
}
@Override
public void visitPyArgumentList(final PyArgumentList list) {
findProblem(list);
}
@Override
public void visitPyParenthesizedExpression(final PyParenthesizedExpression expression) {
Stack<PsiElement> stack = new Stack<PsiElement>();
stack.push(expression.getContainedExpression());
while (!stack.isEmpty()) {
PsiElement element = stack.pop();
findProblem(element);
for (PsiElement psiElement : element.getChildren()) {
stack.push(psiElement);
}
}
}
@Override
public void visitPyDictLiteralExpression(final PyDictLiteralExpression expression) {
findProblem(expression);
}
@Override
public void visitPyListLiteralExpression(final PyListLiteralExpression expression) {
findProblem(expression);
}
@Override
public void visitPySetLiteralExpression(final PySetLiteralExpression expression) {
findProblem(expression);
}
private void findProblem (final PsiElement expression) {
PsiWhiteSpace[] children = PsiTreeUtil.getChildrenOfType(expression, PsiWhiteSpace.class);
if (children != null) {
for (PsiWhiteSpace ws : children) {
if (ws.getText().contains("\\")) {
registerProblem(ws, "Unnecessary backslash in expression.", new RemoveUnnecessaryBackslashQuickFix());
}
}
}
}
}
}
@@ -54,6 +54,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider {
PyOldStyleClassesInspection.class,
PyCompatibilityInspection.class,
PyListCreationInspection.class,
PyUnnecessaryBackslashInspection.class,
};
}
}
@@ -0,0 +1,47 @@
def foo(year, month, day, hour, minute, second):
if 1900 < year < 2100 and 1 <= month <= 12 \
and 1 <= day <= 31 and 0 <= hour < 24 \
and 0 <= minute < 60 and 0 <= second < 60: # Looks like a valid date
return 1
if (True<warning descr="Unnecessary backslash in expression."> \</warning>
or True<warning descr="Unnecessary backslash in expression."> \</warning>
or False):
print("false")
var1 = (1,1,<warning descr="Unnecessary backslash in expression.">\</warning>
2,<warning descr="Unnecessary backslash in expression.">\</warning>
3,
4)
var2 = [1,2,<warning descr="Unnecessary backslash in expression.">\</warning>
3,<warning descr="Unnecessary backslash in expression.">\</warning>
4]
var3 = <warning descr="Python version 2.5 does not support set literal expressions">{1, 2,<warning descr="Unnecessary backslash in expression.">\</warning>
3,4}</warning>
var4 = {1:1, 2:2,<warning descr="Unnecessary backslash in expression.">\</warning>
3:3,
4:4}
assert (val>4,<warning descr="Unnecessary backslash in expression."> \</warning>
"val is too small")
var5 = (val1 < 20) and \
(val2 < 30) and \
(val3 < 40)
var6 = ('1' + '2' + '3' +<warning descr="Unnecessary backslash in expression."> \</warning>
'4' + '5')
def foo(a, b,<warning descr="Unnecessary backslash in expression."> \</warning>
c):
pass
foo(1, 2,<warning descr="Unnecessary backslash in expression."> \</warning>
3)
@@ -0,0 +1,4 @@
var1 = (1,1,<warning descr="Unnecessary backslash in expression."><caret>\</warning>
2,<warning descr="Unnecessary backslash in expression.">\</warning>
3,
4)
@@ -0,0 +1,4 @@
def foo(a, b,<warning descr="Unnecessary backslash in expression."><caret>\</warning>
c,<warning descr="Unnecessary backslash in expression.">\</warning>
d):
pass
@@ -0,0 +1,4 @@
def foo(a, b,
c,
d):
pass
@@ -0,0 +1,4 @@
var1 = (1,1,
2,
3,
4)
@@ -220,6 +220,26 @@ public class PyQuickFixTest extends PyLightFixtureTestCase {
PyBundle.message("QFIX.list.creation"), true, true);
}
public void testUnnecessaryBackslash() {
String[] testFiles = new String[]{"UnnecessaryBackslash.py"};
myFixture.enableInspections(PyUnnecessaryBackslashInspection.class);
myFixture.configureByFiles(testFiles);
myFixture.checkHighlighting(true, false, true);
IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.remove.unnecessary.backslash"));
myFixture.launchAction(intentionAction);
myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after"));
}
public void testUnnecessaryBackslashInArgumentList() {
String[] testFiles = new String[]{"UnnecessaryBackslashInArguments.py"};
myFixture.enableInspections(PyUnnecessaryBackslashInspection.class);
myFixture.configureByFiles(testFiles);
myFixture.checkHighlighting(true, false, true);
IntentionAction intentionAction = myFixture.getAvailableIntention(PyBundle.message("QFIX.remove.unnecessary.backslash"));
myFixture.launchAction(intentionAction);
myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after"));
}
@Override
@NonNls
protected String getTestDataPath() {
@@ -304,4 +304,7 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase {
doHighlightingTest(PyStringFormatInspection.class);
}
public void testPyUnnecessaryBackslashInspection() { //PY-2952
doHighlightingTest(PyUnnecessaryBackslashInspection.class);
}
}