diff --git a/mypy/reachability.py b/mypy/reachability.py index 5d170b5071db..132c269e96af 100644 --- a/mypy/reachability.py +++ b/mypy/reachability.py @@ -115,31 +115,44 @@ def infer_condition_value(expr: Expression, options: Options) -> int: MYPY_TRUE if true under mypy and false at runtime, MYPY_FALSE if false under mypy and true at runtime, else TRUTH_VALUE_UNKNOWN. """ + if isinstance(expr, UnaryExpr) and expr.op == "not": + positive = infer_condition_value(expr.expr, options) + return inverted_truth_mapping[positive] + pyversion = options.python_version name = "" - negated = False - alias = expr - if isinstance(alias, UnaryExpr): - if alias.op == "not": - expr = alias.expr - negated = True + result = TRUTH_VALUE_UNKNOWN if isinstance(expr, NameExpr): name = expr.name elif isinstance(expr, MemberExpr): name = expr.name - elif isinstance(expr, OpExpr) and expr.op in ("and", "or"): + elif isinstance(expr, OpExpr): + if expr.op not in ("or", "and"): + return TRUTH_VALUE_UNKNOWN + left = infer_condition_value(expr.left, options) - if (left in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == "and") or ( - left in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == "or" - ): - # Either `True and ` or `False or `: the result will - # always be the right-hand-side. - return infer_condition_value(expr.right, options) - else: - # The result will always be the left-hand-side (e.g. ALWAYS_* or - # TRUTH_VALUE_UNKNOWN). - return left + right = infer_condition_value(expr.right, options) + results = {left, right} + if expr.op == "or": + if ALWAYS_TRUE in results: + return ALWAYS_TRUE + elif MYPY_TRUE in results: + return MYPY_TRUE + elif left == right == MYPY_FALSE: + return MYPY_FALSE + elif results <= {ALWAYS_FALSE, MYPY_FALSE}: + return ALWAYS_FALSE + elif expr.op == "and": + if ALWAYS_FALSE in results: + return ALWAYS_FALSE + elif MYPY_FALSE in results: + return MYPY_FALSE + elif left == right == ALWAYS_TRUE: + return ALWAYS_TRUE + elif results <= {ALWAYS_TRUE, MYPY_TRUE}: + return MYPY_TRUE + return TRUTH_VALUE_UNKNOWN else: result = consider_sys_version_info(expr, pyversion) if result == TRUTH_VALUE_UNKNOWN: @@ -155,8 +168,6 @@ def infer_condition_value(expr: Expression, options: Options) -> int: result = ALWAYS_TRUE elif name in options.always_false: result = ALWAYS_FALSE - if negated: - result = inverted_truth_mapping[result] return result diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 6821b74b8b6d..368431127b76 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -481,25 +481,101 @@ import typing def make() -> bool: pass PY2 = PY3 = make() -a = PY2 and 's' -b = PY3 and 's' -c = PY2 or 's' -d = PY3 or 's' -e = (PY2 or PY3) and 's' -f = (PY3 or PY2) and 's' -g = (PY2 or PY3) or 's' -h = (PY3 or PY2) or 's' +a = PY2 and str() +b = PY3 and str() +c = PY2 or str() +d = PY3 or str() +e = (PY2 or PY3) and str() +f = (PY3 or PY2) and str() +g = (PY2 or PY3) or str() +h = (PY3 or PY2) or str() reveal_type(a) # N: Revealed type is "builtins.bool" -reveal_type(b) # N: Revealed type is "Literal['s']" -reveal_type(c) # N: Revealed type is "Literal['s']" +reveal_type(b) # N: Revealed type is "builtins.str" +reveal_type(c) # N: Revealed type is "builtins.str" reveal_type(d) # N: Revealed type is "builtins.bool" -reveal_type(e) # N: Revealed type is "Literal['s']" -reveal_type(f) # N: Revealed type is "Literal['s']" +reveal_type(e) # N: Revealed type is "builtins.str" +reveal_type(f) # N: Revealed type is "builtins.str" reveal_type(g) # N: Revealed type is "builtins.bool" reveal_type(h) # N: Revealed type is "builtins.bool" [builtins fixtures/ops.pyi] [out] +[case testConditionalValuesBinaryOps] +# flags: --platform linux +import sys + +t_and_t = (sys.platform == 'linux' and sys.platform == 'linux') and str() +t_or_t = (sys.platform == 'linux' or sys.platform == 'linux') and str() +t_and_f = (sys.platform == 'linux' and sys.platform == 'windows') and str() +t_or_f = (sys.platform == 'linux' or sys.platform == 'windows') and str() +f_and_t = (sys.platform == 'windows' and sys.platform == 'linux') and str() +f_or_t = (sys.platform == 'windows' or sys.platform == 'linux') and str() +f_and_f = (sys.platform == 'windows' and sys.platform == 'windows') and str() +f_or_f = (sys.platform == 'windows' or sys.platform == 'windows') and str() +reveal_type(t_and_t) # N: Revealed type is "builtins.str" +reveal_type(t_or_t) # N: Revealed type is "builtins.str" +reveal_type(f_and_t) # N: Revealed type is "builtins.bool" +reveal_type(f_or_t) # N: Revealed type is "builtins.str" +reveal_type(t_and_f) # N: Revealed type is "builtins.bool" +reveal_type(t_or_f) # N: Revealed type is "builtins.str" +reveal_type(f_and_f) # N: Revealed type is "builtins.bool" +reveal_type(f_or_f) # N: Revealed type is "builtins.bool" +[builtins fixtures/ops.pyi] + +[case testConditionalValuesNegation] +# flags: --platform linux +import sys + +not_t = not sys.platform == 'linux' and str() +not_f = not sys.platform == 'windows' and str() +not_and_t = not (sys.platform == 'linux' and sys.platform == 'linux') and str() +not_and_f = not (sys.platform == 'linux' and sys.platform == 'windows') and str() +not_or_t = not (sys.platform == 'linux' or sys.platform == 'linux') and str() +not_or_f = not (sys.platform == 'windows' or sys.platform == 'windows') and str() +reveal_type(not_t) # N: Revealed type is "builtins.bool" +reveal_type(not_f) # N: Revealed type is "builtins.str" +reveal_type(not_and_t) # N: Revealed type is "builtins.bool" +reveal_type(not_and_f) # N: Revealed type is "builtins.str" +reveal_type(not_or_t) # N: Revealed type is "builtins.bool" +reveal_type(not_or_f) # N: Revealed type is "builtins.str" +[builtins fixtures/ops.pyi] + +[case testConditionalValuesUnsupportedOps] +# flags: --platform linux +import sys + +unary_minus = -(sys.platform == 'linux') and str() +binary_minus = ((sys.platform == 'linux') - (sys.platform == 'linux')) and str() +reveal_type(unary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]" +reveal_type(binary_minus) # N: Revealed type is "Union[Literal[0], builtins.str]" +[builtins fixtures/ops.pyi] + +[case testMypyFalseValuesInBinaryOps_no_empty] +# flags: --platform linux +import sys +from typing import TYPE_CHECKING + +MYPY = 0 + +if TYPE_CHECKING and sys.platform == 'linux': + def foo1() -> int: ... +if sys.platform == 'linux' and TYPE_CHECKING: + def foo2() -> int: ... +if MYPY and sys.platform == 'linux': + def foo3() -> int: ... +if sys.platform == 'linux' and MYPY: + def foo4() -> int: ... + +if TYPE_CHECKING or sys.platform == 'linux': + def bar1() -> int: ... # E: Missing return statement +if sys.platform == 'linux' or TYPE_CHECKING: + def bar2() -> int: ... # E: Missing return statement +if MYPY or sys.platform == 'linux': + def bar3() -> int: ... # E: Missing return statement +if sys.platform == 'linux' or MYPY: + def bar4() -> int: ... # E: Missing return statement +[builtins fixtures/ops.pyi] + [case testShortCircuitAndWithConditionalAssignment] # flags: --platform linux import sys diff --git a/test-data/unit/fixtures/ops.pyi b/test-data/unit/fixtures/ops.pyi index df3b163166ad..67bc74b35c51 100644 --- a/test-data/unit/fixtures/ops.pyi +++ b/test-data/unit/fixtures/ops.pyi @@ -25,7 +25,7 @@ class tuple(Sequence[Tco]): class function: pass class str: - def __init__(self, x: 'int') -> None: pass + def __init__(self, x: 'int' = ...) -> None: pass def __add__(self, x: 'str') -> 'str': pass def __eq__(self, x: object) -> bool: pass def startswith(self, x: 'str') -> bool: pass pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy