Skip to content

Commit 0950c97

Browse files
committed
Avoid invalid deprecation checks of in the else branch of conditional expressions
1 parent 39e752f commit 0950c97

File tree

2 files changed

+45
-4
lines changed

2 files changed

+45
-4
lines changed

mypy/checkexpr.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ class ExpressionChecker(ExpressionVisitor[Type], ExpressionCheckerSharedApi):
286286
plugin: Plugin
287287

288288
_arg_infer_context_cache: ArgumentInferContext | None
289+
# Used to prevent generating redundant or invalid `@deprecated()` reports
290+
_valid_pep702_type_context: bool
289291

290292
def __init__(
291293
self,
@@ -322,6 +324,7 @@ def __init__(
322324
type_state.infer_polymorphic = not self.chk.options.old_type_inference
323325

324326
self._arg_infer_context_cache = None
327+
self._valid_pep702_type_context = True
325328
self.expr_cache: dict[
326329
tuple[Expression, Type | None],
327330
tuple[int, Type, list[ErrorInfo], dict[Expression, Type]],
@@ -375,7 +378,7 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
375378
# Unknown reference; use any type implicitly to avoid
376379
# generating extra type errors.
377380
result = AnyType(TypeOfAny.from_error)
378-
if isinstance(node, TypeInfo):
381+
if self._valid_pep702_type_context and isinstance(node, TypeInfo):
379382
if self.type_context[-1] is not None:
380383
proper_result = get_proper_type(result)
381384
if isinstance(proper_result, (CallableType, Overloaded)):
@@ -5963,6 +5966,10 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F
59635966
e.else_expr,
59645967
context=if_type_fallback,
59655968
allow_none_return=allow_none_return,
5969+
# `@deprecated()` is already properly reported in the else branch when obtaining
5970+
# `full_context_else_type`. Reporting it again is redundant, and also invalid when
5971+
# analysing reference expressions here because the full type context is not used.
5972+
valid_pep702_type_context=False,
59665973
)
59675974

59685975
# In most cases using if_type as a context for right branch gives better inferred types.
@@ -5988,17 +5995,26 @@ def analyze_cond_branch(
59885995
context: Type | None,
59895996
allow_none_return: bool = False,
59905997
suppress_unreachable_errors: bool = True,
5998+
valid_pep702_type_context: bool = True,
59915999
) -> Type:
59926000
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
6001+
_valid_pep702_context = self._valid_pep702_type_context
6002+
self._valid_pep702_type_context = valid_pep702_type_context
6003+
result: Type
59936004
if map is None:
59946005
# We still need to type check node, in case we want to
59956006
# process it for isinstance checks later. Since the branch was
59966007
# determined to be unreachable, any errors should be suppressed.
59976008
with self.msg.filter_errors(filter_errors=suppress_unreachable_errors):
59986009
self.accept(node, type_context=context, allow_none_return=allow_none_return)
5999-
return UninhabitedType()
6000-
self.chk.push_type_map(map)
6001-
return self.accept(node, type_context=context, allow_none_return=allow_none_return)
6010+
result = UninhabitedType()
6011+
else:
6012+
self.chk.push_type_map(map)
6013+
result = self.accept(
6014+
node, type_context=context, allow_none_return=allow_none_return
6015+
)
6016+
self._valid_pep702_type_context = _valid_pep702_context
6017+
return result
60026018

60036019
def _combined_context(self, ty: Type | None) -> Type | None:
60046020
ctx_items = []

test-data/unit/check-deprecated.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,31 @@ class SettableChild2(Settable[type[A]]):
515515
[builtins fixtures/property.pyi]
516516

517517

518+
[case testDeprecatedClassConstructorInConditionalExprCallableTypeContext]
519+
# flags: --enable-error-code=deprecated
520+
521+
from typing import Any, Callable
522+
from typing_extensions import deprecated
523+
524+
class A:
525+
@deprecated("do not use")
526+
def __init__(self) -> None: ...
527+
528+
class B(A): ...
529+
530+
var: object
531+
callable_: Callable[..., Any] = (
532+
A # E: function __main__.A.__init__ is deprecated: do not use
533+
if (var is None) else
534+
B # E: function __main__.A.__init__ is deprecated: do not use
535+
)
536+
537+
TypeA1: type[A] = A if (var is None) else B
538+
TypeA2 = A if (var is None) else B
539+
540+
[builtins fixtures/tuple.pyi]
541+
542+
518543
[case testDeprecatedClassConstructorInUnionTypeContext]
519544
# flags: --enable-error-code=deprecated
520545

0 commit comments

Comments
 (0)