# HG changeset patch # User Paul Boddie # Date 1485173042 -3600 # Node ID d1515ba2585958a8ec7143a9704869e1e7858ff1 # Parent a8f358aafccff15ec75ee37903eff1cb96fad116 Test for unknown attributes hiding in attribute chains. diff -r a8f358aafccf -r d1515ba25859 deducer.py --- a/deducer.py Mon Jan 23 13:03:45 2017 +0100 +++ b/deducer.py Mon Jan 23 13:04:02 2017 +0100 @@ -79,6 +79,10 @@ self.attr_instance_types = {} self.attr_module_types = {} + # All known attribute names. + + self.all_attrnames = set() + # Modified attributes from usage observations. self.modified_attributes = {} @@ -128,10 +132,11 @@ # The processing workflow itself. self.init_usage_index() + self.init_attr_type_indexes() + self.init_combined_attribute_index() self.init_accessors() self.init_accesses() self.init_aliases() - self.init_attr_type_indexes() self.modify_mutated_attributes() self.identify_references() self.classify_accessors() @@ -818,6 +823,10 @@ def init_accesses(self): """ + Check that attributes used in accesses are actually defined on some + object. This can be overlooked if unknown attributes are employed in + attribute chains. + Initialise collections for accesses involving assignments. """ @@ -834,24 +843,38 @@ # assignments. for access_number, (assignment, invocation) in enumerate(modifiers): - if not assignment and not invocation: - continue if name: access_location = (path, name, attrname_str, access_number) else: access_location = (path, None, attrname_str, 0) + # Plain name accesses do not employ attributes and are + # ignored. + + if attrname_str: + continue + + attrnames = get_attrnames(attrname_str) + + # Check the attribute names. + + for attrname in attrnames: + if not attrname in self.all_attrnames: + raise DeduceError("In %s, attribute %s is not defined in the program." % (path, attrname)) + + # Now only process assignments and invocations. + if invocation: self.reference_invocations.add(access_location) continue + elif not assignment: + continue + + # Associate assignments with usage. self.reference_assignments.add(access_location) - # Associate assignments with usage. - - attrnames = get_attrnames(attrname_str) - # Assignment occurs for the only attribute. if len(attrnames) == 1: @@ -1119,6 +1142,15 @@ return types + def init_combined_attribute_index(self): + + "Initialise a combined index for the detection of invalid attributes." + + self.all_attrnames = set() + for attrs in (self.importer.all_combined_attrs, self.importer.all_module_attrs): + for name, attrnames in attrs.items(): + self.all_attrnames.update(attrnames) + # Reference identification. def identify_references(self): diff -r a8f358aafccf -r d1515ba25859 tests/attr_unknown_bad.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/attr_unknown_bad.py Mon Jan 23 13:04:02 2017 +0100 @@ -0,0 +1,2 @@ +def access(x): + print x.__name__.unknown