Narrow type of expression through an assignment. Do nothing if the expression doesn't support narrowing. When not narrowing though an assignment (isinstance() etc.), use put() directly. This omits some special-casing logic for assignments.
(self, expr: Expression, type: Type, declared_type: Type | None)
| 463 | self.type_assignments = old_assignments |
| 464 | |
| 465 | def assign_type(self, expr: Expression, type: Type, declared_type: Type | None) -> None: |
| 466 | """Narrow type of expression through an assignment. |
| 467 | |
| 468 | Do nothing if the expression doesn't support narrowing. |
| 469 | |
| 470 | When not narrowing though an assignment (isinstance() etc.), use put() |
| 471 | directly. This omits some special-casing logic for assignments. |
| 472 | """ |
| 473 | # We should erase last known value in binder, because if we are using it, |
| 474 | # it means that the target is not final, and therefore can't hold a literal. |
| 475 | type = remove_instance_last_known_values(type) |
| 476 | |
| 477 | if self.type_assignments is not None: |
| 478 | # We are in a multiassign from union, defer the actual binding, |
| 479 | # just collect the types. |
| 480 | self.type_assignments[expr].append((type, declared_type)) |
| 481 | return |
| 482 | if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): |
| 483 | return |
| 484 | if not literal(expr): |
| 485 | return |
| 486 | self.invalidate_dependencies(expr) |
| 487 | |
| 488 | if declared_type is None: |
| 489 | # Not sure why this happens. It seems to mainly happen in |
| 490 | # member initialization. |
| 491 | return |
| 492 | if not is_subtype(type, declared_type): |
| 493 | # Pretty sure this is only happens when there's a type error. |
| 494 | |
| 495 | # Ideally this function wouldn't be called if the |
| 496 | # expression has a type error, though -- do other kinds of |
| 497 | # errors cause this function to get called at invalid |
| 498 | # times? |
| 499 | return |
| 500 | |
| 501 | p_declared = get_proper_type(declared_type) |
| 502 | p_type = get_proper_type(type) |
| 503 | if isinstance(p_type, AnyType): |
| 504 | # Any type requires some special casing, for both historical reasons, |
| 505 | # and to optimise user experience without sacrificing correctness too much. |
| 506 | if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred: |
| 507 | # First case: a local/global variable without explicit annotation, |
| 508 | # in this case we just assign Any (essentially following the SSA logic). |
| 509 | self.put(expr, type) |
| 510 | elif isinstance(p_declared, UnionType): |
| 511 | all_items = flatten_nested_unions(p_declared.items) |
| 512 | if any(isinstance(get_proper_type(item), NoneType) for item in all_items): |
| 513 | # Second case: explicit optional type, in this case we optimize for |
| 514 | # a common pattern when an untyped value used as a fallback replacing None. |
| 515 | new_items = [ |
| 516 | type if isinstance(get_proper_type(item), NoneType) else item |
| 517 | for item in all_items |
| 518 | ] |
| 519 | self.put(expr, UnionType(new_items)) |
| 520 | elif any(isinstance(get_proper_type(item), AnyType) for item in all_items): |
| 521 | # Third case: a union already containing Any (most likely from |
| 522 | # an un-imported name), in this case we allow assigning Any as well. |
no test coverage detected