1.1 --- a/inspector.py Fri Mar 31 18:42:27 2017 +0200
1.2 +++ b/inspector.py Fri Mar 31 23:26:47 2017 +0200
1.3 @@ -27,7 +27,8 @@
1.4 from referencing import Reference
1.5 from resolving import NameResolving
1.6 from results import AccessRef, InstanceRef, InvocationRef, LiteralSequenceRef, \
1.7 - LocalNameRef, NameRef, ResolvedNameRef, VariableRef
1.8 + LocalNameRef, MultipleRef, NameRef, ResolvedNameRef, \
1.9 + Result, VariableRef
1.10 import compiler
1.11 import sys
1.12
1.13 @@ -800,7 +801,7 @@
1.14
1.15 "Process the given operator node 'n'."
1.16
1.17 - self.process_operator_chain(n.nodes, self.process_structure_node)
1.18 + return self.process_operator_chain(n.nodes)
1.19
1.20 def process_name_node(self, n):
1.21
1.22 @@ -915,20 +916,24 @@
1.23 self.in_invocation)
1.24 return None
1.25
1.26 - def process_operator_chain(self, nodes, fn):
1.27 + def process_operator_chain(self, nodes):
1.28
1.29 """
1.30 - Process the given chain of 'nodes', applying 'fn' to each node or item.
1.31 + Process the given chain of 'nodes', processing each node or item.
1.32 Each node starts a new conditional region, effectively making a deeply-
1.33 nested collection of if-like statements.
1.34 """
1.35
1.36 + results = []
1.37 +
1.38 tracker = self.trackers[-1]
1.39
1.40 for item in nodes:
1.41 tracker.new_branchpoint()
1.42 tracker.new_branch()
1.43 - fn(item)
1.44 + result = self.process_structure_node(item)
1.45 + if result:
1.46 + results.append(result)
1.47
1.48 for item in nodes[:-1]:
1.49 tracker.shelve_branch()
1.50 @@ -939,6 +944,8 @@
1.51 tracker.shelve_branch()
1.52 tracker.merge_branches()
1.53
1.54 + return MultipleRef(results)
1.55 +
1.56 def process_try_node(self, n):
1.57
1.58 """
1.59 @@ -1322,22 +1329,8 @@
1.60
1.61 "Return a suitable initialiser reference for 'value'."
1.62
1.63 - # Includes LiteralSequenceRef, ResolvedNameRef...
1.64 -
1.65 - if isinstance(value, (NameRef, AccessRef, InstanceRef)):
1.66 + if isinstance(value, Result):
1.67 return value.reference()
1.68 -
1.69 - # In general, invocations do not produce known results. However, the
1.70 - # name initialisers are resolved once a module has been inspected.
1.71 -
1.72 - elif isinstance(value, InvocationRef):
1.73 - return value.reference()
1.74 -
1.75 - # Variable references are unknown results.
1.76 -
1.77 - elif isinstance(value, VariableRef):
1.78 - return value.reference()
1.79 -
1.80 else:
1.81 return value
1.82
2.1 --- a/resolving.py Fri Mar 31 18:42:27 2017 +0200
2.2 +++ b/resolving.py Fri Mar 31 23:26:47 2017 +0200
2.3 @@ -21,7 +21,7 @@
2.4
2.5 from common import init_item
2.6 from results import AccessRef, InstanceRef, InvocationRef, LocalNameRef, \
2.7 - NameRef, ResolvedNameRef
2.8 + MultipleRef, NameRef, ResolvedNameRef
2.9 from referencing import Reference
2.10 import sys
2.11
2.12 @@ -283,12 +283,40 @@
2.13 initialised reference, along with any aliased name information.
2.14 """
2.15
2.16 - const_accesses = self.const_accesses.get(path)
2.17 -
2.18 initialised_ref = None
2.19 aliased_names = None
2.20 no_reference = None, None
2.21
2.22 + # Attempt to obtain a coherent reference from multiple outcomes.
2.23 +
2.24 + if isinstance(name_ref, MultipleRef):
2.25 + refs = set()
2.26 + aliases = []
2.27 +
2.28 + for result in name_ref.results:
2.29 + _initialised_ref, _aliased_names = self.resolve_reference(path, result)
2.30 +
2.31 + # Unsuitable references at any level cause the result to yield
2.32 + # no reference.
2.33 +
2.34 + if not _initialised_ref:
2.35 + refs = None
2.36 + elif refs is not None:
2.37 + refs.add(_initialised_ref)
2.38 +
2.39 + if not _aliased_names:
2.40 + aliases = None
2.41 + elif aliases is not None:
2.42 + aliases += _aliased_names
2.43 +
2.44 + # Only unambiguous references are returned as initialising
2.45 + # references.
2.46 +
2.47 + if refs and len(refs) == 1:
2.48 + return list(refs)[0], aliases
2.49 + else:
2.50 + return None, aliases
2.51 +
2.52 # Unwrap invocations.
2.53
2.54 if isinstance(name_ref, InvocationRef):
2.55 @@ -297,6 +325,8 @@
2.56 else:
2.57 invocation = False
2.58
2.59 + const_accesses = self.const_accesses.get(path)
2.60 +
2.61 # Obtain a usable reference from names or constants.
2.62
2.63 if isinstance(name_ref, ResolvedNameRef):
3.1 --- a/results.py Fri Mar 31 18:42:27 2017 +0200
3.2 +++ b/results.py Fri Mar 31 23:26:47 2017 +0200
3.3 @@ -209,6 +209,26 @@
3.4 def __repr__(self):
3.5 return "LiteralSequenceRef(%r, %r, %r, %r)" % (self.name, self.ref, self.node, self.items)
3.6
3.7 +class MultipleRef(Result):
3.8 +
3.9 + "A multiple outcome result."
3.10 +
3.11 + def __init__(self, results):
3.12 + self.results = results
3.13 +
3.14 + def reference(self):
3.15 + refs = set(self.references())
3.16 + ref = len(refs) == 1 and list(refs)[0] or Reference("<var>")
3.17 +
3.18 + def references(self):
3.19 + refs = []
3.20 + for result in self.results:
3.21 + refs.append(result.reference())
3.22 + return refs
3.23 +
3.24 + def __repr__(self):
3.25 + return "MultipleRef(%r)" % self.results
3.26 +
3.27 class VariableRef(Result):
3.28
3.29 "A variable reference."