mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Special warnings for unresolved references in try-except ImportError (PY-3639)
This commit is contained in:
@@ -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
|
||||
+26
@@ -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)
|
||||
+4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user