1
0
forked from VimPlug/jedi
Files
jedi-fork/jedi/evaluate/flow_analysis.py
2016-10-29 02:11:04 +02:00

91 lines
3.0 KiB
Python

from jedi.parser import tree
class Status(object):
lookup_table = {}
def __init__(self, value, name):
self._value = value
self._name = name
Status.lookup_table[value] = self
def invert(self):
if self is REACHABLE:
return UNREACHABLE
elif self is UNREACHABLE:
return REACHABLE
else:
return UNSURE
def __and__(self, other):
if UNSURE in (self, other):
return UNSURE
else:
return REACHABLE if self._value and other._value else UNREACHABLE
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self._name)
REACHABLE = Status(True, 'reachable')
UNREACHABLE = Status(False, 'unreachable')
UNSURE = Status(None, 'unsure')
def reachability_check(context, context_scope, node, origin_scope=None):
flow_scope = node.get_parent_scope(include_flows=True)
# Direct parents get resolved, we filter scopes that are separate branches.
# This makes sense for autocompletion and static analysis. For actual
# Python it doesn't matter, because we're talking about potentially
# unreachable code.
# e.g. `if 0:` would cause all name lookup within the flow make
# unaccessible. This is not a "problem" in Python, because the code is
# never called. In Jedi though, we still want to infer types.
#while origin_scope is not None:
#if flow_scope == origin_scope:
#return REACHABLE
#origin_scope = origin_scope.parent
return _break_check(context, context_scope, flow_scope, node)
def _break_check(context, context_scope, flow_scope, node):
reachable = REACHABLE
if flow_scope.type == 'if_stmt':
if flow_scope.node_after_else(node):
for check_node in flow_scope.check_nodes():
reachable = _check_if(context, check_node)
if reachable in (REACHABLE, UNSURE):
break
reachable = reachable.invert()
else:
node = flow_scope.node_in_which_check_node(node)
if node is not None:
reachable = _check_if(context, node)
elif flow_scope.type in ('try_stmt', 'while_stmt'):
return UNSURE
# Only reachable branches need to be examined further.
if reachable in (UNREACHABLE, UNSURE):
return reachable
# TODO REMOVE WTF
#if element_scope.type == 'file_input':
# The definition is in another module and therefore just return what we
# have generated.
# return reachable
if context_scope != flow_scope and context_scope != flow_scope.parent:
flow_scope = flow_scope.get_parent_scope(include_flows=True)
return reachable & _break_check(context, context_scope, flow_scope, node)
else:
return reachable
def _check_if(context, node):
types = context.eval_node(node)
values = set(x.py__bool__() for x in types)
if len(values) == 1:
return Status.lookup_table[values.pop()]
else:
return UNSURE