diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 603acbe84f0f..fcbc619f5ea4 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1671,6 +1671,10 @@ def check_call( object_type, original_type=callee, ) + elif isinstance(callee, UninhabitedType): + ret = UninhabitedType() + ret.ambiguous = callee.ambiguous + return callee, ret else: return self.msg.not_callable(callee, context), AnyType(TypeOfAny.from_error) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 5b5580a648a8..a1c84f07e473 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -69,6 +69,7 @@ TypeVarLikeType, TypeVarTupleType, TypeVarType, + UninhabitedType, UnionType, get_proper_type, ) @@ -268,6 +269,10 @@ def _analyze_member_access( if not mx.suppress_errors: mx.msg.deleted_as_rvalue(typ, mx.context) return AnyType(TypeOfAny.from_error) + elif isinstance(typ, UninhabitedType): + attr_type = UninhabitedType() + attr_type.ambiguous = typ.ambiguous + return attr_type return report_missing_attribute(mx.original_type, typ, name, mx) diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 368431127b76..f425410a9774 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -1587,3 +1587,19 @@ x = 0 # not unreachable f2: Callable[[], NoReturn] = lambda: foo() x = 0 # not unreachable + +[case testAttributeNoReturn] +# flags: --warn-unreachable +from typing import Optional, NoReturn, TypeVar + +def foo() -> NoReturn: + raise + +T = TypeVar("T") +def bar(x: Optional[list[T]] = None) -> T: + ... + +reveal_type(bar().attr) # N: Revealed type is "Never" +1 # not unreachable +reveal_type(foo().attr) # N: Revealed type is "Never" +1 # E: Statement is unreachable