PY-3569 Inspection to warn if a loop variable is assigned inside the loop

This commit is contained in:
Ilya Kazakevich
2014-01-18 01:01:42 +04:00
committed by Ilya.Kazakevich
parent d912f2d8bf
commit 14bdb823c0
16 changed files with 58 additions and 42 deletions
@@ -1,13 +0,0 @@
<html>
<body>
<span style="font-family: verdana,serif;">
This inspection checks for cases when loop variable is redeclared inside of loop.
</span>
<pre style="font-family: monospace">
for i in xrange(5):
for i in xrange(20, 25):
print("Inner", i)
print("Outer", i)
</pre>
</body>
</html>
@@ -0,0 +1,21 @@
<html>
<body>
<span style="font-family: verdana,serif;">
This inspection checks for cases when loop variable is redeclared inside of loop:
</span>
<pre style="font-family: monospace">
for i in xrange(5):
for i in xrange(20, 25):
print("Inner", i)
print("Outer", i)
</pre>
<span style="font-family: verdana,serif;">
It also warns you if variable declared in <code>with</code> statement is redeclared inside of statement body:
</span>
<pre style="font-family: monospace">
with open("file") as f:
f.read()
f = open("another file")
</pre>
</body>
</html>
+1 -1
View File
@@ -329,7 +329,7 @@
<localInspection language="Python" shortName="PyShadowingNamesInspection" displayName="Shadowing names from outer scopes" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyShadowingNamesInspection"/>
<localInspection language="Python" shortName="PyAbstractClassInspection" displayName="Class must implement all abstract methods" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyAbstractClassInspection"/>
<localInspection language="Python" shortName="PyPep8NamingInspection" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
<localInspection language="Python" shortName="PyAssignmentToForLoopParameterInspection" displayName="Assignment to 'for' loop parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToForLoopParameterInspection"/>
<localInspection language="Python" shortName="PyAssignmentToLoopOrWithParameterInspection" displayName="Assignment to 'for' loop or 'with' statement parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToLoopOrWithParameterInspection"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType"/>
<liveTemplateMacro implementation="com.jetbrains.python.codeInsight.liveTemplates.CollectionElementNameMacro"/>
@@ -511,8 +511,8 @@ INSP.none.function.assignment=Function ''{0}'' doesn''t return anything
INSP.NAME.global.undefined=Global variable is undefined at the module level
INSP.NAME.global.$0.undefined=Global variable ''{0}'' is undefined at the module level
#PyAssignmentToForLoopParameterInspection
INSP.NAME.assignment.to.for.loop.parameter.display.name=Assignment to 'for' loop parameter
#PyAssignmentToLoopOrWithParameterInspection
INSP.NAME.assignment.to.loop.or.with.parameter.display.name=Assignment to 'for' loop or 'with' statement parameter
# Refactoring
# introduce
@@ -19,10 +19,11 @@ import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.PyForPart;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.PyWithStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -34,12 +35,18 @@ import org.jetbrains.annotations.Nullable;
* for i in range(1, 10):
* i = "new value"
* </pre>
* and
* <pre>
* with open("file") as f:
* f.read()
* f = open("another file")
* </pre>
*
* @author link
*/
public class PyAssignmentToForLoopParameterInspection extends PyInspection {
public class PyAssignmentToLoopOrWithParameterInspection extends PyInspection {
private static final String MESSAGE = PyBundle.message("INSP.NAME.assignment.to.for.loop.parameter.display.name");
private static final String MESSAGE = PyBundle.message("INSP.NAME.assignment.to.loop.or.with.parameter.display.name");
@NotNull
@Override
@@ -73,14 +80,9 @@ public class PyAssignmentToForLoopParameterInspection extends PyInspection {
return; //We are checking first time declaration
}
//Find "for" between predecessors until we tree root
PsiElement element = variableFirstTimeDeclaration;
while (!(element instanceof PsiFile) && element != null) {
if (element instanceof PyForPart) {
registerProblem(node, MESSAGE);
return;
}
element = element.getParent();
//Check if variable declared in "for" or "with" statement
if (PsiTreeUtil.getNonStrictParentOfType(variableFirstTimeDeclaration, PyForPart.class, PyWithStatement.class) != null) {
registerProblem(node, MESSAGE);
}
}
}
@@ -1,3 +0,0 @@
for i in range(1, 2):
print(i)
<warning descr="Assignment to 'for' loop parameter">i</warning> = 12
@@ -1,3 +0,0 @@
for i in [1, 2, 3]:
print(i)
(<warning descr="Assignment to 'for' loop parameter">i</warning>, f) = (1, 2)
@@ -1,3 +0,0 @@
for (k, v) in {"K": "V"}.items():
print(k)
<warning descr="Assignment to 'for' loop parameter">k</warning> = "12"
@@ -1,4 +0,0 @@
for i in range(5):
for <warning descr="Assignment to 'for' loop parameter">i</warning> in range(20, 25):
print("Inner", i)
print("Outer", i)
@@ -0,0 +1,3 @@
for i in range(1, 2):
print(i)
<warning descr="Assignment to 'for' loop or 'with' statement parameter">i</warning> = 12
@@ -0,0 +1,3 @@
for i in [1, 2, 3]:
print(i)
(<warning descr="Assignment to 'for' loop or 'with' statement parameter">i</warning>, f) = (1, 2)
@@ -0,0 +1,3 @@
for (k, v) in {"K": "V"}.items():
print(k)
<warning descr="Assignment to 'for' loop or 'with' statement parameter">k</warning> = "12"
@@ -0,0 +1,4 @@
for i in range(5):
for <warning descr="Assignment to 'for' loop or 'with' statement parameter">i</warning> in range(20, 25):
print("Inner", i)
print("Outer", i)
@@ -0,0 +1,3 @@
with open("file") as f:
f.read()
<warning descr="Assignment to 'for' loop or 'with' statement parameter">f</warning> = open("another file")
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.NotNull;
/**
* @author link
*/
public class PyAssignmentToForLoopParameterInspectionTest extends PyInspectionTestCase {
public class PyAssignmentToLoopOrWithParameterInspectionTest extends PyInspectionTestCase {
public void testGood() {
doTest();
@@ -42,12 +42,15 @@ public class PyAssignmentToForLoopParameterInspectionTest extends PyInspectionTe
public void testTwoLoops() {
doTest();
}
public void testWithStatement() {
doTest();
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {
return PyAssignmentToForLoopParameterInspection.class;
return PyAssignmentToLoopOrWithParameterInspection.class;
}
@Override