1.1 --- a/deducer.py Mon May 01 23:35:01 2017 +0200
1.2 +++ b/deducer.py Fri Jun 16 00:37:50 2017 +0200
1.3 @@ -562,21 +562,36 @@
1.4
1.5 if not constrained and self.access_index_rev.get(location):
1.6
1.7 - # Record specific type guard details.
1.8 -
1.9 - if len(all_types) == 1:
1.10 - self.accessor_guard_tests[location] = ("specific", test_label_for_type(first(all_types)))
1.11 - elif is_single_class_type(all_types):
1.12 - self.accessor_guard_tests[location] = ("specific", "object")
1.13 -
1.14 - # Record common type guard details.
1.15 -
1.16 - elif len(all_general_types) == 1:
1.17 - self.accessor_guard_tests[location] = ("common", test_label_for_type(first(all_types)))
1.18 - elif is_single_class_type(all_general_types):
1.19 - self.accessor_guard_tests[location] = ("common", "object")
1.20 -
1.21 - # Otherwise, no convenient guard can be defined.
1.22 + # Test for self parameters in methods that could not be
1.23 + # constrained, potentially due to usage unsupported by the class
1.24 + # defining the method (such as in mix-in classes).
1.25 +
1.26 + if location.name != "self" or self.in_method(location.path):
1.27 + self.record_guard(location, all_types, all_general_types)
1.28 +
1.29 + def record_guard(self, location, all_types, all_general_types):
1.30 +
1.31 + """
1.32 + For the accessor 'location', record a guard if 'all_types' or
1.33 + 'all_general_types' are appropriate. Where no guard is recorded, access
1.34 + tests will need to be applied.
1.35 + """
1.36 +
1.37 + # Record specific type guard details.
1.38 +
1.39 + if len(all_types) == 1:
1.40 + self.accessor_guard_tests[location] = ("specific", test_label_for_type(first(all_types)))
1.41 + elif is_single_class_type(all_types):
1.42 + self.accessor_guard_tests[location] = ("specific", "object")
1.43 +
1.44 + # Record common type guard details.
1.45 +
1.46 + elif len(all_general_types) == 1:
1.47 + self.accessor_guard_tests[location] = ("common", test_label_for_type(first(all_types)))
1.48 + elif is_single_class_type(all_general_types):
1.49 + self.accessor_guard_tests[location] = ("common", "object")
1.50 +
1.51 + # Otherwise, no convenient guard can be defined.
1.52
1.53 def classify_accesses(self):
1.54
1.55 @@ -1462,24 +1477,29 @@
1.56
1.57 # Constrain "self" references.
1.58
1.59 - if location.name == "self":
1.60 -
1.61 - # Test for the class of the method in the deduced types.
1.62 -
1.63 - class_name = self.in_method(location.path)
1.64 -
1.65 - if class_name and class_name not in class_types and class_name not in only_instance_types:
1.66 + class_name = self.in_method(location.path)
1.67 + constrained = False
1.68 +
1.69 + if class_name and location.name == "self":
1.70 +
1.71 + # Constrain the types to the class's hierarchy.
1.72 +
1.73 + class_types, only_instance_types, module_types, constrained = \
1.74 + self.constrain_to_class(class_name, class_types, only_instance_types)
1.75 +
1.76 + # Without any deduced types, produce an error.
1.77 +
1.78 + if not class_types and not only_instance_types:
1.79 raise DeduceError("In %s, usage {%s} is not directly supported by class %s or its instances." %
1.80 (location.path, encode_usage(usage), class_name))
1.81
1.82 - # Constrain the types to the class's hierarchy.
1.83 -
1.84 - t = self.constrain_self_reference(location.path, class_types, only_instance_types)
1.85 - if t:
1.86 - class_types, only_instance_types, module_types, constrained = t
1.87 - return class_types, only_instance_types, module_types, constrained, have_assignments
1.88 -
1.89 - return class_types, only_instance_types, module_types, False, have_assignments
1.90 + # If the class defining the method does not appear amongst the
1.91 + # types supporting the usage, remove the constrained status of the
1.92 + # name.
1.93 +
1.94 + constrained = constrained and (class_name in class_types or class_name in only_instance_types)
1.95 +
1.96 + return class_types, only_instance_types, module_types, constrained, have_assignments
1.97
1.98 def constrain_self_reference(self, unit_path, class_types, only_instance_types):
1.99
1.100 @@ -1496,6 +1516,18 @@
1.101 if not class_name:
1.102 return None
1.103
1.104 + return self.constrain_to_class(class_name, class_types, only_instance_types)
1.105 +
1.106 + def constrain_to_class(self, class_name, class_types, only_instance_types):
1.107 +
1.108 + """
1.109 + Constrain to 'class_name' and its descendants, the given 'class_types'
1.110 + and 'only_instance_types'.
1.111 +
1.112 + Return the class, instance, module types plus whether the types are
1.113 + constrained.
1.114 + """
1.115 +
1.116 classes = set([class_name])
1.117 classes.update(self.get_descendants_for_class(class_name))
1.118
1.119 @@ -1512,7 +1544,11 @@
1.120
1.121 "Return whether 'path' refers to a method."
1.122
1.123 - class_name, method_name = path.rsplit(".", 1)
1.124 + t = path.rsplit(".", 1)
1.125 + if len(t) == 1:
1.126 + return False
1.127 +
1.128 + class_name, method_name = t
1.129 return class_name != "__builtins__.core.type" and self.importer.classes.has_key(class_name) and class_name
1.130
1.131 def init_reference_details(self, location):