diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 872684db4df7..96f2e1e711e2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1090,6 +1090,17 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: mypy.checker.find_isinstance_check(e.left, self.chk.type_map, self.chk.typing_mode_weak()) elif e.op == 'or': + # Special case for `Optional[T] or T` -- this should infer T. + # If the type is a Union containing None, remove the None. + if isinstance(left_type, UnionType): + items = [i for i in left_type.items if not isinstance(i, NoneTyp)] + if len(items) < len(left_type.items): + left_type = UnionType.make_simplified_union(items) + + # Special case for `None or T` -- this should infer T. + if isinstance(left_type, NoneTyp): + left_type = None + _, right_map = \ mypy.checker.find_isinstance_check(e.left, self.chk.type_map, self.chk.typing_mode_weak()) @@ -1103,9 +1114,13 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: right_type = self.accept(e.right, left_type) - self.check_not_void(left_type, context) + if left_type is not None: + self.check_not_void(left_type, context) self.check_not_void(right_type, context) - return UnionType.make_simplified_union([left_type, right_type]) + if left_type is None: + return right_type + else: + return UnionType.make_simplified_union([left_type, right_type]) def check_list_multiply(self, e: OpExpr) -> Type: """Type check an expression of form '[...] * e'. diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 9e842d38aafc..d63dcebe419a 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -175,3 +175,68 @@ def f(x: None) -> str: pass def f(x: int) -> int: pass reveal_type(f(None)) # E: Revealed type is 'builtins.str' reveal_type(f(0)) # E: Revealed type is 'builtins.int' + +[case testOptionalTypeOrTypePlain] +from typing import Optional +def f(a: Optional[int]) -> int: + return a or 0 +[out] + +[case testOptionalTypeOrTypeTypeVar] +from typing import Optional, TypeVar +T = TypeVar('T') +def f(a: Optional[T], b: T) -> T: + return a or b +[out] + +[case testOptionalTypeOrTypeBothOptional] +from typing import Optional +def f(a: Optional[int], b: Optional[int]) -> None: + reveal_type(a or b) +def g(a: int, b: Optional[int]) -> None: + reveal_type(a or b) +[out] +main: note: In function "f": +main:3: error: Revealed type is 'Union[builtins.int, builtins.None]' +main: note: In function "g": +main:5: error: Revealed type is 'Union[builtins.int, builtins.None]' + +[case testOptionalTypeOrTypeComplexUnion] +from typing import Union +def f(a: Union[int, str, None]) -> None: + reveal_type(a or 'default') +[out] +main: note: In function "f": +main:3: error: Revealed type is 'Union[builtins.int, builtins.str]' + +[case testOptionalTypeOrTypeNoTriggerPlain] +from typing import Optional +def f(a: Optional[int], b: int) -> int: + return b or a +[out] +main: note: In function "f": +main:3: error: Incompatible return value type (got "Optional[int]", expected "int") + +[case testOptionalTypeOrTypeNoTriggerTypeVar] +from typing import Optional, TypeVar +T = TypeVar('T') +def f(a: Optional[T], b: T) -> T: + return b or a +[out] +main: note: In function "f": +main:4: error: Incompatible return value type (got "Optional[T]", expected "T") + +[case testNoneOrStringIsString] +def f() -> str: + a = None + b = '' + return a or b +[out] + +[case testNoneOrTypeVarIsTypeVar] +from typing import TypeVar +T = TypeVar('T') +def f(b: T) -> T: + a = None + return a or b +[out]