diff --git a/mypy/checker.py b/mypy/checker.py index 33705c98e10c..79831766fb22 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5345,6 +5345,11 @@ def visit_if_stmt(self, s: IfStmt) -> None: # Fall-through to the original frame is handled explicitly in each block. with self.binder.frame_context(can_skip=False, conditional_frame=True, fall_through=0): for e, b in zip(s.expr, s.body): + # If condition is 'x and y' and body always raises, + # suppress false redundant-expr warning (see issue #21533) + if isinstance(e, OpExpr) and e.op == "and": + if b.body and all(self.is_noop_for_reachability(s) for s in b.body): + e.right_always = True t = get_proper_type(self.expr_checker.accept(e)) if isinstance(t, DeletedType): diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 7bab7baa6ceb..d635773f3cdb 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -4281,3 +4281,27 @@ def func(y: H) -> H: else: return y [builtins fixtures/primitives.pyi] + +[case testRedundantExprAndWithRaise] +# flags: --enable-error-code redundant-expr +from typing_extensions import TypeIs +from typing import TypeVar, Sequence, Any + +class Cat: ... + +T = TypeVar('T') +MyType = TypeVar('MyType', int, str, Cat) + +def _is_seq_of(seq: Sequence[Any], tp: type[T]) -> TypeIs[Sequence[T]]: ... + +def main1(a: Sequence[MyType]) -> MyType: + if not _is_seq_of(a, Cat) and not _is_seq_of(a, int): + raise Exception("unexpected") + return a[0] + +def main2(a: Sequence[MyType]) -> MyType: + if not _is_seq_of(a, Cat): + if not _is_seq_of(a, int): + raise Exception("unexpected") + return a[0] +[builtins fixtures/exception.pyi]