1.1 --- a/deducer.py Sun Mar 12 00:53:57 2017 +0100
1.2 +++ b/deducer.py Sun Mar 12 17:58:14 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,21 +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 -
1.26 - attrs = []
1.27 - for _attrtype, object_type, attr in self.referenced_attrs[access_location]:
1.28 - attrs.append(attr)
1.29 -
1.30 - # Separate the different attribute types.
1.31 -
1.32 - (class_types, instance_types, module_types,
1.33 - function_types, var_types) = separate_types(attrs)
1.34 + # Obtain references and attribute types for the access.
1.35 +
1.36 + attrs = self.get_references_for_access(access_location)
1.37 + attrs = self.convert_invocation_providers(attrs, invocation)
1.38 +
1.39 + (class_types, instance_types, module_types, function_types,
1.40 + var_types) = separate_types(attrs)
1.41
1.42 # Where non-accessor types are found, do not attempt to refine
1.43 # the defined accessor types.
1.44 @@ -1646,6 +1646,9 @@
1.45 if function_types or var_types:
1.46 return
1.47
1.48 + # Invocations converting class accessors to instances do not
1.49 + # change the nature of class providers.
1.50 +
1.51 class_types = set(provider_class_types).intersection(class_types)
1.52 instance_types = set(provider_instance_types).intersection(instance_types)
1.53 module_types = set(provider_module_types).intersection(module_types)
1.54 @@ -1657,8 +1660,10 @@
1.55
1.56 attr = self.get_initialised_name(access_location)
1.57 if attr:
1.58 - (class_types, instance_types, module_types,
1.59 - _function_types, _var_types) = separate_types([attr])
1.60 + attrs = self.convert_invocation_providers([attr], invocation)
1.61 +
1.62 + (class_types, instance_types, module_types, function_types,
1.63 + var_types) = separate_types(attrs)
1.64
1.65 # Where no further information is found, do not attempt to
1.66 # refine the defined accessor types.
1.67 @@ -1676,6 +1681,8 @@
1.68 self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False)
1.69
1.70 # Without an access, attempt to identify references for the alias.
1.71 + # Invocations convert classes to instances and also attempt to find
1.72 + # return value information.
1.73
1.74 else:
1.75 refs = set()
1.76 @@ -1688,6 +1695,8 @@
1.77 access_location = self.const_accesses[access_location]
1.78
1.79 location, name, attrnames, access_number = access_location
1.80 + invocation = self.reference_invocations.get(access_location)
1.81 +
1.82 attrnames = attrnames and attrnames.split(".")
1.83 remaining = attrnames and len(attrnames) > 1
1.84
1.85 @@ -1703,28 +1712,92 @@
1.86 attrname = attrnames and attrnames[0]
1.87
1.88 if attrname:
1.89 - attrs = []
1.90 - for attrtype, object_type, attr in self.referenced_attrs[access_location]:
1.91 - attrs.append(attr)
1.92 +
1.93 + # Obtain references and attribute types for the access.
1.94 +
1.95 + attrs = self.get_references_for_access(access_location)
1.96 + attrs = self.convert_invocations(attrs, invocation)
1.97 refs.update(attrs)
1.98
1.99 # Alias references a name, not an access.
1.100
1.101 else:
1.102 +
1.103 + # Obtain initialiser information.
1.104 +
1.105 attr = self.get_initialised_name(access_location)
1.106 - attrs = attr and [attr] or []
1.107 - if not attrs and self.provider_class_types.has_key(access_location):
1.108 + if attr:
1.109 + refs.update(self.convert_invocations([attr], invocation))
1.110 +
1.111 + # Obtain provider information.
1.112 +
1.113 + elif self.provider_class_types.has_key(access_location):
1.114 class_types = self.provider_class_types[access_location]
1.115 instance_types = self.provider_instance_types[access_location]
1.116 module_types = self.provider_module_types[access_location]
1.117 - attrs = combine_types(class_types, instance_types, module_types)
1.118 - if attrs:
1.119 - refs.update(attrs)
1.120 +
1.121 + refs.update(combine_types(class_types, instance_types, module_types))
1.122
1.123 # Record reference details for the alias separately from accessors.
1.124
1.125 self.referenced_objects[accessor_location] = refs
1.126
1.127 + def get_references_for_access(self, access_location):
1.128 +
1.129 + "Return the references identified for 'access_location'."
1.130 +
1.131 + attrs = []
1.132 + for attrtype, object_type, attr in self.referenced_attrs[access_location]:
1.133 + attrs.append(attr)
1.134 + return attrs
1.135 +
1.136 + def convert_invocation_providers(self, refs, invocation):
1.137 +
1.138 + """
1.139 + Convert 'refs' to providers corresponding to the results of invoking
1.140 + each of the given references, if 'invocation' is set to a true value.
1.141 + """
1.142 +
1.143 + if not invocation:
1.144 + return refs
1.145 +
1.146 + providers = set()
1.147 +
1.148 + for ref in refs:
1.149 + ref = self.convert_invocation_provider(ref)
1.150 + if ref.has_kind("<instance>"):
1.151 + providers.add(Reference("<class>", ref.get_origin()))
1.152 + providers.add(ref)
1.153 +
1.154 + return providers
1.155 +
1.156 + def convert_invocation_provider(self, ref):
1.157 +
1.158 + "Convert 'ref' to a provider appropriate to its invocation result."
1.159 +
1.160 + if ref and ref.has_kind("<class>"):
1.161 + return ref
1.162 +
1.163 + return Reference("<var>")
1.164 +
1.165 + def convert_invocations(self, refs, invocation):
1.166 +
1.167 + """
1.168 + Convert 'refs' to invocation results if 'invocation' is set to a true
1.169 + value.
1.170 + """
1.171 +
1.172 + return invocation and map(self.convert_invocation, refs) or refs
1.173 +
1.174 + def convert_invocation(self, ref):
1.175 +
1.176 + "Convert 'ref' to its invocation result."
1.177 +
1.178 + if ref and ref.has_kind("<class>"):
1.179 + return ref.instance_of()
1.180 +
1.181 + return Reference("<var>")
1.182 +
1.183 def get_initialised_name(self, access_location):
1.184
1.185 """
2.1 --- a/inspector.py Sun Mar 12 00:53:57 2017 +0100
2.2 +++ b/inspector.py Sun Mar 12 17:58:14 2017 +0100
2.3 @@ -773,7 +773,7 @@
2.4 if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):
2.5 return InstanceRef(name_ref.reference().instance_of())
2.6
2.7 - elif isinstance(name_ref, NameRef):
2.8 + elif isinstance(name_ref, (NameRef, AccessRef)):
2.9 return InvocationRef(name_ref)
2.10
2.11 # Provide a general reference to indicate that something is produced