|
168 | 168 | false_only,
|
169 | 169 | fixup_partial_type,
|
170 | 170 | function_type,
|
| 171 | + get_type_type_item, |
171 | 172 | get_type_vars,
|
172 | 173 | is_literal_type_like,
|
173 | 174 | is_singleton_type,
|
|
202 | 203 | Type,
|
203 | 204 | TypeAliasType,
|
204 | 205 | TypedDictType,
|
205 |
| - TypeGuardedType, |
206 | 206 | TypeOfAny,
|
207 | 207 | TypeTranslator,
|
208 | 208 | TypeType,
|
@@ -1397,6 +1397,31 @@ def check_func_def(
|
1397 | 1397 | body_is_trivial = is_trivial_body(defn.body)
|
1398 | 1398 | self.check_default_args(item, body_is_trivial)
|
1399 | 1399 |
|
| 1400 | + if mypy.options._based and typ.type_guard: |
| 1401 | + if typ.type_guard.target_value in typ.arg_names: |
| 1402 | + idx = typ.arg_names.index(typ.type_guard.target_value) # type: ignore[arg-type] |
| 1403 | + elif typ.type_guard.target_is_positional: |
| 1404 | + idx = typ.type_guard.target_value # type: ignore[assignment] |
| 1405 | + else: |
| 1406 | + assert isinstance(typ.definition, FuncDef) |
| 1407 | + idx = [arg.variable.name for arg in typ.definition.arguments].index( |
| 1408 | + typ.type_guard.target_value # type: ignore[arg-type] |
| 1409 | + ) |
| 1410 | + self.check_subtype( |
| 1411 | + typ.type_guard.type_guard, |
| 1412 | + typ.arg_types[idx], |
| 1413 | + typ.type_guard.type_guard, |
| 1414 | + ErrorMessage( |
| 1415 | + "A type-guard's type must be assignable to its parameter's type.", |
| 1416 | + code=codes.TYPEGUARD_SUBTYPE, |
| 1417 | + ), |
| 1418 | + "guard has type", |
| 1419 | + "parameter has type", |
| 1420 | + notes=[ |
| 1421 | + "If this is correct, try making it an intersection with the parameter type" |
| 1422 | + ], |
| 1423 | + ) |
| 1424 | + |
1400 | 1425 | # Type check body in a new scope.
|
1401 | 1426 | with self.binder.top_frame_context():
|
1402 | 1427 | # Copy some type narrowings from an outer function when it seems safe enough
|
@@ -5675,64 +5700,115 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM
|
5675 | 5700 | if is_false_literal(node):
|
5676 | 5701 | return None, {}
|
5677 | 5702 |
|
5678 |
| - if isinstance(node, CallExpr) and len(node.args) != 0: |
5679 |
| - expr = collapse_walrus(node.args[0]) |
5680 |
| - if refers_to_fullname(node.callee, "builtins.isinstance"): |
5681 |
| - if len(node.args) != 2: # the error will be reported elsewhere |
5682 |
| - return {}, {} |
5683 |
| - if literal(expr) == LITERAL_TYPE: |
5684 |
| - return conditional_types_to_typemaps( |
5685 |
| - expr, |
5686 |
| - *self.conditional_types_with_intersection( |
5687 |
| - self.lookup_type(expr), self.get_isinstance_type(node.args[1]), expr |
5688 |
| - ), |
5689 |
| - ) |
5690 |
| - elif refers_to_fullname(node.callee, "builtins.issubclass"): |
5691 |
| - if len(node.args) != 2: # the error will be reported elsewhere |
5692 |
| - return {}, {} |
5693 |
| - if literal(expr) == LITERAL_TYPE: |
5694 |
| - return self.infer_issubclass_maps(node, expr) |
5695 |
| - elif refers_to_fullname(node.callee, "builtins.callable"): |
5696 |
| - if len(node.args) != 1: # the error will be reported elsewhere |
5697 |
| - return {}, {} |
5698 |
| - if literal(expr) == LITERAL_TYPE: |
5699 |
| - vartype = self.lookup_type(expr) |
5700 |
| - return self.conditional_callable_type_map(expr, vartype) |
5701 |
| - elif refers_to_fullname(node.callee, "builtins.hasattr"): |
5702 |
| - if len(node.args) != 2: # the error will be reported elsewhere |
5703 |
| - return {}, {} |
5704 |
| - attr = try_getting_str_literals(node.args[1], self.lookup_type(node.args[1])) |
5705 |
| - if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1: |
5706 |
| - return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0]) |
5707 |
| - elif isinstance(node.callee, (RefExpr, CallExpr, LambdaExpr)): |
| 5703 | + if isinstance(node, CallExpr): |
| 5704 | + if len(node.args) != 0: |
| 5705 | + expr = collapse_walrus(node.args[0]) |
| 5706 | + if refers_to_fullname(node.callee, "builtins.isinstance"): |
| 5707 | + if len(node.args) != 2: # the error will be reported elsewhere |
| 5708 | + return {}, {} |
| 5709 | + if literal(expr) == LITERAL_TYPE: |
| 5710 | + return conditional_types_to_typemaps( |
| 5711 | + expr, |
| 5712 | + *self.conditional_types_with_intersection( |
| 5713 | + self.lookup_type(expr), |
| 5714 | + self.get_isinstance_type(node.args[1]), |
| 5715 | + expr, |
| 5716 | + ), |
| 5717 | + ) |
| 5718 | + elif refers_to_fullname(node.callee, "builtins.issubclass"): |
| 5719 | + if len(node.args) != 2: # the error will be reported elsewhere |
| 5720 | + return {}, {} |
| 5721 | + if literal(expr) == LITERAL_TYPE: |
| 5722 | + return self.infer_issubclass_maps(node, expr) |
| 5723 | + elif refers_to_fullname(node.callee, "builtins.callable"): |
| 5724 | + if len(node.args) != 1: # the error will be reported elsewhere |
| 5725 | + return {}, {} |
| 5726 | + if literal(expr) == LITERAL_TYPE: |
| 5727 | + vartype = self.lookup_type(expr) |
| 5728 | + return self.conditional_callable_type_map(expr, vartype) |
| 5729 | + elif refers_to_fullname(node.callee, "builtins.hasattr"): |
| 5730 | + if len(node.args) != 2: # the error will be reported elsewhere |
| 5731 | + return {}, {} |
| 5732 | + attr = try_getting_str_literals(node.args[1], self.lookup_type(node.args[1])) |
| 5733 | + if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1: |
| 5734 | + return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0]) |
| 5735 | + if isinstance(node.callee, (RefExpr, CallExpr, LambdaExpr)): |
| 5736 | + # TODO: AssignmentExpr `(a := A())()` |
5708 | 5737 | if node.callee.type_guard is not None:
|
5709 | 5738 | # TODO: Follow *args, **kwargs
|
5710 |
| - if node.arg_kinds[0] != nodes.ARG_POS: |
5711 |
| - # the first argument might be used as a kwarg |
5712 |
| - called_type = get_proper_type(self.lookup_type(node.callee)) |
5713 |
| - assert isinstance(called_type, (CallableType, Overloaded)) |
5714 |
| - |
5715 |
| - # *assuming* the overloaded function is correct, there's a couple cases: |
5716 |
| - # 1) The first argument has different names, but is pos-only. We don't |
5717 |
| - # care about this case, the argument must be passed positionally. |
5718 |
| - # 2) The first argument allows keyword reference, therefore must be the |
5719 |
| - # same between overloads. |
5720 |
| - name = called_type.items[0].arg_names[0] |
5721 |
| - |
5722 |
| - if name in node.arg_names: |
5723 |
| - idx = node.arg_names.index(name) |
5724 |
| - # we want the idx-th variable to be narrowed |
5725 |
| - expr = collapse_walrus(node.args[idx]) |
| 5739 | + called_type = get_proper_type(self.lookup_type(node.callee)) |
| 5740 | + if isinstance(called_type, Instance): |
| 5741 | + called_type = get_proper_type( |
| 5742 | + analyze_member_access( |
| 5743 | + name="__call__", |
| 5744 | + typ=called_type, |
| 5745 | + context=node, |
| 5746 | + is_lvalue=False, |
| 5747 | + is_super=False, |
| 5748 | + is_operator=True, |
| 5749 | + msg=self.msg, |
| 5750 | + original_type=called_type, |
| 5751 | + chk=self, |
| 5752 | + ) |
| 5753 | + ) |
| 5754 | + assert isinstance(called_type, (CallableType, Overloaded)) |
| 5755 | + guard = node.callee.type_guard |
| 5756 | + target = guard.target_value |
| 5757 | + if guard.target_is_class or guard.target_is_self: |
| 5758 | + if isinstance(node.callee, (CallExpr, NameExpr)): |
| 5759 | + expr = node.callee |
| 5760 | + elif isinstance(node.callee, MemberExpr): |
| 5761 | + expr = node.callee.expr |
5726 | 5762 | else:
|
5727 |
| - self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node) |
| 5763 | + raise AssertionError("What is this?") |
| 5764 | + if guard.target_is_class and isinstance( |
| 5765 | + get_proper_type(self.lookup_type(expr)), Instance |
| 5766 | + ): |
| 5767 | + guarded = get_type_type_item( |
| 5768 | + get_proper_type(node.callee.type_guard.type_guard) |
| 5769 | + ) |
| 5770 | + if not guarded: |
| 5771 | + return {}, {} |
| 5772 | + else: |
| 5773 | + guarded = node.callee.type_guard.type_guard |
| 5774 | + return {collapse_walrus(expr): guarded}, {} |
| 5775 | + if isinstance(target, int): |
| 5776 | + idx = target |
| 5777 | + if called_type.items[0].def_extras.get("first_arg"): |
| 5778 | + self.fail( |
| 5779 | + "type-guard on positional class function is unsupported", |
| 5780 | + node, |
| 5781 | + code=codes.TYPEGUARD_LIMITATION, |
| 5782 | + ) |
| 5783 | + return {}, {} |
| 5784 | + elif target in called_type.items[0].arg_names: |
| 5785 | + idx = called_type.items[0].arg_names.index(target) |
| 5786 | + elif target == "first argument": |
| 5787 | + idx = 0 |
| 5788 | + if not node.args: |
5728 | 5789 | return {}, {}
|
| 5790 | + else: |
| 5791 | + definition = called_type.items[0].definition |
| 5792 | + assert isinstance(definition, FuncDef) |
| 5793 | + idx = [arg.variable.name for arg in definition.arguments].index(target) |
| 5794 | + if target in node.arg_names: |
| 5795 | + idx = node.arg_names.index(target) # type: ignore[arg-type] |
| 5796 | + if len(node.arg_kinds) <= idx: |
| 5797 | + return {}, {} |
| 5798 | + if node.arg_kinds[idx].is_star(): |
| 5799 | + self.fail(message_registry.TYPE_GUARD_POS_LIMITATION, node) |
| 5800 | + return {}, {} |
| 5801 | + # we want the idx-th variable to be narrowed |
| 5802 | + expr = collapse_walrus(node.args[idx]) |
5729 | 5803 | if literal(expr) == LITERAL_TYPE:
|
5730 | 5804 | # Note: we wrap the target type, so that we can special case later.
|
5731 | 5805 | # Namely, for isinstance() we use a normal meet, while TypeGuard is
|
5732 | 5806 | # considered "always right" (i.e. even if the types are not overlapping).
|
5733 | 5807 | # Also note that a care must be taken to unwrap this back at read places
|
5734 | 5808 | # where we use this to narrow down declared type.
|
5735 |
| - return {expr: TypeGuardedType(node.callee.type_guard)}, {} |
| 5809 | + # |
| 5810 | + # Based: We don't do any of that. |
| 5811 | + return {expr: node.callee.type_guard.type_guard}, {} |
5736 | 5812 | if isinstance(node, CallExpr) and isinstance(node.callee, LambdaExpr):
|
5737 | 5813 | return self.find_isinstance_check_helper(
|
5738 | 5814 | safe(cast(ReturnStmt, node.callee.body.body[0]).expr)
|
|
0 commit comments