diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 4ac36dfd..7132d35d 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -222,9 +222,8 @@ def get_executable_nodes(module): ------------ cared for: global_stmt -exec_stmt +exec_stmt # no priority assert_stmt -compound_stmt if_stmt while_stmt for_stmt @@ -233,21 +232,16 @@ try_stmt with_stmt (with_item) (with_var) +print_stmt +del_stmt +return_stmt +raise_stmt yield_expr file_input funcdef param old_lambdef lambdef -print_stmt -del_stmt -pass_stmt -flow_stmt -break_stmt -continue_stmt -return_stmt -raise_stmt -import_stmt import_name import_from (import_as_name) @@ -258,6 +252,7 @@ import_from classdef comp_for (comp_if) ? +decorator ----------- add basic test @@ -298,8 +293,6 @@ simple_stmt testlist_comp # prob ignore and care about it with atom. dictorsetmaker trailer - -decorator decorators decorated @@ -312,7 +305,13 @@ vfplist # not in 3 --------- not existing with parser reductions. small_stmt +import_stmt +flow_stmt +compound_stmt stmt +pass_stmt +break_stmt +continue_stmt comp_op augassign old_test @@ -320,6 +319,7 @@ typedargslist # afaik becomes [param] varargslist # dito vname comp_iter +test_nocond """ def check_children(node): diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 2e4394f6..9e86f66b 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -485,6 +485,11 @@ class Node(BaseNode): """Concrete implementation for interior nodes.""" __slots__ = ('type',) + _IGNORE_EXECUTE_NODES = set([ + 'suite', 'subscriptlist', 'subscript', 'simple_stmt', 'sliceop', + 'testlist_comp', 'dictorsetmaker', 'trailer', 'decorators', 'decorated' + ]) + def __init__(self, type, children): """ Initializer. @@ -502,6 +507,9 @@ class Node(BaseNode): For static analysis. """ result = [] + if self.type not in Node._IGNORE_EXECUTE_NODES: + result.append(self) + for child in self.children: result += child.nodes_to_execute(last_added) return result @@ -655,11 +663,25 @@ class Module(Scope): return True return False + def nodes_to_execute(self, last_added=False): + # Yield itself, class needs to be executed for decorator checks. + result = [] + for child in self.children: + result += child.nodes_to_execute() + return result + class Decorator(BaseNode): type = 'decorator' __slots__ = () + def nodes_to_execute(self, last_added=False): + if self.children[-2] == ')': + node = self.children[-3] + if node != '(': + return node.nodes_to_execute() + return [] + class ClassOrFunc(Scope): __slots__ = () @@ -727,7 +749,7 @@ class Class(ClassOrFunc): # metaclass= raise NotImplementedError('Metaclasses not implemented') # care for the class suite: - for node_to_execute in self.children[-1].nodes_to_execute(False): + for node_to_execute in self.children[-1].nodes_to_execute(): yield node_to_execute @@ -840,7 +862,7 @@ class Function(ClassOrFunc): if param.default is not None: yield param.default # care for the function suite: - for node_to_execute in self.children[-1].nodes_to_execute(False): + for node_to_execute in self.children[-1].nodes_to_execute(): yield node_to_execute @@ -873,7 +895,7 @@ class Lambda(Function): if param.default is not None: yield param.default # Care for the lambda test (last child): - for node_to_execute in self.children[-1].nodes_to_execute(False): + for node_to_execute in self.children[-1].nodes_to_execute(): yield node_to_execute def __repr__(self): @@ -885,7 +907,7 @@ class Flow(BaseNode): def nodes_to_execute(self, last_added=False): for child in self.children: - for node_to_execute in child.nodes_to_execute(False): + for node_to_execute in child.nodes_to_execute(): yield node_to_execute @@ -948,6 +970,16 @@ class TryStmt(Flow): elif node == 'except': yield None + def nodes_to_execute(self, last_added=False): + result = [] + for child in self.children[2::3]: + result += child.nodes_to_execute() + for child in self.children[0::3]: + if child.type == 'except_clause': + # Add the test node and ignore the `as NAME` definition. + result += child.children[1].nodes_to_execute() + return result + class WithStmt(Flow): type = 'with_stmt' @@ -968,6 +1000,16 @@ class WithStmt(Flow): if is_node(node, 'with_item'): return node.children[0] + def nodes_to_execute(self, last_added=False): + result = [] + for child in self.children[1::2]: + if child.type == 'with_item': + # Just ignore the `as EXPR` part - at least for now, because + # most times it's just a name. + child = child.children[0] + result += child.nodes_to_execute() + return result + class Import(BaseNode): __slots__ = () @@ -990,6 +1032,14 @@ class Import(BaseNode): def is_star_import(self): return self.children[-1] == '*' + def nodes_to_execute(self, last_added=False): + """ + `nodes_to_execute` works a bit different for imports, because the names + itself cannot directly get resolved (except on itself). + """ + # TODO couldn't we return the names? Would be nicer. + return [self] + class ImportFrom(Import): type = 'import_from' @@ -1114,7 +1164,10 @@ class ImportName(Import): class KeywordStatement(BaseNode): """ For the following statements: `assert`, `del`, `global`, `nonlocal`, - `raise`, `return`, `yield`, `pass`, `continue`, `break`, `return`, `yield`. + `raise`, `return`, `yield`, `return`, `yield`. + + `pass`, `continue` and `break` are not in there, because they are just + simple keywords and the parser reduces it to a keyword. """ __slots__ = () @@ -1130,6 +1183,12 @@ class KeywordStatement(BaseNode): def keyword(self): return self.children[0].value + def nodes_to_execute(self, last_added=False): + result = [] + for child in self.children: + result += child.nodes_to_execute(last_added) + return result + class AssertStmt(KeywordStatement): __slots__ = () @@ -1147,6 +1206,13 @@ class GlobalStmt(KeywordStatement): def get_global_names(self): return self.children[1::2] + def nodes_to_execute(self, last_added=False): + """ + The global keyword allows to define any name. Even if it doesn't + exist. + """ + return [] + class ReturnStmt(KeywordStatement): __slots__ = () @@ -1202,6 +1268,14 @@ class ExprStmt(BaseNode, DocstringMixin): except IndexError: return None + def nodes_to_execute(self, last_added=False): + # I think evaluating the statment (and possibly returned arrays), + # should be enough for static analysis. + result = [self] + for child in self.children: + result += child.nodes_to_execute(last_added=True) + return result + class Param(BaseNode): """