forked from VimPlug/jedi
Fix try/except checks in static analysis.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Module for statical analysis.
|
Module for statical analysis.
|
||||||
"""
|
"""
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.parser import tree as pr
|
from jedi.parser import tree as pr
|
||||||
@@ -127,59 +128,70 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception, payload=None):
|
|||||||
it.
|
it.
|
||||||
Returns True if the exception was catched.
|
Returns True if the exception was catched.
|
||||||
"""
|
"""
|
||||||
def check_match(cls):
|
def check_match(cls, exception):
|
||||||
try:
|
try:
|
||||||
return isinstance(cls, CompiledObject) and issubclass(exception, cls.obj)
|
return isinstance(cls, CompiledObject) and issubclass(exception, cls.obj)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_try_for_except(obj):
|
def check_try_for_except(obj, exception):
|
||||||
while obj.next is not None:
|
# Only nodes in try
|
||||||
obj = obj.next
|
iterator = iter(obj.children)
|
||||||
if not obj.inputs:
|
for branch_type in iterator:
|
||||||
# No import implies a `except:` catch, which catches
|
colon = next(iterator)
|
||||||
# everything.
|
suite = next(iterator)
|
||||||
return True
|
if branch_type == 'try' \
|
||||||
|
and not (branch_type.start_pos < jedi_obj.start_pos <= suite.end_pos):
|
||||||
|
return False
|
||||||
|
|
||||||
for i in obj.inputs:
|
for node in obj.except_clauses():
|
||||||
except_classes = evaluator.eval_statement(i)
|
if node is None:
|
||||||
|
return True # An exception block that catches everything.
|
||||||
|
else:
|
||||||
|
except_classes = evaluator.eval_element(node)
|
||||||
for cls in except_classes:
|
for cls in except_classes:
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
if isinstance(cls, iterable.Array) and cls.type == 'tuple':
|
if isinstance(cls, iterable.Array) and cls.type == 'tuple':
|
||||||
# multiple exceptions
|
# multiple exceptions
|
||||||
for c in cls.values():
|
for c in cls.values():
|
||||||
if check_match(c):
|
if check_match(c, exception):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if check_match(cls):
|
if check_match(cls, exception):
|
||||||
return True
|
return True
|
||||||
return False
|
|
||||||
|
|
||||||
def check_hasattr(node):
|
def check_hasattr(node):
|
||||||
try:
|
try:
|
||||||
assert len(expression_list) == 1
|
assert node.type == 'power'
|
||||||
call = expression_list[0]
|
base = node.children[0]
|
||||||
assert isinstance(call, pr.Call) and str(call.name) == 'hasattr'
|
assert base.type == 'name' and base.value == 'hasattr'
|
||||||
assert call.next_is_execution()
|
trailer = node.children[1]
|
||||||
execution = call.next
|
assert trailer.type == 'trailer'
|
||||||
assert execution and len(execution) == 2
|
arglist = trailer.children[1]
|
||||||
|
assert arglist.type == 'arglist'
|
||||||
|
from jedi.evaluate.param import Arguments
|
||||||
|
args = list(Arguments(evaluator, arglist).unpack())
|
||||||
|
# Arguments should be very simple
|
||||||
|
assert len(args) == 2
|
||||||
|
|
||||||
# check if the names match
|
# Check name
|
||||||
names = evaluator.eval_statement(execution[1])
|
assert len(args[1]) == 1
|
||||||
|
names = evaluator.eval_element(args[1][0])
|
||||||
assert len(names) == 1 and isinstance(names[0], CompiledObject)
|
assert len(names) == 1 and isinstance(names[0], CompiledObject)
|
||||||
assert names[0].obj == str(payload[1])
|
assert names[0].obj == str(payload[1])
|
||||||
|
|
||||||
objects = evaluator.eval_statement(execution[0])
|
# Check objects
|
||||||
|
assert len(args[0]) == 1
|
||||||
|
objects = evaluator.eval_element(args[0][0])
|
||||||
return payload[0] in objects
|
return payload[0] in objects
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
pass
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
obj = jedi_obj
|
obj = jedi_obj
|
||||||
while obj is not None and not obj.isinstance(pr.Function, pr.Class):
|
while obj is not None and not obj.isinstance(pr.Function, pr.Class):
|
||||||
if obj.isinstance(pr.Flow):
|
if obj.isinstance(pr.Flow):
|
||||||
# try/except catch check
|
# try/except catch check
|
||||||
if obj.isinstance(pr.TryStmt) and check_try_for_except(obj):
|
if obj.isinstance(pr.TryStmt) and check_try_for_except(obj, exception):
|
||||||
return True
|
return True
|
||||||
# hasattr check
|
# hasattr check
|
||||||
if exception == AttributeError and obj.isinstance(pr.IfStmt, pr.WhileStmt):
|
if exception == AttributeError and obj.isinstance(pr.IfStmt, pr.WhileStmt):
|
||||||
@@ -243,6 +255,8 @@ def get_module_statements(module):
|
|||||||
for flow in scope.flows:
|
for flow in scope.flows:
|
||||||
if flow.type == 'for_stmt':
|
if flow.type == 'for_stmt':
|
||||||
nodes.add(flow.children[3])
|
nodes.add(flow.children[3])
|
||||||
|
elif flow.type == 'try_stmt':
|
||||||
|
nodes.update(e for e in flow.except_clauses() if e is not None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
decorators = scope.get_decorators()
|
decorators = scope.get_decorators()
|
||||||
|
|||||||
@@ -955,6 +955,17 @@ class ForStmt(Flow):
|
|||||||
class TryStmt(Flow):
|
class TryStmt(Flow):
|
||||||
type = 'try_stmt'
|
type = 'try_stmt'
|
||||||
|
|
||||||
|
def except_clauses(self):
|
||||||
|
"""
|
||||||
|
Returns the ``test`` nodes found in ``except_clause`` nodes.
|
||||||
|
Returns ``[None]`` for except clauses without an exception given.
|
||||||
|
"""
|
||||||
|
for node in self.children:
|
||||||
|
if node.type == 'except_clause':
|
||||||
|
yield node.children[1]
|
||||||
|
elif node == 'except':
|
||||||
|
yield None
|
||||||
|
|
||||||
|
|
||||||
class WithStmt(Flow):
|
class WithStmt(Flow):
|
||||||
type = 'with_stmt'
|
type = 'with_stmt'
|
||||||
|
|||||||
Reference in New Issue
Block a user