diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 1d95e3f7..5cf45434 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -355,7 +355,7 @@ def _remove_statements(evaluator, stmt, name): check_instance = stmt.instance stmt = stmt.var - pep0484types = pep0484.find_type_from_comment_hint(evaluator, stmt) + pep0484types = pep0484.find_type_from_comment_hint(evaluator, stmt, name) if pep0484types: return pep0484types types |= evaluator.eval_statement(stmt, seek_name=name) diff --git a/jedi/evaluate/pep0484.py b/jedi/evaluate/pep0484.py index 260e15e4..69162178 100644 --- a/jedi/evaluate/pep0484.py +++ b/jedi/evaluate/pep0484.py @@ -31,10 +31,20 @@ from jedi import _compatibility import re -def _evaluate_for_annotation(evaluator, annotation): +def _evaluate_for_annotation(evaluator, annotation, index=None): + """ + Evaluates a string-node, looking for an annotation + If index is not None, the annotation is expected to be a tuple + and we're interested in that index + """ if annotation is not None: definitions = evaluator.eval_element( _fix_forward_reference(evaluator, annotation)) + if index is not None: + definitions = list(itertools.chain.from_iterable( + definition.py__getitem__(index) for definition in definitions + if definition.type == 'tuple' and + len(list(definition.py__iter__())) >= index)) return list(itertools.chain.from_iterable( evaluator.execute(d) for d in definitions)) else: @@ -139,7 +149,21 @@ def get_types_for_typing_module(evaluator, typ, node): return result -def find_type_from_comment_hint(evaluator, stmt): +def find_type_from_comment_hint(evaluator, stmt, name): + index = None + if stmt.children[0].type == "testlist_star_expr": + # something like "a, b = 1, 2" + leftside = stmt.children[0] + index = 0 + for child in leftside.children: + if child == name: + break + if child.type == "operator": + continue + index += 1 + else: + return [] + comment = stmt.get_following_comment_same_line() if comment is None: return [] @@ -151,4 +175,4 @@ def find_type_from_comment_hint(evaluator, stmt): repr(str(match.group(1).strip())), stmt.start_pos) annotation.parent = stmt.parent - return _evaluate_for_annotation(evaluator, annotation) + return _evaluate_for_annotation(evaluator, annotation, index) diff --git a/jedi/parser/tree.py b/jedi/parser/tree.py index 1906d364..0625a98a 100644 --- a/jedi/parser/tree.py +++ b/jedi/parser/tree.py @@ -514,7 +514,7 @@ class BaseNode(Base): def last_leaf(self): try: - return self.children[-1].first_leaf() + return self.children[-1].last_leaf() except AttributeError: return self.children[-1] @@ -527,6 +527,10 @@ class BaseNode(Base): whitespace = self.last_leaf().get_next().prefix except AttributeError: return None + except ValueError: + # in some particular cases, the tree doesn't seem to be linked + # correctly + return None if "#" not in whitespace: return None comment = whitespace[whitespace.index("#"):] diff --git a/test/completion/pep0484_comments.py b/test/completion/pep0484_comments.py index 54ad38c6..e49717e6 100644 --- a/test/completion/pep0484_comments.py +++ b/test/completion/pep0484_comments.py @@ -37,3 +37,32 @@ def test(a, b): d #? str() e + +a,b = 1, 2 # type: str, float +#? str() +a +#? float() +b + +class Employee: + pass + +from typing import List +x = [] # type: List[Employee] +#? Employee() +x[1] +x, y, z = [], [], [] # type: List[int], List[int], List[str] +#? int() +y[2] +x, y, z = [], [], [] # type: (List[float], List[float], List[BB]) +for zi in z: + #? BB() + zi + +x = [ + 1, + 2, +] # type: List[str] + +#? str() +x[1]