1.1 --- a/deducer.py Sun Mar 12 17:46:28 2017 +0100
1.2 +++ b/deducer.py Sun Mar 12 17:56:55 2017 +0100
1.3 @@ -1609,7 +1609,9 @@
1.4 have_access = self.provider_class_types.has_key(accessor_location)
1.5
1.6 # With an access, attempt to narrow the existing selection of provider
1.7 - # types.
1.8 + # types. Invocations attempt to find return value information, with
1.9 + # instance return values also yielding class providers (since attributes
1.10 + # on instances could be provided by classes).
1.11
1.12 if have_access:
1.13 provider_class_types = self.provider_class_types[accessor_location]
1.14 @@ -1624,19 +1626,19 @@
1.15
1.16 for access_location in self.alias_index[accessor_location]:
1.17 location, name, attrnames, access_number = access_location
1.18 + invocation = self.reference_invocations.get(access_location)
1.19
1.20 # Alias references an attribute access.
1.21
1.22 if attrnames:
1.23
1.24 - # Obtain attribute references for the access.
1.25 + # Obtain references and attribute types for the access.
1.26
1.27 attrs = self.get_references_for_access(access_location)
1.28 -
1.29 - # Separate the different attribute types.
1.30 -
1.31 - (class_types, instance_types, module_types,
1.32 - function_types, var_types) = separate_types(attrs)
1.33 + attrs = self.convert_invocation_providers(attrs, invocation)
1.34 +
1.35 + (class_types, instance_types, module_types, function_types,
1.36 + var_types) = separate_types(attrs)
1.37
1.38 # Where non-accessor types are found, do not attempt to refine
1.39 # the defined accessor types.
1.40 @@ -1644,6 +1646,9 @@
1.41 if function_types or var_types:
1.42 return
1.43
1.44 + # Invocations converting class accessors to instances do not
1.45 + # change the nature of class providers.
1.46 +
1.47 class_types = set(provider_class_types).intersection(class_types)
1.48 instance_types = set(provider_instance_types).intersection(instance_types)
1.49 module_types = set(provider_module_types).intersection(module_types)
1.50 @@ -1655,8 +1660,10 @@
1.51
1.52 attr = self.get_initialised_name(access_location)
1.53 if attr:
1.54 - (class_types, instance_types, module_types,
1.55 - _function_types, _var_types) = separate_types([attr])
1.56 + attrs = self.convert_invocation_providers([attr], invocation)
1.57 +
1.58 + (class_types, instance_types, module_types, function_types,
1.59 + var_types) = separate_types(attrs)
1.60
1.61 # Where no further information is found, do not attempt to
1.62 # refine the defined accessor types.
1.63 @@ -1674,6 +1681,8 @@
1.64 self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False)
1.65
1.66 # Without an access, attempt to identify references for the alias.
1.67 + # Invocations convert classes to instances and also attempt to find
1.68 + # return value information.
1.69
1.70 else:
1.71 refs = set()
1.72 @@ -1686,6 +1695,8 @@
1.73 access_location = self.const_accesses[access_location]
1.74
1.75 location, name, attrnames, access_number = access_location
1.76 + invocation = self.reference_invocations.get(access_location)
1.77 +
1.78 attrnames = attrnames and attrnames.split(".")
1.79 remaining = attrnames and len(attrnames) > 1
1.80
1.81 @@ -1701,21 +1712,31 @@
1.82 attrname = attrnames and attrnames[0]
1.83
1.84 if attrname:
1.85 +
1.86 + # Obtain references and attribute types for the access.
1.87 +
1.88 attrs = self.get_references_for_access(access_location)
1.89 + attrs = self.convert_invocations(attrs, invocation)
1.90 refs.update(attrs)
1.91
1.92 # Alias references a name, not an access.
1.93
1.94 else:
1.95 +
1.96 + # Obtain initialiser information.
1.97 +
1.98 attr = self.get_initialised_name(access_location)
1.99 - attrs = attr and [attr] or []
1.100 - if not attrs and self.provider_class_types.has_key(access_location):
1.101 + if attr:
1.102 + refs.update(self.convert_invocations([attr], invocation))
1.103 +
1.104 + # Obtain provider information.
1.105 +
1.106 + elif self.provider_class_types.has_key(access_location):
1.107 class_types = self.provider_class_types[access_location]
1.108 instance_types = self.provider_instance_types[access_location]
1.109 module_types = self.provider_module_types[access_location]
1.110 - attrs = combine_types(class_types, instance_types, module_types)
1.111 - if attrs:
1.112 - refs.update(attrs)
1.113 +
1.114 + refs.update(combine_types(class_types, instance_types, module_types))
1.115
1.116 # Record reference details for the alias separately from accessors.
1.117
1.118 @@ -1730,6 +1751,53 @@
1.119 attrs.append(attr)
1.120 return attrs
1.121
1.122 + def convert_invocation_providers(self, refs, invocation):
1.123 +
1.124 + """
1.125 + Convert 'refs' to providers corresponding to the results of invoking
1.126 + each of the given references, if 'invocation' is set to a true value.
1.127 + """
1.128 +
1.129 + if not invocation:
1.130 + return refs
1.131 +
1.132 + providers = set()
1.133 +
1.134 + for ref in refs:
1.135 + ref = self.convert_invocation_provider(ref)
1.136 + if ref.has_kind("<instance>"):
1.137 + providers.add(Reference("<class>", ref.get_origin()))
1.138 + providers.add(ref)
1.139 +
1.140 + return providers
1.141 +
1.142 + def convert_invocation_provider(self, ref):
1.143 +
1.144 + "Convert 'ref' to a provider appropriate to its invocation result."
1.145 +
1.146 + if ref and ref.has_kind("<class>"):
1.147 + return ref
1.148 +
1.149 + return Reference("<var>")
1.150 +
1.151 + def convert_invocations(self, refs, invocation):
1.152 +
1.153 + """
1.154 + Convert 'refs' to invocation results if 'invocation' is set to a true
1.155 + value.
1.156 + """
1.157 +
1.158 + return invocation and map(self.convert_invocation, refs) or refs
1.159 +
1.160 + def convert_invocation(self, ref):
1.161 +
1.162 + "Convert 'ref' to its invocation result."
1.163 +
1.164 + if ref and ref.has_kind("<class>"):
1.165 + return ref.instance_of()
1.166 +
1.167 + return Reference("<var>")
1.168 +
1.169 def get_initialised_name(self, access_location):
1.170
1.171 """