1
0
forked from VimPlug/jedi

Re-enable AttributeError/NameError detection for more complicated occurances than just statements.

This commit is contained in:
Dave Halter
2014-12-11 19:18:00 +01:00
parent 77fdbac234
commit d11ea73ef4
6 changed files with 65 additions and 16 deletions

View File

@@ -609,17 +609,33 @@ class Script(object):
for o in origins if hasattr(o, 'py__call__')] for o in origins if hasattr(o, 'py__call__')]
def _analysis(self): def _analysis(self):
def check_types(types):
for typ in types:
try:
f = typ.iter_content
except AttributeError:
pass
else:
check_types(f())
#statements = set(chain(*self._parser.module().used_names.values())) #statements = set(chain(*self._parser.module().used_names.values()))
stmts, imp_names = analysis.get_module_statements(self._parser.module()) nodes, imp_names, decorated_funcs = \
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 n in imp_names: for n in imp_names:
iw = imports.ImportWrapper(self._evaluator, n).follow() iw = imports.ImportWrapper(self._evaluator, n).follow()
i = n.get_definition() i = n.get_definition()
if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw): if i.is_nested() and any(not isinstance(i, pr.Module) for i in iw):
analysis.add(self._evaluator, 'import-error', i.namespace_names[-1]) analysis.add(self._evaluator, 'import-error', i.namespace_names[-1])
for stmt in sorted(stmts, key=lambda obj: obj.start_pos): for node in sorted(nodes, key=lambda obj: obj.start_pos):
#if not (isinstance(stmt.parent, pr.ForFlow) and stmt.parent.set_stmt == stmt): #if not (isinstance(stmt.parent, pr.ForFlow) and stmt.parent.set_stmt == stmt):
self._evaluator.eval_statement(stmt) if node.type == 'expr_stmt':
check_types(self._evaluator.eval_statement(node))
else:
self._evaluator.eval_element(node)
for dec_func in decorated_funcs:
er.Function(self._evaluator, dec_func).get_decorated_func()
ana = [a for a in self._evaluator.analysis if self.path == a.path] ana = [a for a in self._evaluator.analysis if self.path == a.path]
return sorted(set(ana), key=lambda x: x.line) return sorted(set(ana), key=lambda x: x.line)

View File

@@ -64,8 +64,8 @@ class Error(object):
def __repr__(self): def __repr__(self):
return '<%s %s: %s@%s,%s>' % (self.__class__.__name__, return '<%s %s: %s@%s,%s>' % (self.__class__.__name__,
self.name, self.path, self.name, self.path,
self._start_pos[0], self._start_pos[1]) self._start_pos[0], self._start_pos[1])
class Warning(Error): class Warning(Error):
@@ -195,6 +195,28 @@ def get_module_statements(module):
Returns the statements used in a module. All these statements should be Returns the statements used in a module. All these statements should be
evaluated to check for potential exceptions. evaluated to check for potential exceptions.
""" """
def check_children(node):
try:
children = node.children
except AttributeError:
return []
else:
nodes = []
for child in children:
nodes += check_children(child)
if child.type == 'trailer':
c = child.children
if c[0] == '(' and c[1] != ')':
if c[1].type != 'arglist':
nodes.append(c[1])
else:
for argument in c[1].children:
if argument.type == 'argument':
nodes.append(argument.children[-1])
elif argument.type != 'operator':
nodes.append(argument)
return nodes
def add_stmts(stmts): def add_stmts(stmts):
new = set() new = set()
for stmt in stmts: for stmt in stmts:
@@ -203,28 +225,36 @@ def get_module_statements(module):
new |= add_stmts(stmt.inputs) new |= add_stmts(stmt.inputs)
stmt = stmt.next stmt = stmt.next
continue continue
if isinstance(stmt, pr.KeywordStatement):
stmt = stmt.stmt stmt = stmt.stmt
if stmt is None: if stmt is None:
continue continue
new.add(stmt) if stmt.type == 'expr_stmt':
new.add(stmt)
for node in stmt.children:
new.update(check_children(node))
if node.type != 'keyword' and stmt.type != 'expr_stmt':
new.add(node)
return new return new
stmts = set() nodes = set()
import_names = set() import_names = set()
decorated_funcs = []
for scope in module.walk(): for scope in module.walk():
for imp in set(scope.imports): for imp in set(scope.imports):
import_names |= set(imp.get_defined_names()) import_names |= set(imp.get_defined_names())
if imp.is_nested(): if imp.is_nested():
import_names |= set(path[-1] for path in imp.paths()) import_names |= set(path[-1] for path in imp.paths())
stmts |= add_stmts(scope.statements) nodes |= add_stmts(scope.statements)
stmts |= add_stmts(r for r in scope.returns if r is not None) nodes |= add_stmts(r for r in scope.returns if r is not None)
try: try:
decorators = scope.decorators decorators = scope.get_decorators()
except AttributeError: except AttributeError:
pass pass
else: else:
stmts |= add_stmts(decorators) if decorators:
return stmts, import_names decorated_funcs.append(scope)
return nodes, import_names, decorated_funcs

View File

@@ -159,7 +159,7 @@ class CompiledObject(Base):
else: else:
raise KeyError("CompiledObject doesn't have an attribute '%s'." % name) raise KeyError("CompiledObject doesn't have an attribute '%s'." % name)
def get_index_types(self, evaluator, index_array): def get_index_types(self, evaluator, index_array=()):
# If the object doesn't have `__getitem__`, just raise the # If the object doesn't have `__getitem__`, just raise the
# AttributeError. # AttributeError.
if not hasattr(self.obj, '__getitem__'): if not hasattr(self.obj, '__getitem__'):

View File

@@ -375,7 +375,7 @@ class FakeImport(pr.Import):
def start_pos(self): def start_pos(self):
return 0, 0 return 0, 0
def _paths(self): def paths(self):
return [[self.name]] return [[self.name]]

View File

@@ -248,6 +248,9 @@ class Array(IterableWrapper):
# Can raise an IndexError # Can raise an IndexError
return self._evaluator.eval_element(self._items()[mixed_index]) return self._evaluator.eval_element(self._items()[mixed_index])
def iter_content(self):
return self.values()
def scope_names_generator(self, position=None): def scope_names_generator(self, position=None):
""" """
It returns e.g. for a list: append, pop, ... It returns e.g. for a list: append, pop, ...

View File

@@ -1,7 +1,7 @@
def generator(): def generator():
yield 1 yield 1
#! 11 type-error-generator #! 12 type-error-generator
generator()[0] generator()[0]
list(generator())[0] list(generator())[0]