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
25 changes: 21 additions & 4 deletions mypy/reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
MemberExpr,
NameExpr,
OpExpr,
SetExpr,
SliceExpr,
StrExpr,
TupleExpr,
Expand Down Expand Up @@ -232,19 +233,27 @@ def consider_sys_platform(expr: Expression, platform: str) -> int:
# - sys.platform == 'linux'
# - sys.platform != 'win32'
# - sys.platform.startswith('win')
# - sys.platform in {'linux', 'darwin'}
# - sys.platform not in ('win32', 'cygwin')
if isinstance(expr, ComparisonExpr):
# Let's not yet support chained comparisons.
if len(expr.operators) > 1:
return TRUTH_VALUE_UNKNOWN
op = expr.operators[0]
if op not in ("==", "!="):
return TRUTH_VALUE_UNKNOWN
if not is_sys_attr(expr.operands[0], "platform"):
return TRUTH_VALUE_UNKNOWN
right = expr.operands[1]
if not isinstance(right, StrExpr):
if op in ("==", "!=") and isinstance(right, StrExpr):
return fixed_comparison(platform, op, right.value)
if op not in ("in", "not in"):
return TRUTH_VALUE_UNKNOWN
items = contains_tuple_or_set_of_strings(right)
if items is None:
return TRUTH_VALUE_UNKNOWN
return fixed_comparison(platform, op, right.value)
result = platform in items
if op == "not in":
result = not result
return ALWAYS_TRUE if result else ALWAYS_FALSE
elif isinstance(expr, CallExpr):
if not isinstance(expr.callee, MemberExpr):
return TRUTH_VALUE_UNKNOWN
Expand Down Expand Up @@ -296,6 +305,14 @@ def contains_int_or_tuple_of_ints(expr: Expression) -> None | int | tuple[int, .
return None


def contains_tuple_or_set_of_strings(expr: Expression) -> tuple[str, ...] | None:
if isinstance(expr, (TupleExpr, SetExpr)) and all(
isinstance(item, StrExpr) for item in expr.items
):
return tuple(item.value for item in expr.items if isinstance(item, StrExpr))
return None


def contains_sys_version_info(expr: Expression) -> None | int | tuple[int | None, int | None]:
if is_sys_attr(expr, "version_info"):
return (None, None) # Same as sys.version_info[:]
Expand Down
76 changes: 76 additions & 0 deletions test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,82 @@ reveal_type(x) # N: Revealed type is "builtins.str"
[builtins fixtures/ops.pyi]
[out]

[case testCustomSysPlatformMembershipTuple]
# flags: --platform linux
import sys
if sys.platform in ('linux', 'darwin'):
x = "foo"
else:
x = 3
reveal_type(x) # N: Revealed type is "builtins.str"

if sys.platform not in ('win32', 'cygwin'):
y = "foo"
else:
y = 3
reveal_type(y) # N: Revealed type is "builtins.str"

if sys.platform in ('win32', 'cygwin'):
z = "foo"
else:
z = 3
reveal_type(z) # N: Revealed type is "builtins.int"

if sys.platform not in ('linux', 'darwin'):
w = "foo"
else:
w = 3
reveal_type(w) # N: Revealed type is "builtins.int"
[builtins fixtures/ops.pyi]
[out]

[case testCustomSysPlatformMembershipSet]
# flags: --platform linux
import sys
if sys.platform in {'linux', 'darwin'}:
x = "foo"
else:
x = 3
reveal_type(x) # N: Revealed type is "builtins.str"

if sys.platform not in {'linux', 'darwin'}:
y = "foo"
else:
y = 3
reveal_type(y) # N: Revealed type is "builtins.int"
[builtins fixtures/set.pyi]
[out]

[case testSysPlatformMembershipUnknown]
import sys

platform = sys.platform
if sys.platform in ('linux', platform):
def f() -> int: return 0
else:
def f() -> str: return '' # E: All conditional function variants must have identical signatures \
# N: Original: \
# N: def f() -> int \
# N: Redefinition: \
# N: def f() -> str
[builtins fixtures/ops.pyi]
[out]

[case testSysPlatformMembershipListIsUnknown]
# flags: --platform linux
import sys

if sys.platform in ['linux', 'darwin']:
def f() -> int: return 0
else:
def f() -> str: return '' # E: All conditional function variants must have identical signatures \
# N: Original: \
# N: def f() -> int \
# N: Redefinition: \
# N: def f() -> str
[builtins fixtures/list.pyi]
[out]

[case testShortCircuitInExpression]
import typing
def make() -> bool: pass
Expand Down
Loading