mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
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()]
|
||||
|
||||
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.
|
||||
for stmt in sorted(statements, key=lambda obj: obj.start_pos):
|
||||
if isinstance(stmt, pr.Import):
|
||||
imps = imports.ImportWrapper(self._evaluator, stmt,
|
||||
for i in imps:
|
||||
iw = imports.ImportWrapper(self._evaluator, i,
|
||||
nested_resolve=True).follow()
|
||||
if stmt.is_nested() and any(not isinstance(i, pr.Module) for i in imps):
|
||||
analysis.add(self._evaluator, 'import-error', stmt)
|
||||
elif not (isinstance(stmt.parent, pr.ForFlow)
|
||||
if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw):
|
||||
analysis.add(self._evaluator, 'import-error', i)
|
||||
for stmt in sorted(stmts, key=lambda obj: obj.start_pos):
|
||||
if not (isinstance(stmt.parent, pr.ForFlow)
|
||||
and stmt.parent.set_stmt == stmt):
|
||||
self._evaluator.eval_statement(stmt)
|
||||
|
||||
|
||||
@@ -103,3 +103,37 @@ def _check_for_exception_catch(evaluator, jedi_obj, exception):
|
||||
return True
|
||||
jedi_obj = jedi_obj.parent
|
||||
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,
|
||||
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):
|
||||
"""
|
||||
@@ -792,22 +804,25 @@ class KeywordStatement(Base):
|
||||
For the following statements: `assert`, `del`, `global`, `nonlocal`,
|
||||
`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):
|
||||
self.name = name
|
||||
self.start_pos = start_pos
|
||||
self._stmt = stmt
|
||||
self.stmt = stmt
|
||||
self.parent = parent
|
||||
|
||||
if stmt is not None:
|
||||
stmt.parent = self
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s(%s): %s>" % (type(self).__name__, self.name, self.stmt)
|
||||
|
||||
def get_code(self):
|
||||
if self._stmt is None:
|
||||
if self.stmt is None:
|
||||
return "%s\n" % self.name
|
||||
else:
|
||||
return '%s %s\n' % (self.name, self._stmt)
|
||||
return '%s %s\n' % (self.name, self.stmt)
|
||||
|
||||
def get_defined_names(self):
|
||||
return []
|
||||
@@ -815,7 +830,7 @@ class KeywordStatement(Base):
|
||||
@property
|
||||
def end_pos(self):
|
||||
try:
|
||||
return self._stmt.end_pos
|
||||
return self.stmt.end_pos
|
||||
except AttributeError:
|
||||
return self.start_pos[0], self.start_pos[1] + len(self.name)
|
||||
|
||||
|
||||
@@ -44,9 +44,26 @@ c.something = None
|
||||
something = a
|
||||
something
|
||||
|
||||
# -----------------
|
||||
# Unused array variables should still raise attribute errors.
|
||||
# -----------------
|
||||
|
||||
# should not raise anything.
|
||||
for loop_variable in [1, 2]:
|
||||
#! name-error
|
||||
x = undefined
|
||||
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