Skip to content

Commit

Permalink
Short-circuit if expression for always true/always false vars and M…
Browse files Browse the repository at this point in the history
…YPY/TYPE_CHECKING

 - Always short-circuit in the if's expression, instead of only
   short-circuiting for "ALWAYS_TRUE/FALSE" constants (e.g. sys.platform
   checks) and not "MYPY_TRUE/FALSE" constants (e.g. `MYPY`)

 - Make --always-true and --always-false map to ALWAYS_TRUE and
   ALWAYS_FALSE, respectively, instead of MYPY_TRUE and MYPY_FALSE.
   The only difference between the two is a change in import priority,
   but semantically uses of "--always-true" are probably closer to
   "this condition should be true at compile time and runtime" than
   "this condition should be true at compile time but false at
   runtime".

 - Fixes #5963
  • Loading branch information
samertm committed Dec 19, 2018
1 parent acc7740 commit 268b99c
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
8 changes: 4 additions & 4 deletions mypy/reachability.py
Expand Up @@ -84,8 +84,8 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
name = expr.name
elif isinstance(expr, OpExpr) and expr.op in ('and', 'or'):
left = infer_condition_value(expr.left, options)
if ((left == ALWAYS_TRUE and expr.op == 'and') or
(left == ALWAYS_FALSE and expr.op == 'or')):
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 <other>` or `False or <other>`: the result will
# always be the right-hand-side.
return infer_condition_value(expr.right, options)
Expand All @@ -105,9 +105,9 @@ def infer_condition_value(expr: Expression, options: Options) -> int:
elif name == 'MYPY' or name == 'TYPE_CHECKING':
result = MYPY_TRUE
elif name in options.always_true:
result = MYPY_TRUE
result = ALWAYS_TRUE
elif name in options.always_false:
result = MYPY_FALSE
result = ALWAYS_FALSE
if negated:
result = inverted_truth_mapping[result]
return result
Expand Down
11 changes: 6 additions & 5 deletions mypy/semanal.py
Expand Up @@ -90,7 +90,8 @@
from mypy.semanal_enum import EnumCallAnalyzer
from mypy.semanal_newtype import NewTypeAnalyzer
from mypy.reachability import (
infer_reachability_of_if_statement, infer_condition_value, ALWAYS_FALSE, ALWAYS_TRUE
infer_reachability_of_if_statement, infer_condition_value, ALWAYS_FALSE, ALWAYS_TRUE,
MYPY_TRUE, MYPY_FALSE
)
from mypy.typestate import TypeState

Expand Down Expand Up @@ -3102,12 +3103,12 @@ def visit_op_expr(self, expr: OpExpr) -> None:

if expr.op in ('and', 'or'):
inferred = infer_condition_value(expr.left, self.options)
if ((inferred == ALWAYS_FALSE and expr.op == 'and') or
(inferred == ALWAYS_TRUE and expr.op == 'or')):
if ((inferred in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == 'and') or
(inferred in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == 'or')):
expr.right_unreachable = True
return
elif ((inferred == ALWAYS_TRUE and expr.op == 'and') or
(inferred == ALWAYS_FALSE and expr.op == 'or')):
elif ((inferred in (ALWAYS_TRUE, MYPY_TRUE) and expr.op == 'and') or
(inferred in (ALWAYS_FALSE, MYPY_FALSE) and expr.op == 'or')):
expr.right_always = True

expr.right.accept(self)
Expand Down
36 changes: 36 additions & 0 deletions test-data/unit/check-unreachable-code.test
Expand Up @@ -567,6 +567,42 @@ else:
reveal_type(y) # E: Revealed type is 'builtins.str'
[builtins fixtures/ops.pyi]

[case testShortCircuitNoEvaluation]
# flags: --platform linux --always-false COMPILE_TIME_FALSE
import sys

if sys.platform == 'darwin':
mac_only = 'junk'

# `mac_only` should not be evaluated
if sys.platform == 'darwin' and mac_only:
pass
if sys.platform == 'linux' or mac_only:
pass

COMPILE_TIME_FALSE = 'junk'

if COMPILE_TIME_FALSE:
compile_time_false_only = 'junk'

# `compile_time_false_only` should not be evaluated
if COMPILE_TIME_FALSE and compile_time_false_only:
pass
if not COMPILE_TIME_FALSE or compile_time_false_only:
pass

MYPY = False

if not MYPY:
mypy_only = 'junk'

# `mypy_only` should not be evaluated
if not MYPY and mypy_only:
pass
if MYPY or mypy_only:
pass
[builtins fixtures/ops.pyi]

[case testConditionalAssertWithoutElse]
import typing

Expand Down

0 comments on commit 268b99c

Please sign in to comment.