gh-90633: Improve error and docs for typing.assert_never (#91720)

Closes #90633

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 2f62193..4635da7 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -2345,11 +2345,25 @@
                case _ as unreachable:
                    assert_never(unreachable)
 
+   Here, the annotations allow the type checker to infer that the
+   last case can never execute, because ``arg`` is either
+   an :class:`int` or a :class:`str`, and both options are covered by
+   earlier cases.
    If a type checker finds that a call to ``assert_never()`` is
-   reachable, it will emit an error.
+   reachable, it will emit an error. For example, if the type annotation
+   for ``arg`` was instead ``int | str | float``, the type checker would
+   emit an error pointing out that ``unreachable`` is of type :class:`float`.
+   For a call to ``assert_never`` to succeed, the inferred type of
+   the argument passed in must be the bottom type, :data:`Never`, and nothing
+   else.
 
    At runtime, this throws an exception when called.
 
+   .. seealso::
+      `Unreachable Code and Exhaustiveness Checking
+      <https://typing.readthedocs.io/en/latest/source/unreachable.html>_` has more
+      information about exhaustiveness checking with static typing.
+
    .. versionadded:: 3.11
 
 .. function:: reveal_type(obj)
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 1fd99a0..fce340f 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -223,6 +223,19 @@ def test_exception(self):
         with self.assertRaises(AssertionError):
             assert_never(None)
 
+        value = "some value"
+        with self.assertRaisesRegex(AssertionError, value):
+            assert_never(value)
+
+        # Make sure a huge value doesn't get printed in its entirety
+        huge_value = "a" * 10000
+        with self.assertRaises(AssertionError) as cm:
+            assert_never(huge_value)
+        self.assertLess(
+            len(cm.exception.args[0]),
+            typing._ASSERT_NEVER_REPR_MAX_LENGTH * 2,
+        )
+
 
 class SelfTests(BaseTestCase):
     def test_equality(self):
diff --git a/Lib/typing.py b/Lib/typing.py
index a6f4fa9..9d8149c 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -2382,6 +2382,9 @@ class Film(TypedDict):
     return isinstance(tp, _TypedDictMeta)
 
 
+_ASSERT_NEVER_REPR_MAX_LENGTH = 100
+
+
 def assert_never(arg: Never, /) -> Never:
     """Statically assert that a line of code is unreachable.
 
@@ -2402,7 +2405,10 @@ def int_or_str(arg: int | str) -> None:
     At runtime, this throws an exception when called.
 
     """
-    raise AssertionError("Expected code to be unreachable")
+    value = repr(arg)
+    if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH:
+        value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...'
+    raise AssertionError(f"Expected code to be unreachable, but got: {value}")
 
 
 def no_type_check(arg):
diff --git a/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst b/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst
new file mode 100644
index 0000000..d86c2d3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-19-19-50-10.gh-issue-90633.Youov0.rst
@@ -0,0 +1,2 @@
+Include the passed value in the exception thrown by
+:func:`typing.assert_never`. Patch by Jelle Zijlstra.