diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 018f8284..073846f9 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -570,7 +570,7 @@ class Script(object): # Iterate tuples. unpack_tuple_to_dict(self._evaluator, types, testlist) else: - try_iter_content(self._evaluator.eval_element(node)) + try_iter_content(self._evaluator.goto_definition(node)) ana = [a for a in self._evaluator.analysis if self.path == a.path] return sorted(set(ana), key=lambda x: x.line) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index d9a761b4..fa39f26c 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -409,9 +409,15 @@ class Evaluator(object): # TODO rename to goto_definitions def_ = name.get_definition() if def_.type == 'expr_stmt' and name in def_.get_defined_names(): - return self.eval_statement(def_, name) - call = helpers.call_of_name(name) - return self.eval_element(call) + types = self.eval_statement(def_, name) + elif def_.type == 'for_stmt': + container_types = self.eval_element(def_.children[3]) + for_types = iterable.py__iter__types(self, container_types, def_.children[3]) + types = finder.check_tuple_assignments(self, for_types, name) + else: + call = helpers.call_of_name(name) + types = self.eval_element(call) + return types def goto(self, name): def resolve_implicit_imports(names): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 89de08e4..4cd92deb 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -251,7 +251,6 @@ class NameFinder(object): def _names_to_types(self, names, search_global): types = set() - debug.warning('start nt %s', names) # Add isinstance and other if/assert knowledge. if isinstance(self.name_str, tree.Name): # Ignore FunctionExecution parents for now. diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 7c569915..b5352cd0 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -452,7 +452,6 @@ def py__getitem__(evaluator, types, index, node): if isinstance(typ, Array) and typ.type == 'dict': types.remove(typ) result |= typ.dict_values() - print('ITER', types, py__iter__types(evaluator, types)) return result | py__iter__types(evaluator, types) for typ in types: diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 5a871671..0044973d 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -144,6 +144,51 @@ class Base(object): scope = scope.parent return scope + def get_definition(self): + scope = self + while scope.parent is not None: + parent = scope.parent + if scope.isinstance(Node, Name) and parent.type != 'simple_stmt': + if scope.type == 'testlist_comp': + try: + if isinstance(scope.children[1], CompFor): + return scope.children[1] + except IndexError: + pass + scope = parent + else: + break + return scope + + def assignment_indexes(self): + """ + Returns an array of tuple(int, node) of the indexes that are used in + tuple assignments. + + For example if the name is ``y`` in the following code:: + + x, (y, z) = 2, '' + + would result in ``[(1, xyz_node), (0, yz_node)]``. + """ + indexes = [] + node = self.parent + compare = self + while node is not None: + if is_node(node, 'testlist_comp', 'testlist_star_expr', 'exprlist'): + for i, child in enumerate(node.children): + if child == compare: + indexes.insert(0, (int(i / 2), node)) + break + else: + raise LookupError("Couldn't find the assignment.") + elif isinstance(node, (ExprStmt, CompFor)): + break + + compare = node + node = node.parent + return indexes + def is_scope(self): # Default is not being a scope. Just inherit from Scope. return False @@ -288,22 +333,6 @@ class Name(Leaf): return "<%s: %s@%s,%s>" % (type(self).__name__, self.value, self.start_pos[0], self.start_pos[1]) - def get_definition(self): - scope = self - while scope.parent is not None: - parent = scope.parent - if scope.isinstance(Node, Name) and parent.type != 'simple_stmt': - if scope.type == 'testlist_comp': - try: - if isinstance(scope.children[1], CompFor): - return scope.children[1] - except IndexError: - pass - scope = parent - else: - break - return scope - def is_definition(self): stmt = self.get_definition() if stmt.type in ('funcdef', 'classdef', 'file_input', 'param'): @@ -317,35 +346,6 @@ class Name(Leaf): 'comp_for', 'with_stmt') \ and self in stmt.get_defined_names() - def assignment_indexes(self): - """ - Returns an array of tuple(int, node) of the indexes that are used in - tuple assignments. - - For example if the name is ``y`` in the following code:: - - x, (y, z) = 2, '' - - would result in ``[(1, xyz_node), (0, yz_node)]``. - """ - indexes = [] - node = self.parent - compare = self - while node is not None: - if is_node(node, 'testlist_comp', 'testlist_star_expr', 'exprlist'): - for i, child in enumerate(node.children): - if child == compare: - indexes.insert(0, (int(i / 2), node)) - break - else: - raise LookupError("Couldn't find the assignment.") - elif isinstance(node, (ExprStmt, CompFor)): - break - - compare = node - node = node.parent - return indexes - def nodes_to_execute(self, last_added=False): if last_added is False: yield self @@ -500,7 +500,8 @@ class Node(BaseNode): _IGNORE_EXECUTE_NODES = set([ 'suite', 'subscriptlist', 'subscript', 'simple_stmt', 'sliceop', 'testlist_comp', 'dictorsetmaker', 'trailer', 'decorators', - 'decorated', 'arglist', 'argument' + 'decorated', 'arglist', 'argument', 'exprlist', 'testlist', + 'testlist_safe', 'testlist1' ]) def __init__(self, type, children): @@ -1024,12 +1025,6 @@ class ForStmt(Flow): type = 'for_stmt' __slots__ = () - def nodes_to_execute(self, last_added=False): - # We shouldn't include the definitions. - for child in self.children[3:]: - for node_to_execute in child.nodes_to_execute(): - yield node_to_execute - def get_input_node(self): """ Returns the input node ``y`` from: ``for x in y:``. diff --git a/test/static_analysis/iterable.py b/test/static_analysis/iterable.py index 7e7ab72a..0a1f5a65 100644 --- a/test/static_analysis/iterable.py +++ b/test/static_analysis/iterable.py @@ -4,3 +4,11 @@ a x = [1] x[0], b = {'a': 1, 'b': '2'} + +dct = {3: ''} +for x in dct: + pass + +#! 4 type-error-not-iterable +for x, y in dct: + pass