|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +import functools |
5 | 6 | import itertools |
6 | 7 | from collections import defaultdict |
7 | 8 | from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet |
|
25 | 26 | from mypy.binder import ConditionalTypeBinder, Frame, get_declaration |
26 | 27 | from mypy.checker_shared import CheckerScope, TypeCheckerSharedApi, TypeRange |
27 | 28 | from mypy.checker_state import checker_state |
28 | | -from mypy.checkexpr import type_info_from_type |
29 | 29 | from mypy.checkmember import ( |
30 | 30 | MemberContext, |
31 | 31 | analyze_class_attribute_access, |
@@ -6239,115 +6239,86 @@ def is_type_call(expr: CallExpr) -> bool: |
6239 | 6239 | # exprs that are being passed into type |
6240 | 6240 | exprs_in_type_calls: list[Expression] = [] |
6241 | 6241 | # all the types that an expression will have if the overall expression is truthy |
6242 | | - target_types: list[Instance] = [] |
| 6242 | + target_types: list[list[TypeRange]] = [] |
6243 | 6243 | # only a single type can be used when passed directly (eg "str") |
6244 | | - fixed_type: TypeRange | None = None |
| 6244 | + fixed_type: Type | None = None |
6245 | 6245 | # is this single type final? |
6246 | 6246 | is_final = False |
6247 | 6247 |
|
6248 | | - def update_fixed_type(new_fixed_type: TypeRange, new_is_final: bool) -> bool: |
| 6248 | + def update_fixed_type(new_fixed_type: Type, new_is_final: bool) -> bool: |
6249 | 6249 | """Returns if the update succeeds""" |
6250 | 6250 | nonlocal fixed_type, is_final |
6251 | | - if update := ( |
6252 | | - fixed_type is None |
6253 | | - or ( |
6254 | | - new_fixed_type.is_upper_bound == fixed_type.is_upper_bound |
6255 | | - and is_same_type(new_fixed_type.item, fixed_type.item) |
6256 | | - ) |
6257 | | - ): |
| 6251 | + if update := (fixed_type is None or (is_same_type(new_fixed_type, fixed_type))): |
6258 | 6252 | fixed_type = new_fixed_type |
6259 | 6253 | is_final = new_is_final |
6260 | 6254 | return update |
6261 | 6255 |
|
6262 | 6256 | for index in expr_indices: |
6263 | 6257 | expr = node.operands[index] |
| 6258 | + proper_type = get_proper_type(self.lookup_type(expr)) |
6264 | 6259 |
|
6265 | 6260 | if isinstance(expr, CallExpr) and is_type_call(expr): |
6266 | 6261 | arg = expr.args[0] |
6267 | 6262 | exprs_in_type_calls.append(arg) |
6268 | | - typ = self.lookup_type(arg) |
6269 | | - else: |
6270 | | - proper_type = get_proper_type(self.lookup_type(expr)) |
6271 | | - # get the range as though we were using isinstance |
6272 | | - type_range = self.isinstance_type_range(proper_type) |
6273 | | - # None range means this should not be used in comparison (eg tuple) |
6274 | | - if type_range is None: |
6275 | | - fixed_type = TypeRange(UninhabitedType(), True) |
6276 | | - continue |
| 6263 | + elif ( |
| 6264 | + isinstance(expr, OpExpr) |
| 6265 | + or isinstance(proper_type, TupleType) |
| 6266 | + or is_named_instance(proper_type, "builtins.tuple") |
| 6267 | + ): |
| 6268 | + # not valid for type comparisons, but allowed for isinstance checks |
| 6269 | + fixed_type = UninhabitedType() |
| 6270 | + continue |
6277 | 6271 |
|
6278 | | - if isinstance(expr, RefExpr) and isinstance(expr.node, TypeInfo): |
6279 | | - if not update_fixed_type(type_range, expr.node.is_final): |
| 6272 | + type_range = self.get_isinstance_type(expr) |
| 6273 | + if type_range is not None: |
| 6274 | + target_types.append(type_range) |
| 6275 | + if ( |
| 6276 | + isinstance(expr, RefExpr) |
| 6277 | + and isinstance(expr.node, TypeInfo) |
| 6278 | + and len(type_range) == 1 |
| 6279 | + ): |
| 6280 | + if not update_fixed_type( |
| 6281 | + Instance( |
| 6282 | + expr.node, |
| 6283 | + [AnyType(TypeOfAny.special_form)] * len(expr.node.defn.type_vars), |
| 6284 | + ), |
| 6285 | + expr.node.is_final, |
| 6286 | + ): |
6280 | 6287 | return None, {} |
6281 | | - typ = type_range.item |
6282 | | - |
6283 | | - type_info = type_info_from_type(typ) |
6284 | | - if type_info is not None: |
6285 | | - target_types.append( |
6286 | | - Instance( |
6287 | | - type_info, |
6288 | | - [AnyType(TypeOfAny.special_form)] * len(type_info.defn.type_vars), |
6289 | | - ) |
6290 | | - ) |
6291 | 6288 |
|
6292 | 6289 | if not exprs_in_type_calls: |
6293 | 6290 | return {}, {} |
6294 | 6291 |
|
6295 | | - if target_types: |
6296 | | - least_type: Type | None = target_types[0] |
6297 | | - for target_type in target_types[1:]: |
6298 | | - if fixed_type is None: |
6299 | | - # intersect types if fixed type doesn't need keeping |
6300 | | - least_type = self.intersect_instances( |
6301 | | - (cast(Instance, least_type), target_type), [] # what to do with errors? |
6302 | | - ) |
6303 | | - else: |
6304 | | - # otherwise, be safe and use meet |
6305 | | - least_type = meet_types(cast(Type, least_type), target_type) |
6306 | | - if least_type is None: |
6307 | | - break |
6308 | | - elif fixed_type: |
6309 | | - least_type = fixed_type.item |
6310 | | - else: |
6311 | | - # no bounds means no inference can be made |
6312 | | - return {}, {} |
6313 | | - |
6314 | | - # if the type differs from the fixed type, comparison cannot succeed |
6315 | | - if least_type is None or ( |
6316 | | - fixed_type is not None and not is_same_type(least_type, fixed_type.item) |
6317 | | - ): |
6318 | | - return None, {} |
6319 | | - |
6320 | | - shared_type = [TypeRange(least_type, not is_final)] |
6321 | | - |
6322 | | - if_maps: list[TypeMap] = [] |
6323 | | - else_maps: list[TypeMap] = [] |
| 6292 | + if_maps = [] |
| 6293 | + else_maps = [] |
6324 | 6294 | for expr in exprs_in_type_calls: |
6325 | | - if_map, else_map = conditional_types_to_typemaps( |
6326 | | - expr, |
6327 | | - *self.conditional_types_with_intersection( |
6328 | | - self.lookup_type(expr), shared_type, expr |
6329 | | - ), |
6330 | | - ) |
| 6295 | + expr_type = get_proper_type(self.lookup_type(expr)) |
| 6296 | + for type_range in target_types: |
| 6297 | + new_expr_type, _ = self.conditional_types_with_intersection( |
| 6298 | + expr_type, type_range, expr |
| 6299 | + ) |
| 6300 | + if new_expr_type is not None: |
| 6301 | + new_expr_type = get_proper_type(new_expr_type) |
| 6302 | + if isinstance(expr_type, AnyType): |
| 6303 | + expr_type = new_expr_type |
| 6304 | + elif not isinstance(new_expr_type, AnyType): |
| 6305 | + expr_type = meet_types(expr_type, new_expr_type) |
| 6306 | + _, else_map = conditional_types_to_typemaps( |
| 6307 | + expr, |
| 6308 | + *self.conditional_types_with_intersection( |
| 6309 | + (self.lookup_type(expr)), (type_range), expr |
| 6310 | + ), |
| 6311 | + ) |
| 6312 | + else_maps.append(else_map) |
| 6313 | + if fixed_type and expr_type is not None: |
| 6314 | + expr_type = meet_types(expr_type, fixed_type) |
| 6315 | + |
| 6316 | + if_map, _ = conditional_types_to_typemaps(expr, expr_type, None) |
6331 | 6317 | if_maps.append(if_map) |
6332 | | - else_maps.append(else_map) |
6333 | 6318 |
|
6334 | | - def combine_maps(list_maps: list[TypeMap]) -> TypeMap: |
6335 | | - """Combine all typemaps in list_maps into one typemap""" |
6336 | | - if all(m is None for m in list_maps): |
6337 | | - return None |
6338 | | - result_map = {} |
6339 | | - for d in list_maps: |
6340 | | - if d is not None: |
6341 | | - result_map.update(d) |
6342 | | - return result_map |
6343 | | - |
6344 | | - if_map = combine_maps(if_maps) |
6345 | | - # type(x) == T is only true when x has the same type as T, meaning |
6346 | | - # that it can be false if x is an instance of a subclass of T. That means |
6347 | | - # we can't do any narrowing in the else case unless T is final, in which |
6348 | | - # case T can't be subclassed |
| 6319 | + if_map = functools.reduce(and_conditional_maps, if_maps) |
6349 | 6320 | if is_final: |
6350 | | - else_map = combine_maps(else_maps) |
| 6321 | + else_map = functools.reduce(or_conditional_maps, else_maps) |
6351 | 6322 | else: |
6352 | 6323 | else_map = {} |
6353 | 6324 | return if_map, else_map |
|
0 commit comments