# HG changeset patch # User Paul Boddie # Date 1489337815 -3600 # Node ID 26281477b9c12728c24b6dbe16a7ac94eb797024 # Parent 66016f38e5a104a08eef2293d7bb8fffec3d310b Support deduction on invoked alias-initialising accesses. diff -r 66016f38e5a1 -r 26281477b9c1 deducer.py --- a/deducer.py Sun Mar 12 17:46:28 2017 +0100 +++ b/deducer.py Sun Mar 12 17:56:55 2017 +0100 @@ -1609,7 +1609,9 @@ have_access = self.provider_class_types.has_key(accessor_location) # With an access, attempt to narrow the existing selection of provider - # types. + # types. Invocations attempt to find return value information, with + # instance return values also yielding class providers (since attributes + # on instances could be provided by classes). if have_access: provider_class_types = self.provider_class_types[accessor_location] @@ -1624,19 +1626,19 @@ for access_location in self.alias_index[accessor_location]: location, name, attrnames, access_number = access_location + invocation = self.reference_invocations.get(access_location) # Alias references an attribute access. if attrnames: - # Obtain attribute references for the access. + # Obtain references and attribute types for the access. attrs = self.get_references_for_access(access_location) - - # Separate the different attribute types. - - (class_types, instance_types, module_types, - function_types, var_types) = separate_types(attrs) + attrs = self.convert_invocation_providers(attrs, invocation) + + (class_types, instance_types, module_types, function_types, + var_types) = separate_types(attrs) # Where non-accessor types are found, do not attempt to refine # the defined accessor types. @@ -1644,6 +1646,9 @@ if function_types or var_types: return + # Invocations converting class accessors to instances do not + # change the nature of class providers. + class_types = set(provider_class_types).intersection(class_types) instance_types = set(provider_instance_types).intersection(instance_types) module_types = set(provider_module_types).intersection(module_types) @@ -1655,8 +1660,10 @@ attr = self.get_initialised_name(access_location) if attr: - (class_types, instance_types, module_types, - _function_types, _var_types) = separate_types([attr]) + attrs = self.convert_invocation_providers([attr], invocation) + + (class_types, instance_types, module_types, function_types, + var_types) = separate_types(attrs) # Where no further information is found, do not attempt to # refine the defined accessor types. @@ -1674,6 +1681,8 @@ self.record_reference_types(accessor_location, all_class_types, all_instance_types, all_module_types, False) # Without an access, attempt to identify references for the alias. + # Invocations convert classes to instances and also attempt to find + # return value information. else: refs = set() @@ -1686,6 +1695,8 @@ access_location = self.const_accesses[access_location] location, name, attrnames, access_number = access_location + invocation = self.reference_invocations.get(access_location) + attrnames = attrnames and attrnames.split(".") remaining = attrnames and len(attrnames) > 1 @@ -1701,21 +1712,31 @@ attrname = attrnames and attrnames[0] if attrname: + + # Obtain references and attribute types for the access. + attrs = self.get_references_for_access(access_location) + attrs = self.convert_invocations(attrs, invocation) refs.update(attrs) # Alias references a name, not an access. else: + + # Obtain initialiser information. + attr = self.get_initialised_name(access_location) - attrs = attr and [attr] or [] - if not attrs and self.provider_class_types.has_key(access_location): + if attr: + refs.update(self.convert_invocations([attr], invocation)) + + # Obtain provider information. + + elif self.provider_class_types.has_key(access_location): class_types = self.provider_class_types[access_location] instance_types = self.provider_instance_types[access_location] module_types = self.provider_module_types[access_location] - attrs = combine_types(class_types, instance_types, module_types) - if attrs: - refs.update(attrs) + + refs.update(combine_types(class_types, instance_types, module_types)) # Record reference details for the alias separately from accessors. @@ -1730,6 +1751,53 @@ attrs.append(attr) return attrs + def convert_invocation_providers(self, refs, invocation): + + """ + Convert 'refs' to providers corresponding to the results of invoking + each of the given references, if 'invocation' is set to a true value. + """ + + if not invocation: + return refs + + providers = set() + + for ref in refs: + ref = self.convert_invocation_provider(ref) + if ref.has_kind(""): + providers.add(Reference("", ref.get_origin())) + providers.add(ref) + + return providers + + def convert_invocation_provider(self, ref): + + "Convert 'ref' to a provider appropriate to its invocation result." + + if ref and ref.has_kind(""): + return ref + + return Reference("") + + def convert_invocations(self, refs, invocation): + + """ + Convert 'refs' to invocation results if 'invocation' is set to a true + value. + """ + + return invocation and map(self.convert_invocation, refs) or refs + + def convert_invocation(self, ref): + + "Convert 'ref' to its invocation result." + + if ref and ref.has_kind(""): + return ref.instance_of() + + return Reference("") + def get_initialised_name(self, access_location): """ diff -r 66016f38e5a1 -r 26281477b9c1 inspector.py --- a/inspector.py Sun Mar 12 17:46:28 2017 +0100 +++ b/inspector.py Sun Mar 12 17:56:55 2017 +0100 @@ -766,7 +766,7 @@ if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind(""): return InstanceRef(name_ref.reference().instance_of()) - elif isinstance(name_ref, NameRef): + elif isinstance(name_ref, (NameRef, AccessRef)): return InvocationRef(name_ref) # Provide a general reference to indicate that something is produced diff -r 66016f38e5a1 -r 26281477b9c1 resolving.py --- a/resolving.py Sun Mar 12 17:46:28 2017 +0100 +++ b/resolving.py Sun Mar 12 17:56:55 2017 +0100 @@ -283,13 +283,12 @@ # but they may be resolvable later. if not ref: - if not invocation: - # Record the path used for tracking purposes - # alongside original name, attribute and access - # number details. + # Record the path used for tracking purposes + # alongside original name, attribute and access + # number details. - aliased_names[i] = path, name_ref.original_name, name_ref.attrnames, name_ref.number + aliased_names[i] = path, name_ref.original_name, name_ref.attrnames, name_ref.number continue @@ -303,13 +302,12 @@ # cannot be resolved, but they may be resolvable later. if not ref: - if not invocation: - # Record the path used for tracking purposes - # alongside original name, attribute and access - # number details. + # Record the path used for tracking purposes + # alongside original name, attribute and access + # number details. - aliased_names[i] = path, name_ref.name, None, name_ref.number + aliased_names[i] = path, name_ref.name, None, name_ref.number continue