|
6 | 6 |
|
7 | 7 | from typing import (
|
8 | 8 | Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator, Iterable,
|
9 |
| - Sequence |
| 9 | + Mapping, Sequence |
10 | 10 | )
|
11 | 11 | from typing_extensions import Final
|
12 | 12 |
|
|
47 | 47 | )
|
48 | 48 | from mypy.typeops import (
|
49 | 49 | map_type_from_supertype, bind_self, erase_to_bound, make_simplified_union,
|
50 |
| - erase_def_to_union_or_bound, erase_to_union_or_bound, |
| 50 | + erase_def_to_union_or_bound, erase_to_union_or_bound, coerce_to_literal, |
| 51 | + try_getting_str_literals_from_type, try_getting_int_literals_from_type, |
| 52 | + tuple_fallback, lookup_attribute_type, is_singleton_type, try_expanding_enum_to_union, |
51 | 53 | true_only, false_only, function_type,
|
52 | 54 | )
|
53 | 55 | from mypy import message_registry
|
|
72 | 74 | from mypy.plugin import Plugin, CheckerPluginInterface
|
73 | 75 | from mypy.sharedparse import BINARY_MAGIC_METHODS
|
74 | 76 | from mypy.scope import Scope
|
75 |
| -from mypy.typeops import ( |
76 |
| - tuple_fallback, coerce_to_literal, is_singleton_type, try_expanding_enum_to_union |
77 |
| -) |
78 | 77 | from mypy import state, errorcodes as codes
|
79 | 78 | from mypy.traverser import has_return_statement, all_return_statements
|
80 | 79 | from mypy.errorcodes import ErrorCode
|
@@ -3709,6 +3708,12 @@ def find_isinstance_check(self, node: Expression
|
3709 | 3708 |
|
3710 | 3709 | Guaranteed to not return None, None. (But may return {}, {})
|
3711 | 3710 | """
|
| 3711 | + if_map, else_map = self.find_isinstance_check_helper(node) |
| 3712 | + new_if_map = propagate_up_typemap_info(self.type_map, if_map) |
| 3713 | + new_else_map = propagate_up_typemap_info(self.type_map, else_map) |
| 3714 | + return new_if_map, new_else_map |
| 3715 | + |
| 3716 | + def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeMap]: |
3712 | 3717 | type_map = self.type_map
|
3713 | 3718 | if is_true_literal(node):
|
3714 | 3719 | return {}, None
|
@@ -3835,23 +3840,23 @@ def find_isinstance_check(self, node: Expression
|
3835 | 3840 | else None)
|
3836 | 3841 | return if_map, else_map
|
3837 | 3842 | elif isinstance(node, OpExpr) and node.op == 'and':
|
3838 |
| - left_if_vars, left_else_vars = self.find_isinstance_check(node.left) |
3839 |
| - right_if_vars, right_else_vars = self.find_isinstance_check(node.right) |
| 3843 | + left_if_vars, left_else_vars = self.find_isinstance_check_helper(node.left) |
| 3844 | + right_if_vars, right_else_vars = self.find_isinstance_check_helper(node.right) |
3840 | 3845 |
|
3841 | 3846 | # (e1 and e2) is true if both e1 and e2 are true,
|
3842 | 3847 | # and false if at least one of e1 and e2 is false.
|
3843 | 3848 | return (and_conditional_maps(left_if_vars, right_if_vars),
|
3844 | 3849 | or_conditional_maps(left_else_vars, right_else_vars))
|
3845 | 3850 | elif isinstance(node, OpExpr) and node.op == 'or':
|
3846 |
| - left_if_vars, left_else_vars = self.find_isinstance_check(node.left) |
3847 |
| - right_if_vars, right_else_vars = self.find_isinstance_check(node.right) |
| 3851 | + left_if_vars, left_else_vars = self.find_isinstance_check_helper(node.left) |
| 3852 | + right_if_vars, right_else_vars = self.find_isinstance_check_helper(node.right) |
3848 | 3853 |
|
3849 | 3854 | # (e1 or e2) is true if at least one of e1 or e2 is true,
|
3850 | 3855 | # and false if both e1 and e2 are false.
|
3851 | 3856 | return (or_conditional_maps(left_if_vars, right_if_vars),
|
3852 | 3857 | and_conditional_maps(left_else_vars, right_else_vars))
|
3853 | 3858 | elif isinstance(node, UnaryExpr) and node.op == 'not':
|
3854 |
| - left, right = self.find_isinstance_check(node.expr) |
| 3859 | + left, right = self.find_isinstance_check_helper(node.expr) |
3855 | 3860 | return right, left
|
3856 | 3861 |
|
3857 | 3862 | # Not a supported isinstance check
|
@@ -4780,3 +4785,96 @@ def has_bool_item(typ: ProperType) -> bool:
|
4780 | 4785 | return any(is_named_instance(item, 'builtins.bool')
|
4781 | 4786 | for item in typ.items)
|
4782 | 4787 | return False
|
| 4788 | + |
| 4789 | + |
| 4790 | +def propagate_up_typemap_info(existing_types: Mapping[Expression, Type], |
| 4791 | + new_types: TypeMap) -> TypeMap: |
| 4792 | + """Attempts refining parent expressions of any MemberExpr or IndexExprs in new_types. |
| 4793 | +
|
| 4794 | + Specifically, this function accepts two mappings of expression to original types: |
| 4795 | + the original mapping (existing_types), and a new mapping (new_types) intended to |
| 4796 | + update the original. |
| 4797 | +
|
| 4798 | + This function iterates through new_types and attempts to use the information to try |
| 4799 | + refining the parent type if |
| 4800 | + """ |
| 4801 | + if new_types is None: |
| 4802 | + return None |
| 4803 | + output_map = {} |
| 4804 | + for expr, typ in new_types.items(): |
| 4805 | + # The original inferred type should always be present in the output map, of course |
| 4806 | + output_map[expr] = typ |
| 4807 | + |
| 4808 | + # Next, check and see if this expression is one that's attempting to |
| 4809 | + # "index" into the parent type. If so, grab both the parent and the "key". |
| 4810 | + keys = [] # type: Sequence[Union[str, int]] |
| 4811 | + if isinstance(expr, MemberExpr): |
| 4812 | + parent_expr = expr.expr |
| 4813 | + parent_type = existing_types.get(parent_expr) |
| 4814 | + variant_name = expr.name |
| 4815 | + keys = [variant_name] |
| 4816 | + elif isinstance(expr, IndexExpr): |
| 4817 | + parent_expr = expr.base |
| 4818 | + parent_type = existing_types.get(parent_expr) |
| 4819 | + |
| 4820 | + variant_type = existing_types.get(expr.index) |
| 4821 | + if variant_type is None: |
| 4822 | + continue |
| 4823 | + |
| 4824 | + str_literals = try_getting_str_literals_from_type(variant_type) |
| 4825 | + if str_literals is not None: |
| 4826 | + keys = str_literals |
| 4827 | + else: |
| 4828 | + int_literals = try_getting_int_literals_from_type(variant_type) |
| 4829 | + if int_literals is not None: |
| 4830 | + keys = int_literals |
| 4831 | + else: |
| 4832 | + continue |
| 4833 | + else: |
| 4834 | + continue |
| 4835 | + |
| 4836 | + # We don't try inferring anything if we've either already inferred something for |
| 4837 | + # the parent expression or if the parent somehow doesn't already have an existing type |
| 4838 | + if parent_expr in new_types or parent_type is None: |
| 4839 | + continue |
| 4840 | + |
| 4841 | + # If the parent isn't a union, we won't be able to perform any useful refinements. |
| 4842 | + # So, give up and carry on. |
| 4843 | + # |
| 4844 | + # TODO: We currently refine just the immediate parent. Should we also try refining |
| 4845 | + # any parents of the parents? |
| 4846 | + # |
| 4847 | + # One quick-and-dirty way of doing this would be to have the caller repeatedly run |
| 4848 | + # this function until we seem fixpoint, but that seems expensive. |
| 4849 | + parent_type = get_proper_type(parent_type) |
| 4850 | + if not isinstance(parent_type, UnionType): |
| 4851 | + continue |
| 4852 | + |
| 4853 | + # Take each potential parent type in the union and try "indexing" into it using. |
| 4854 | + # Does the resulting type overlap with the deduced type of the original expression? |
| 4855 | + # If so, keep the parent type in the union. |
| 4856 | + new_parent_types = [] |
| 4857 | + for item in parent_type.items: |
| 4858 | + item = get_proper_type(item) |
| 4859 | + member_types = [] |
| 4860 | + for key in keys: |
| 4861 | + t = lookup_attribute_type(item, key) |
| 4862 | + if t is not None: |
| 4863 | + member_types.append(t) |
| 4864 | + member_type_for_item = make_simplified_union(member_types) |
| 4865 | + if member_type_for_item is None: |
| 4866 | + # We were unable to obtain the member type. So, we give up on refining this |
| 4867 | + # parent type entirely. |
| 4868 | + new_parent_types = [] |
| 4869 | + break |
| 4870 | + |
| 4871 | + if is_overlapping_types(member_type_for_item, typ): |
| 4872 | + new_parent_types.append(item) |
| 4873 | + |
| 4874 | + # If none of the parent types overlap (if we derived an empty union), either |
| 4875 | + # we deliberately aborted or something went wrong. Deriving the uninhabited |
| 4876 | + # type seems unhelpful, so let's just skip refining the parent expression. |
| 4877 | + if new_parent_types: |
| 4878 | + output_map[parent_expr] = make_simplified_union(new_parent_types) |
| 4879 | + |
| 4880 | + return output_map |
0 commit comments