Special warnings for unresolved references in try-except ImportError (PY-3639)

This commit is contained in:
Andrey Vlasovskikh
2011-05-24 19:33:35 +04:00
parent 016c5633a9
commit 3a4a00b90c
10 changed files with 113 additions and 23 deletions
@@ -227,6 +227,7 @@ INSP.module.$0.not.found=Module ''{0}'' not found
INSP.unresolved.ref.$0=Unresolved reference ''{0}''
INSP.unresolved.ref.$0.for.class.$1=Unresolved attribute reference ''{0}'' for class ''{1}''
INSP.cannot.find.$0.in.$1=Cannot find reference ''{0}'' in ''{1}''
INSP.try.except.import.error=''{0}'' in try block with ''except ImportError'' should also be defined in except block
# ReturnValueFromInitInspection
INSP.NAME.init.return=__init__ method that returns a value
@@ -170,6 +170,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
@Override
public void visitPyImportStatement(final PyImportStatement node) {
visitPyImportStatementBase(node);
}
@Override
public void visitPyFromImportStatement(PyFromImportStatement node) {
visitPyImportStatementBase(node);
}
private void visitPyImportStatementBase(PyImportStatementBase node) {
myBuilder.startNode(node);
for (PyImportElement importElement : node.getImportElements()) {
final PyReferenceExpression importReference = importElement.getImportReference();
@@ -26,4 +26,10 @@ public interface Scope {
boolean isNonlocal(String name);
boolean containsDeclaration(String name);
/**
* @return all declarations available at the end of the scope
*/
@NotNull
Collection<ScopeVariable> getAllDeclaredVariables();
}
@@ -68,4 +68,20 @@ public class ScopeUtil {
}
return null;
}
public static boolean isDeclaredAndBoundInScope(PyElement element) {
final String name = element.getName();
if (name != null) {
final ScopeOwner owner = getScopeOwner(element);
if (owner != null) {
final Scope scope = ControlFlowCache.getScope(owner);
for (ScopeVariable v : scope.getAllDeclaredVariables()) {
if (v.getName().equals(name)) {
return true;
}
}
}
}
return false;
}
}
@@ -91,7 +91,7 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
continue;
}
}
if (PyAssignmentStatementNavigator.getStatementByTarget(element) != null ||
if (PyAssignmentStatementNavigator.getStatementByTarget(element) != null ||
PyForStatementNavigator.getPyForStatementByIterable(element) != null ||
PyExceptPartNavigator.getPyExceptPartByTarget(element) != null ||
PyListCompExpressionNavigator.getPyListCompExpressionByVariable(element) != null ||
@@ -19,6 +19,7 @@ import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.imports.AutoImportHintAction;
import com.jetbrains.python.codeInsight.imports.AutoImportQuickFix;
import com.jetbrains.python.codeInsight.imports.OptimizeImportsQuickFix;
@@ -71,7 +72,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
@Override
public void inspectionFinished(LocalInspectionToolSession session) {
public void inspectionFinished(LocalInspectionToolSession session, ProblemsHolder holder) {
final Visitor visitor = session.getUserData(KEY);
assert visitor != null;
if (PyCodeInsightSettings.getInstance().HIGHLIGHT_UNUSED_IMPORTS) {
@@ -137,6 +138,25 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
myAllImports.add(node);
}
private static boolean isGuardedByImportError(PyElement node) {
final PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class);
if (importStatement != null) {
final PyTryPart tryPart = PsiTreeUtil.getParentOfType(node, PyTryPart.class);
if (tryPart != null) {
final PyTryExceptStatement tryExceptStatement = PsiTreeUtil.getParentOfType(tryPart, PyTryExceptStatement.class);
if (tryExceptStatement != null) {
for (PyExceptPart exceptPart : tryExceptStatement.getExceptParts()) {
final PyExpression expr = exceptPart.getExceptClass();
if (expr != null && "ImportError".equals(expr.getName())) {
return true;
}
}
}
}
}
return false;
}
@Override
public void visitPyElement(final PyElement node) {
super.visitPyElement(node);
@@ -147,6 +167,15 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
severity = ((PsiReferenceEx) reference).getUnresolvedHighlightSeverity(myTypeEvalContext);
if (severity == null) continue;
}
if (isGuardedByImportError(node)) {
if (PsiTreeUtil.getParentOfType(node, PyImportElement.class) != null) {
if (!ScopeUtil.isDeclaredAndBoundInScope(node)) {
registerProblem(node, PyBundle.message("INSP.try.except.import.error", node.getName()),
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, null);
}
}
return;
}
boolean unresolved;
if (reference instanceof PsiPolyVariantReference) {
final PsiPolyVariantReference poly = (PsiPolyVariantReference)reference;
@@ -178,7 +207,6 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
final PsiElement ref_element = reference.getElement();
final boolean ref_is_importable = PythonReferenceImporter.isImportable(ref_element);
final List<LocalQuickFix> actions = new ArrayList<LocalQuickFix>(2);
HintAction hintAction = null;
if (ref_text.length() <= 0) return; // empty text, nothing to highlight
if (reference.getElement() instanceof PyReferenceExpression) {
PyReferenceExpression refex = (PyReferenceExpression)reference.getElement();
@@ -4,6 +4,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.psi.PyImportElement;
import com.jetbrains.python.psi.PyImportStatement;
import com.jetbrains.python.psi.PyImportStatementBase;
import org.jetbrains.annotations.Nullable;
/**
@@ -14,9 +15,9 @@ public class PyImportStatementNavigator {
}
@Nullable
public static PyImportStatement getImportStatementByElement(final PsiElement element){
final PyImportStatement statement = PsiTreeUtil.getParentOfType(element, PyImportStatement.class, false);
if (statement == null){
public static PyImportStatementBase getImportStatementByElement(final PsiElement element){
final PyImportStatementBase statement = PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class, false);
if (statement == null) {
return null;
}
for (PyImportElement importElement : statement.getImportElements()) {
@@ -1,20 +1,19 @@
0(1) element: null
1(2) element: PyTryExceptStatement
2(3) element: PyTryPart
3(4,6) element: PyFromImportStatement
4(5,6) READ ACCESS: mercurial
5(6,10) READ ACCESS: lsprof
6(7) element: PyExceptPart
7(8) READ ACCESS: ImportError
8(9) element: PyRaiseStatement
9(19) READ ACCESS: Error
10(11) element: PyAssignmentStatement
11(12) WRITE ACCESS: p
12(13) element: PyTryExceptStatement
13(14) element: PyTryPart
14(15) element: PyReturnStatement
15(16) READ ACCESS: foo
16(17) element: PyFinallyPart
17(18) element: PyPrintStatement
18(19) READ ACCESS: p
19() element: null
3(4,5) element: PyFromImportStatement
4(5,9) WRITE ACCESS: lsprof
5(6) element: PyExceptPart
6(7) READ ACCESS: ImportError
7(8) element: PyRaiseStatement
8(18) READ ACCESS: Error
9(10) element: PyAssignmentStatement
10(11) WRITE ACCESS: p
11(12) element: PyTryExceptStatement
12(13) element: PyTryPart
13(14) element: PyReturnStatement
14(15) READ ACCESS: foo
15(16) element: PyFinallyPart
16(17) element: PyPrintStatement
17(18) READ ACCESS: p
18() element: null
@@ -0,0 +1,26 @@
# PY-3639
def f(x):
from <warning descr="Unresolved reference 'foo'">foo</warning> import <warning descr="Unresolved reference 'StringIO'">StringIO</warning>
return StringIO(x)
def f(x):
try:
from <warning descr="Unresolved reference 'foo'">foo</warning> import <warning descr="Module 'StringIO' not found">StringIO</warning>
except Exception:
pass
return x
def f(x):
try:
from foo import <warning descr="'StringIO' in try block with 'except ImportError' should also be defined in except block">StringIO</warning>
except ImportError:
pass
return StringIO(x)
def f(x):
try:
from lib1 import StringIO
except ImportError:
StringIO = lambda x: x
return StringIO(x)
@@ -30,6 +30,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyLightFixtureTestCase
doTest();
}
public void testImportExceptImportError() {
doTest();
}
private void doTest() {
myFixture.configureByFile("inspections/PyUnresolvedReferencesInspection/" + getTestName(true) + ".py");
myFixture.enableInspections(PyUnresolvedReferencesInspection.class);