1.1 --- a/deducer.py Mon Jan 23 13:03:45 2017 +0100
1.2 +++ b/deducer.py Mon Jan 23 13:04:02 2017 +0100
1.3 @@ -79,6 +79,10 @@
1.4 self.attr_instance_types = {}
1.5 self.attr_module_types = {}
1.6
1.7 + # All known attribute names.
1.8 +
1.9 + self.all_attrnames = set()
1.10 +
1.11 # Modified attributes from usage observations.
1.12
1.13 self.modified_attributes = {}
1.14 @@ -128,10 +132,11 @@
1.15 # The processing workflow itself.
1.16
1.17 self.init_usage_index()
1.18 + self.init_attr_type_indexes()
1.19 + self.init_combined_attribute_index()
1.20 self.init_accessors()
1.21 self.init_accesses()
1.22 self.init_aliases()
1.23 - self.init_attr_type_indexes()
1.24 self.modify_mutated_attributes()
1.25 self.identify_references()
1.26 self.classify_accessors()
1.27 @@ -818,6 +823,10 @@
1.28 def init_accesses(self):
1.29
1.30 """
1.31 + Check that attributes used in accesses are actually defined on some
1.32 + object. This can be overlooked if unknown attributes are employed in
1.33 + attribute chains.
1.34 +
1.35 Initialise collections for accesses involving assignments.
1.36 """
1.37
1.38 @@ -834,24 +843,38 @@
1.39 # assignments.
1.40
1.41 for access_number, (assignment, invocation) in enumerate(modifiers):
1.42 - if not assignment and not invocation:
1.43 - continue
1.44
1.45 if name:
1.46 access_location = (path, name, attrname_str, access_number)
1.47 else:
1.48 access_location = (path, None, attrname_str, 0)
1.49
1.50 + # Plain name accesses do not employ attributes and are
1.51 + # ignored.
1.52 +
1.53 + if attrname_str:
1.54 + continue
1.55 +
1.56 + attrnames = get_attrnames(attrname_str)
1.57 +
1.58 + # Check the attribute names.
1.59 +
1.60 + for attrname in attrnames:
1.61 + if not attrname in self.all_attrnames:
1.62 + raise DeduceError("In %s, attribute %s is not defined in the program." % (path, attrname))
1.63 +
1.64 + # Now only process assignments and invocations.
1.65 +
1.66 if invocation:
1.67 self.reference_invocations.add(access_location)
1.68 continue
1.69 + elif not assignment:
1.70 + continue
1.71 +
1.72 + # Associate assignments with usage.
1.73
1.74 self.reference_assignments.add(access_location)
1.75
1.76 - # Associate assignments with usage.
1.77 -
1.78 - attrnames = get_attrnames(attrname_str)
1.79 -
1.80 # Assignment occurs for the only attribute.
1.81
1.82 if len(attrnames) == 1:
1.83 @@ -1119,6 +1142,15 @@
1.84
1.85 return types
1.86
1.87 + def init_combined_attribute_index(self):
1.88 +
1.89 + "Initialise a combined index for the detection of invalid attributes."
1.90 +
1.91 + self.all_attrnames = set()
1.92 + for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs):
1.93 + for name, attrnames in attrs.items():
1.94 + self.all_attrnames.update(attrnames)
1.95 +
1.96 # Reference identification.
1.97
1.98 def identify_references(self):