forked from VimPlug/jedi
new way of gathering statements to evaluate for static analysis
This commit is contained in:
@@ -587,15 +587,16 @@ class Script(object):
|
|||||||
for o in origins if o.is_callable()]
|
for o in origins if o.is_callable()]
|
||||||
|
|
||||||
def _analysis(self):
|
def _analysis(self):
|
||||||
statements = set(chain(*self._parser.module().used_names.values()))
|
#statements = set(chain(*self._parser.module().used_names.values()))
|
||||||
|
stmts, imps = analysis.get_module_statements(self._parser.module())
|
||||||
# Sort the statements so that the results are reproducible.
|
# Sort the statements so that the results are reproducible.
|
||||||
for stmt in sorted(statements, key=lambda obj: obj.start_pos):
|
for i in imps:
|
||||||
if isinstance(stmt, pr.Import):
|
iw = imports.ImportWrapper(self._evaluator, i,
|
||||||
imps = imports.ImportWrapper(self._evaluator, stmt,
|
|
||||||
nested_resolve=True).follow()
|
nested_resolve=True).follow()
|
||||||
if stmt.is_nested() and any(not isinstance(i, pr.Module) for i in imps):
|
if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw):
|
||||||
analysis.add(self._evaluator, 'import-error', stmt)
|
analysis.add(self._evaluator, 'import-error', i)
|
||||||
elif not (isinstance(stmt.parent, pr.ForFlow)
|
for stmt in sorted(stmts, key=lambda obj: obj.start_pos):
|
||||||
|
if not (isinstance(stmt.parent, pr.ForFlow)
|
||||||
and stmt.parent.set_stmt == stmt):
|
and stmt.parent.set_stmt == stmt):
|
||||||
self._evaluator.eval_statement(stmt)
|
self._evaluator.eval_statement(stmt)
|
||||||
|
|
||||||
|
|||||||
@@ -103,3 +103,37 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception):
|
|||||||
return True
|
return True
|
||||||
jedi_obj = jedi_obj.parent
|
jedi_obj = jedi_obj.parent
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_statements(module):
|
||||||
|
"""
|
||||||
|
Returns the statements used in a module. All these statements should be
|
||||||
|
evaluated to check for potential exceptions.
|
||||||
|
"""
|
||||||
|
def add_stmts(stmts):
|
||||||
|
new = set()
|
||||||
|
for stmt in stmts:
|
||||||
|
if isinstance(stmt, pr.Scope):
|
||||||
|
continue
|
||||||
|
if isinstance(stmt, pr.KeywordStatement):
|
||||||
|
stmt = stmt.stmt
|
||||||
|
if stmt is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for expression in stmt.expression_list():
|
||||||
|
if isinstance(expression, pr.Array):
|
||||||
|
new |= add_stmts(expression.values)
|
||||||
|
|
||||||
|
if isinstance(expression, pr.StatementElement):
|
||||||
|
for element in expression.generate_call_path():
|
||||||
|
if isinstance(element, pr.Array):
|
||||||
|
new |= add_stmts(element.values)
|
||||||
|
new.add(stmt)
|
||||||
|
return new
|
||||||
|
|
||||||
|
stmts = set()
|
||||||
|
imports = set()
|
||||||
|
for scope in module.walk():
|
||||||
|
imports |= set(scope.imports)
|
||||||
|
stmts |= add_stmts(scope.statements)
|
||||||
|
return stmts, imports
|
||||||
|
|||||||
@@ -351,6 +351,18 @@ class Scope(Simple, IsScope, DocstringMixin):
|
|||||||
return "<%s: %s@%s-%s>" % (type(self).__name__, name,
|
return "<%s: %s@%s-%s>" % (type(self).__name__, name,
|
||||||
self.start_pos[0], self.end_pos[0])
|
self.start_pos[0], self.end_pos[0])
|
||||||
|
|
||||||
|
def walk(self):
|
||||||
|
yield self
|
||||||
|
for s in self.subscopes:
|
||||||
|
for scope in s.walk():
|
||||||
|
yield scope
|
||||||
|
|
||||||
|
for r in self.statements:
|
||||||
|
while isinstance(r, Flow):
|
||||||
|
for scope in r.walk():
|
||||||
|
yield scope
|
||||||
|
r = r.next
|
||||||
|
|
||||||
|
|
||||||
class Module(IsScope):
|
class Module(IsScope):
|
||||||
"""
|
"""
|
||||||
@@ -792,22 +804,25 @@ class KeywordStatement(Base):
|
|||||||
For the following statements: `assert`, `del`, `global`, `nonlocal`,
|
For the following statements: `assert`, `del`, `global`, `nonlocal`,
|
||||||
`raise`, `return`, `yield`, `pass`, `continue`, `break`, `return`, `yield`.
|
`raise`, `return`, `yield`, `pass`, `continue`, `break`, `return`, `yield`.
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'start_pos', '_stmt', 'parent')
|
__slots__ = ('name', 'start_pos', 'stmt', 'parent')
|
||||||
|
|
||||||
def __init__(self, name, start_pos, parent, stmt=None):
|
def __init__(self, name, start_pos, parent, stmt=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.start_pos = start_pos
|
self.start_pos = start_pos
|
||||||
self._stmt = stmt
|
self.stmt = stmt
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
if stmt is not None:
|
if stmt is not None:
|
||||||
stmt.parent = self
|
stmt.parent = self
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s(%s): %s>" % (type(self).__name__, self.name, self.stmt)
|
||||||
|
|
||||||
def get_code(self):
|
def get_code(self):
|
||||||
if self._stmt is None:
|
if self.stmt is None:
|
||||||
return "%s\n" % self.name
|
return "%s\n" % self.name
|
||||||
else:
|
else:
|
||||||
return '%s %s\n' % (self.name, self._stmt)
|
return '%s %s\n' % (self.name, self.stmt)
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self):
|
||||||
return []
|
return []
|
||||||
@@ -815,7 +830,7 @@ class KeywordStatement(Base):
|
|||||||
@property
|
@property
|
||||||
def end_pos(self):
|
def end_pos(self):
|
||||||
try:
|
try:
|
||||||
return self._stmt.end_pos
|
return self.stmt.end_pos
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return self.start_pos[0], self.start_pos[1] + len(self.name)
|
return self.start_pos[0], self.start_pos[1] + len(self.name)
|
||||||
|
|
||||||
|
|||||||
@@ -44,9 +44,26 @@ c.something = None
|
|||||||
something = a
|
something = a
|
||||||
something
|
something
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# Unused array variables should still raise attribute errors.
|
||||||
|
# -----------------
|
||||||
|
|
||||||
# should not raise anything.
|
# should not raise anything.
|
||||||
for loop_variable in [1, 2]:
|
for loop_variable in [1, 2]:
|
||||||
#! name-error
|
#! name-error
|
||||||
x = undefined
|
x = undefined
|
||||||
loop_variable
|
loop_variable
|
||||||
|
|
||||||
|
#! name-error
|
||||||
|
for loop_variable in [1, 2, undefined]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
#! attribute-error
|
||||||
|
[1, ''.undefined_attr]
|
||||||
|
|
||||||
|
|
||||||
|
def return_one(something):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#! attribute-error
|
||||||
|
return_one(''.undefined_attribute)
|
||||||
|
|||||||
Reference in New Issue
Block a user