From c44168f7adbc4da666ef49d7b0e05dae2876a25f Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 5 Aug 2014 11:17:18 +0200 Subject: [PATCH] add a Flow.previous attribute to be able to access the if flow from an else clause. --- jedi/cache.py | 2 +- jedi/evaluate/flow_analysis.py | 7 +++++++ jedi/evaluate/helpers.py | 5 ++++- jedi/evaluate/representation.py | 9 ++++++++- jedi/parser/__init__.py | 11 ++++++----- jedi/parser/representation.py | 4 +++- 6 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 jedi/evaluate/flow_analysis.py diff --git a/jedi/cache.py b/jedi/cache.py index ad73c082..e0533150 100644 --- a/jedi/cache.py +++ b/jedi/cache.py @@ -243,7 +243,7 @@ def save_parser(path, name, parser, pickling=True): class ParserPickling(object): - version = 13 + version = 14 """ Version number (integer) for file system cache. diff --git a/jedi/evaluate/flow_analysis.py b/jedi/evaluate/flow_analysis.py new file mode 100644 index 00000000..a118adc5 --- /dev/null +++ b/jedi/evaluate/flow_analysis.py @@ -0,0 +1,7 @@ + +NOT_REACHABLE = object() +REACHABLE = object() +UNSURE = object() + +def break_check(scope): + return UNSURE diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 409fb0ea..6958cacd 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -47,7 +47,10 @@ def fast_parent_copy(obj): elif isinstance(value, list): setattr(new_obj, key, list_rec(value)) elif isinstance(value, pr.Simple): - setattr(new_obj, key, recursion(value)) + try: # because of the circular Flow.previous/Flow.next + setattr(new_obj, key, new_elements[value]) + except KeyError: + setattr(new_obj, key, recursion(value)) return new_obj def list_rec(list_obj): diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 17f114ad..c7ce18fe 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -26,6 +26,7 @@ from jedi.evaluate import iterable from jedi.evaluate import docstrings from jedi.evaluate import helpers from jedi.evaluate import param +from jedi.evaluate import flow_analysis class Executed(pr.IsScope): @@ -471,8 +472,14 @@ class FunctionExecution(Executed): types = list(docstrings.find_return_types(self._evaluator, func)) for r in self.returns: - if r is not None: + if r is None: + continue + + check = flow_analysis.break_check(r.parent.parent) + if check is not flow_analysis.NOT_REACHABLE: types += self._evaluator.eval_statement(r) + if check is flow_analysis.REACHABLE: + break return types @memoize_default(default=()) diff --git a/jedi/parser/__init__.py b/jedi/parser/__init__.py index 8c7ca567..7d7aecc0 100644 --- a/jedi/parser/__init__.py +++ b/jedi/parser/__init__.py @@ -538,9 +538,9 @@ class Parser(object): f = pr.Flow(self.module, command, inputs, first_pos) if command in extended_flow: - # the last statement has to be another part of - # the flow statement, because a dedent releases the - # main scope, so just take the last statement. + # The last statement has to be another part of the flow + # statement, because a dedent releases the main scope, so + # just take the last statement. try: s = self._scope.statements[-1].set_next(f) except (AttributeError, IndexError): @@ -565,8 +565,9 @@ class Parser(object): if stmt is not None: stmt.parent = use_as_parent_scope try: - func.statements.append(pr.KeywordStatement(tok_str, s, - use_as_parent_scope, stmt)) + kw_stmt = pr.KeywordStatement(tok_str, s, + use_as_parent_scope, stmt) + self._scope.statements.append(kw_stmt) func.returns.append(stmt) # start_pos is the one of the return statement stmt.start_pos = s diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 9492ec95..05eba9ef 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -637,10 +637,11 @@ class Flow(Scope): :param start_pos: Position (line, column) of the Flow statement. :type start_pos: tuple(int, int) """ - __slots__ = ('next', 'command', '_parent', 'inputs', 'set_vars') + __slots__ = ('next', 'previous', 'command', '_parent', 'inputs', 'set_vars') def __init__(self, module, command, inputs, start_pos): self.next = None + self.previous = None self.command = command super(Flow, self).__init__(module, start_pos) self._parent = None @@ -706,6 +707,7 @@ class Flow(Scope): else: self.next = next self.next.parent = self.parent + self.next.previous = self return next def scope_names_generator(self, position=None):