Put back logic for highlighting only the first read access to a Python unbound variable

This commit is contained in:
Andrey Vlasovskikh
2012-08-10 18:18:59 +04:00
parent 60636aa406
commit eb53afa6dc
2 changed files with 48 additions and 5 deletions
@@ -1,17 +1,23 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.ControlFlowUtil;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.codeInsight.dataflow.DFALimitExceededException;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.actions.AddGlobalQuickFix;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
@@ -102,6 +108,9 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
return;
}
if (variable == null) {
if (!isFirstUnboundRead(node, owner)) {
return;
}
final PsiElement resolved = node.getReference(resolveWithoutImplicits()).resolve();
final boolean isBuiltin = PyBuiltinCache.getInstance(node).hasInBuiltins(resolved);
if (owner instanceof PyClass) {
@@ -127,6 +136,45 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
}
}
private static boolean isFirstUnboundRead(@NotNull PyReferenceExpression node, @NotNull ScopeOwner owner) {
final String nodeName = node.getReferencedName();
final Scope scope = ControlFlowCache.getScope(owner);
final ControlFlow flow = ControlFlowCache.getControlFlow(owner);
final Instruction[] instructions = flow.getInstructions();
final int num = ControlFlowUtil.findInstructionNumberByElement(instructions, node);
if (num < 0) {
return true;
}
final Ref<Boolean> first = Ref.create(true);
ControlFlowUtil.iteratePrev(num, instructions, new Function<Instruction, ControlFlowUtil.Operation>() {
@Override
public ControlFlowUtil.Operation fun(Instruction instruction) {
if (instruction instanceof ReadWriteInstruction) {
final ReadWriteInstruction rwInstruction = (ReadWriteInstruction)instruction;
final String name = rwInstruction.getName();
final PsiElement element = rwInstruction.getElement();
if (element != null && name != null && name.equals(nodeName) && instruction.num() != num) {
try {
if (scope.getDeclaredVariable(element, name) == null) {
final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess();
if (access.isReadAccess()) {
first.set(false);
return ControlFlowUtil.Operation.BREAK;
}
}
}
catch (DFALimitExceededException e) {
first.set(false);
}
return ControlFlowUtil.Operation.CONTINUE;
}
}
return ControlFlowUtil.Operation.NEXT;
}
});
return first.get();
}
@Override
public void visitPyNonlocalStatement(final PyNonlocalStatement node) {
for (PyTargetExpression var : node.getVariables()) {
@@ -10,11 +10,6 @@
<line>14</line>
<description>"Name 'b' can be not defined"</description>
</problem>
<problem>
<file>test.py</file>
<line>15</line>
<description>"Name 'b' can be not defined"</description>
</problem>
<problem>
<file>test.py</file>
<line>51</line>