# HG changeset patch # User Paul Boddie # Date 1476903924 -7200 # Node ID 5952ffef127e4fe40e5a9c517a25d5d26f8bc528 # Parent aff9bdfadd3fc35f3602bab2a64bc603797369cc Track assignment and invocation within attribute chains more thoroughly. Record assignments affecting the whole attribute chain, not just immediate assignments. diff -r aff9bdfadd3f -r 5952ffef127e common.py --- a/common.py Wed Oct 19 16:37:01 2016 +0200 +++ b/common.py Wed Oct 19 21:05:24 2016 +0200 @@ -559,7 +559,7 @@ # Break attribute chains where non-access nodes are found. if not self.have_access_expression(n): - self.attrs = [] + self.reset_attribute_chain() # Descend into the expression, extending backwards any existing chain, # or building another for the expression. @@ -568,7 +568,8 @@ # Restore chain information applying to this node. - self.attrs = attrs + if not self.have_access_expression(n): + self.restore_attribute_chain(attrs) # Return immediately if the expression was another access and thus a # continuation backwards along the chain. The above processing will @@ -579,6 +580,18 @@ return name_ref + def reset_attribute_chain(self): + + "Reset the attribute chain for a subexpression of an attribute access." + + self.attrs = [] + + def restore_attribute_chain(self, attrs): + + "Restore the attribute chain for an attribute access." + + self.attrs = attrs + def have_access_expression(self, node): "Return whether the expression associated with 'node' is Getattr." diff -r aff9bdfadd3f -r 5952ffef127e inspector.py --- a/inspector.py Wed Oct 19 16:37:01 2016 +0200 +++ b/inspector.py Wed Oct 19 21:05:24 2016 +0200 @@ -21,7 +21,7 @@ """ from branching import BranchTracker -from common import get_argnames, init_item, predefined_constants +from common import CommonModule, get_argnames, init_item, predefined_constants from modules import BasicModule, CacheWritingModule, InspectionNaming from errors import InspectError from referencing import Reference @@ -45,6 +45,14 @@ self.in_class = False self.in_conditional = False self.in_invocation = False + + # Attribute chain state management. + + self.chain_assignment = [] + self.chain_invocation = [] + + # Accesses to global attributes. + self.global_attr_accesses = {} # Usage tracking. @@ -367,20 +375,10 @@ "Process the given attribute access node 'n'." - # Parts of the attribute chain are neither invoked nor assigned. - - in_invocation = self.in_invocation - self.in_invocation = False - in_assignment = self.in_assignment - self.in_assignment = False - # Obtain any completed chain and return the reference to it. name_ref = self.process_attribute_chain(n) - self.in_invocation = in_invocation - self.in_assignment = in_assignment - if self.have_access_expression(n): return name_ref @@ -455,15 +453,14 @@ # Record attribute usage in the tracker, and record the branch # information for the access. - branches = tracker.use_attribute(name, attrname, self.in_invocation, - self.in_assignment and immediate_access) + branches = tracker.use_attribute(name, attrname, self.in_invocation, assignment) if not branches: raise InspectError("Name %s is accessed using %s before an assignment." % ( name, attrname), path, n) self.record_branches_for_access(branches, name, attrnames) - access_number = self.record_access_details(name, attrnames, assignment) + access_number = self.record_access_details(name, attrnames, self.in_assignment) del self.attrs[0] return AccessRef(name, attrnames, access_number) @@ -992,6 +989,26 @@ tracker.resume_broken_branches() + # Attribute chain handling. + + def reset_attribute_chain(self): + + "Reset the attribute chain for a subexpression of an attribute access." + + CommonModule.reset_attribute_chain(self) + self.chain_assignment.append(self.in_assignment) + self.chain_invocation.append(self.in_invocation) + self.in_assignment = False + self.in_invocation = False + + def restore_attribute_chain(self, attrs): + + "Restore the attribute chain for an attribute access." + + CommonModule.restore_attribute_chain(self, attrs) + self.in_assignment = self.chain_assignment.pop() + self.in_invocation = self.chain_invocation.pop() + # Branch tracking methods. def start_tracking(self, names):