Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Make `NoReturn` compatible with all other types (#442)
- Fix treatment of walrus operator in `and`, `or`, and `if/else`
expressions (#441)
- Refactor `isinstance()` support (#440)
Expand Down
25 changes: 0 additions & 25 deletions pyanalyze/test_name_check_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,31 +658,6 @@ def capybara():
assert_is_value(x, KnownValue(3))


class TestNoReturn(TestNameCheckVisitorBase):
@assert_passes()
def test_no_return(self):
from typing_extensions import NoReturn
from typing import Optional

def f() -> NoReturn:
raise Exception

def capybara(x: Optional[int]) -> None:
if x is None:
f()
assert_is_value(x, TypedValue(int))

@assert_fails(ErrorCode.incompatible_argument)
def test_no_return_parameter(self):
from typing_extensions import NoReturn

def assert_unreachable(x: NoReturn) -> None:
pass

def capybara():
assert_unreachable(1)


class TestSubclassValue(TestNameCheckVisitorBase):
# In 3.7 the behavior of Type[] changed.
@only_before((3, 7))
Expand Down
37 changes: 37 additions & 0 deletions pyanalyze/test_never.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# static analysis: ignore
from .value import TypedValue
from .implementation import assert_is_value
from .test_node_visitor import assert_passes
from .test_name_check_visitor import TestNameCheckVisitorBase


class TestNoReturn(TestNameCheckVisitorBase):
@assert_passes()
def test_no_return(self):
from typing_extensions import NoReturn
from typing import Optional

def f() -> NoReturn:
raise Exception

def capybara(x: Optional[int]) -> None:
if x is None:
f()
assert_is_value(x, TypedValue(int))

@assert_passes()
def test_no_return_parameter(self):
from typing_extensions import NoReturn

def assert_unreachable(x: NoReturn) -> None:
pass

def capybara():
assert_unreachable(1) # E: incompatible_argument

@assert_passes()
def test_assignability(self):
from typing_extensions import NoReturn

def takes_never(x: NoReturn):
print(x)
3 changes: 3 additions & 0 deletions pyanalyze/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ def can_assign(self, other: "Value", ctx: "CanAssignContext") -> "CanAssign":
ctx.record_any_used()
return {}
elif isinstance(other, MultiValuedValue):
# The bottom type is assignable to every other type.
if other is NO_RETURN_VALUE:
return {}
tv_maps = []
for val in other.vals:
tv_map = self.can_assign(val, ctx)
Expand Down