Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5345,6 +5345,15 @@ 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):
# Suppress redundant-expr warnings for validation code
# that only terminates execution (raise,assert false, no return ,etc)
if (
isinstance(e, OpExpr)
and e.op == "and"
and b.body
and all(self.is_noop_for_reachability(stmt) for stmt in b.body)
):
e.suppress_redundant_expr = True
t = get_proper_type(self.expr_checker.accept(e))

if isinstance(t, DeletedType):
Expand Down
1 change: 1 addition & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4446,6 +4446,7 @@ def check_boolean_op(self, e: OpExpr) -> Type:
codes.REDUNDANT_EXPR in self.chk.options.enabled_error_codes
and left_unreachable
# don't report an error if it's intentional
and not e.suppress_redundant_expr
and not e.right_always
):
self.msg.redundant_left_operand(e.op, e.left)
Expand Down
4 changes: 4 additions & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2662,6 +2662,7 @@ class OpExpr(Expression):
"right_unreachable",
"analyzed",
"as_type",
"suppress_redundant_expr",
)

__match_args__ = ("left", "op", "right")
Expand All @@ -2675,6 +2676,8 @@ class OpExpr(Expression):
right_always: bool
# Per static analysis only: Is the right side unreachable?
right_unreachable: bool
# Suppress redundant-expr warnings for this expression.
suppress_redundant_expr: bool
# Used for expressions that represent a type "X | Y" in some contexts
analyzed: TypeAliasExpr | None
# If this value expression can also be parsed as a valid type expression,
Expand All @@ -2693,6 +2696,7 @@ def __init__(
self.right_always = False
self.right_unreachable = False
self.analyzed = analyzed
self.suppress_redundant_expr = False
self.as_type = NotParsed.VALUE

def accept(self, visitor: ExpressionVisitor[T]) -> T:
Expand Down
23 changes: 23 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,29 @@ for y in bad_list:
[builtins fixtures/isinstancelist.pyi]
[typing fixtures/typing-full.pyi]

[case testNoRedundantExprForTypeIsAndRaise]
# flags: --enable-error-code redundant-expr
from typing import TypeVar, Sequence, Any
from typing_extensions import TypeIs, Self

class Cat:
def foo(self) -> Self: # type: ignore[empty-body]
...

MyType = TypeVar('MyType', int, str, Cat)
T = TypeVar('T')

def _is_seq_of(seq: Sequence[Any], tp: type[T]) -> TypeIs[Sequence[T]]: # type: ignore[empty-body]
...

def main(a: Sequence[MyType]) -> MyType:
if not _is_seq_of(a, Cat) and not _is_seq_of(a, int):
assert False
return a[0]

[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]


[case testNamedTupleNameMismatch]
from typing import NamedTuple
Expand Down
Loading