From cc661473bcc4b8ee374ec4ebe26b9018769d8fc3 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 11 Nov 2014 17:13:27 +0100 Subject: [PATCH] Trying to change used_names, so that they don't contain name definitions from CompFor. --- jedi/evaluate/__init__.py | 10 ++--- jedi/evaluate/finder.py | 10 ++--- jedi/evaluate/iterable.py | 2 +- jedi/parser/__init__.py | 13 +++++- jedi/parser/representation.py | 83 +++++++++++++++++++++++++---------- test/completion/basic.py | 2 +- 6 files changed, 81 insertions(+), 39 deletions(-) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index d93632eb..b307eddc 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -136,8 +136,8 @@ class Evaluator(object): if seek_name: types = finder.check_tuple_assignments(types, seek_name) - ass_details = stmt.assignment_details - if ass_details and ass_details[0][1] != '=' and not isinstance(stmt, er.InstanceElement): # TODO don't check for this. + #ass_details = stmt.assignment_details + if False and stmt.assignment_details and ass_details[0][1] != '=' and not isinstance(stmt, er.InstanceElement): # TODO don't check for this. expr_list, _operator = ass_details[0] # `=` is always the last character in aug assignments -> -1 operator = copy.copy(_operator) @@ -156,7 +156,7 @@ class Evaluator(object): result = left else: result = precedence.calculate(self, left, operator, result) - elif len(stmt.get_defined_names()) > 1 and seek_name and ass_details: + elif False and len(stmt.get_defined_names()) > 1 and seek_name and ass_details: # Assignment checking is only important if the statement defines # multiple variables. new_result = [] @@ -201,8 +201,8 @@ class Evaluator(object): if isinstance(atom, pr.Name): # This is the first global lookup. stmt = atom.get_definition() - return self.find_types(stmt.get_parent_until(pr.IsScope), atom, - stmt.start_pos, search_global=True) + scope = stmt.get_parent_until(pr.IsScope, include_current=True) + return self.find_types(scope, atom, stmt.start_pos, search_global=True) elif isinstance(atom, pr.Literal): return [compiled.create(self, atom.eval())] else: diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index b6a9393e..58cc599f 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -74,8 +74,8 @@ class NameFinder(object): except KeyError: return [] - names = [name for name in names if name.is_definition()] names = pr.filter_after_position(names, position) + names = [name for name in names if name.is_definition()] # Only the names defined in the last position are valid definitions. last_names = [] @@ -287,12 +287,8 @@ class NameFinder(object): types += check_tuple_assignments(for_types, name) elif isinstance(typ, pr.Param): types += self._eval_param(typ) - elif typ.isinstance(pr.ExprStmt): - if typ.is_global(): - # global keyword handling. - types += evaluator.find_types(typ.parent.parent, str(name)) - else: - types += self._remove_statements(typ, name) + elif typ.isinstance(pr.ExprStmt, pr.CompFor): + types += self._remove_statements(typ, name) elif isinstance(typ, pr.Import): types += imports.ImportWrapper(self._evaluator, name).follow() else: diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index d7dc46e2..a9753bb5 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -222,7 +222,7 @@ class Array(IterableWrapper): if array_node in (']', '}', ')'): return [] # Direct closing bracket, doesn't contain items. - if pr.is_node(array_node, 'testlist_comp', 'testlist_star_expr', 'testlist'): + if pr.is_node(array_node, 'testlist_comp'): return array_node.children[::2] elif pr.is_node(array_node, 'dictorsetmaker'): kv = [] diff --git a/jedi/parser/__init__.py b/jedi/parser/__init__.py index 5e021f18..39eddd7c 100644 --- a/jedi/parser/__init__.py +++ b/jedi/parser/__init__.py @@ -73,6 +73,7 @@ class Parser(object): 'for_stmt': pr.ForStmt, 'while_stmt': pr.WhileStmt, 'try_stmt': pr.TryStmt, + 'comp_for': pr.CompFor, } self._ast_mapping = dict((getattr(pytree.python_symbols, k), v) @@ -126,6 +127,11 @@ class Parser(object): arr = self.scope_names_stack[-1].setdefault(n.value, []) arr.append(n) new_node.names_dict = scope_names + elif isinstance(new_node, pr.CompFor): + # The name definitions of comprehenions shouldn't be part of the + # current scope. They are part of the comprehension scope. + for n in new_node.get_defined_names(): + self.scope_names_stack[-1][n.value].remove(n) return new_node def convert_leaf(self, grammar, type, value, prefix, start_pos): @@ -176,8 +182,11 @@ class Parser(object): clear_names(c.children) except AttributeError: if isinstance(c, pr.Name): - self.scope_names_stack[-1][c.value].remove(c) - self.used_names[c.value].remove(c) + try: + self.scope_names_stack[-1][c.value].remove(c) + self.used_names[c.value].remove(c) + except ValueError: + pass # This may happen with CompFor. for dfa, state, node in stack[start_index:]: clear_names(children=node[1]) diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index cd7eef5d..1b2cac45 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -253,7 +253,19 @@ class Name(Leaf): self.start_pos[0], self.start_pos[1]) def get_definition(self): - return self.parent.get_parent_until((ArrayStmt, StatementElement, Node), reverse=True) + scope = self.parent + while scope.parent is not None: + if scope.isinstance(Node): + if scope.type == python_symbols.testlist_comp: + try: + if isinstance(scope.children[1], CompFor): + return scope.children[1] + except IndexError: + pass + else: + break + scope = scope.parent + return scope def is_definition(self): stmt = self.get_definition() @@ -264,7 +276,7 @@ class Name(Leaf): elif isinstance(stmt, Param): return self == stmt.get_name() else: - return isinstance(stmt, (ExprStmt, Import)) \ + return isinstance(stmt, (ExprStmt, Import, CompFor)) \ and self in stmt.get_defined_names() def assignment_indexes(self): @@ -289,6 +301,8 @@ class Name(Leaf): break else: raise LookupError("Couldn't find the assignment.") + elif isinstance(node, (ExprStmt, CompFor)): + break compare = node node = node.parent @@ -1131,6 +1145,27 @@ class YieldExpr(Simple): pass +def _defined_names(current): + """ + A helper function to find the defined names in statements, for loops and + list comprehensions. + """ + names = [] + if is_node(current, 'testlist_star_expr', 'testlist_comp', 'exprlist'): + for child in current.children[::2]: + names += _defined_names(child) + elif is_node(current, 'atom'): + names += _defined_names(current.children[1]) + elif is_node(current, 'power'): + if current.children[-2] != '**': # Just if there's no operation + trailer = current.children[-1] + if trailer.children[0] == '.': + names.append(trailer.children[1]) + else: + names.append(current) + return names + + class Statement(Simple, DocstringMixin): """ This is the class for all the possible statements. Which means, this class @@ -1167,23 +1202,7 @@ class Statement(Simple, DocstringMixin): self.expression_list() def get_defined_names(self): - def check_tuple(current): - names = [] - if is_node(current, 'testlist_star_expr') or is_node(current, 'testlist_comp'): - for child in current.children[::2]: - names += check_tuple(child) - elif is_node(current, 'atom'): - names += check_tuple(current.children[1]) - elif is_node(current, 'power'): - if current.children[-2] != '**': # Just if there's no operation - trailer = current.children[-1] - if trailer.children[0] == '.': - names.append(trailer.children[1]) - else: - names.append(current) - return names - - return list(chain.from_iterable(check_tuple(self.children[i]) + return list(chain.from_iterable(_defined_names(self.children[i]) for i in range(0, len(self.children) - 2, 2) if self.children[i + 1].value == '=')) @@ -1247,10 +1266,6 @@ class Statement(Simple, DocstringMixin): as_name.start_pos, as_name.end_pos, self)) return dct - def is_global(self): - p = self.parent - return isinstance(p, KeywordStatement) and p.name == 'global' - @property def assignment_details(self): """ @@ -1483,6 +1498,28 @@ class Array(StatementElement): return "<%s: %s%s>" % (type(self).__name__, typ, self.values) +class CompFor(Simple): + def is_scope(self): + return True + + @property + def names_dict(self): + dct = {} + for name in self.get_defined_names(): + arr = dct.setdefault(name.value, []) + arr.append(name) + return dct + + def get_rhs(self): + return self.children[3] + + def get_defined_names(self): + return _defined_names(self.children[1]) + + def scope_names_generator(self, position): + yield self, [] + + class ListComprehension(ForFlow): """ Helper class for list comprehensions """ def __init__(self, module, stmt, middle, input, parent): diff --git a/test/completion/basic.py b/test/completion/basic.py index adce3617..7bba8123 100644 --- a/test/completion/basic.py +++ b/test/completion/basic.py @@ -136,7 +136,7 @@ a[0] arr = [1,''] a = [a for a in arr] -#? int() str() +#? int() a[0] a = [a if 1.0 else '' for a in [1] if [1.0]]