Indicate that a final newline was added in --diff
diff --git a/CHANGES.md b/CHANGES.md
index 5f7ca4f4da7..3fd7ad40496 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -28,6 +28,8 @@
 
 - speed up caching by avoiding pathlib (#1950)
 
+- `--diff` correctly indicates when a file doesn't end in a newline (#1662)
+
 #### _Packaging_
 
 - Self-contained native _Black_ binaries are now provided for releases via GitHub
diff --git a/docs/authors.md b/docs/authors.md
index cdf5046c446..9f2ea0571b9 100644
--- a/docs/authors.md
+++ b/docs/authors.md
@@ -132,6 +132,7 @@ Multiple contributions by:
 - [Pablo Galindo](mailto:Pablogsal@gmail.com)
 - [Paul Ganssle](mailto:p.ganssle@gmail.com)
 - [Paul Meinhardt](mailto:mnhrdt@gmail.com)
+- [Paul "TBBle" Hampson](mailto:Paul.Hampson@Pobox.com)
 - [Peter Bengtsson](mailto:mail@peterbe.com)
 - [Peter Grayson](mailto:pete@jpgrayson.net)
 - [Peter Stensmyr](mailto:peter.stensmyr@gmail.com)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 08930d11cea..7440c59e580 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -6423,14 +6423,14 @@ def assert_stable(src: str, dst: str, mode: Mode) -> None:
 
 
 @mypyc_attr(patchable=True)
-def dump_to_file(*output: str) -> str:
+def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str:
     """Dump `output` to a temporary file. Return path to the file."""
     with tempfile.NamedTemporaryFile(
         mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8"
     ) as f:
         for lines in output:
             f.write(lines)
-            if lines and lines[-1] != "\n":
+            if ensure_final_newline and lines and lines[-1] != "\n":
                 f.write("\n")
     return f.name
 
@@ -6448,11 +6448,20 @@ def diff(a: str, b: str, a_name: str, b_name: str) -> str:
     """Return a unified diff string between strings `a` and `b`."""
     import difflib
 
-    a_lines = [line + "\n" for line in a.splitlines()]
-    b_lines = [line + "\n" for line in b.splitlines()]
-    return "".join(
-        difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
-    )
+    a_lines = [line for line in a.splitlines(keepends=True)]
+    b_lines = [line for line in b.splitlines(keepends=True)]
+    diff_lines = []
+    for line in difflib.unified_diff(
+        a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5
+    ):
+        # Work around https://bugs.python.org/issue2142
+        # See https://www.gnu.org/software/diffutils/manual/html_node/Incomplete-Lines.html
+        if line[-1] == "\n":
+            diff_lines.append(line)
+        else:
+            diff_lines.append(line + "\n")
+            diff_lines.append("\\ No newline at end of file\n")
+    return "".join(diff_lines)
 
 
 def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None:
diff --git a/tests/data/missing_final_newline.diff b/tests/data/missing_final_newline.diff
new file mode 100644
index 00000000000..6d991c74f8f
--- /dev/null
+++ b/tests/data/missing_final_newline.diff
@@ -0,0 +1,8 @@
+--- [Deterministic header]
++++ [Deterministic header]
+@@ -1,3 +1,3 @@
+ # A comment-only file, with no final EOL character
+ # This triggers https://bugs.python.org/issue2142
+-# This is the line without the EOL character
+\ No newline at end of file
++# This is the line without the EOL character
diff --git a/tests/data/missing_final_newline.py b/tests/data/missing_final_newline.py
new file mode 100644
index 00000000000..687e1367552
--- /dev/null
+++ b/tests/data/missing_final_newline.py
@@ -0,0 +1,3 @@
+# A comment-only file, with no final EOL character
+# This triggers https://bugs.python.org/issue2142
+# This is the line without the EOL character
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index a2efef82422..5d14ceda8f4 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -300,7 +300,6 @@ def test_expression_diff(self) -> None:
             os.unlink(tmp_file)
         actual = result.output
         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
-        actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         if expected != actual:
             dump = black.dump_to_file(actual)
             msg = (
@@ -1798,6 +1797,28 @@ def test_newline_comment_interaction(self) -> None:
         output = black.format_str(source, mode=DEFAULT_MODE)
         black.assert_stable(source, output, mode=DEFAULT_MODE)
 
+    def test_bpo_2142_workaround(self) -> None:
+
+        # https://bugs.python.org/issue2142
+
+        source, _ = read_data("missing_final_newline.py")
+        # read_data adds a trailing newline
+        source = source.rstrip()
+        expected, _ = read_data("missing_final_newline.diff")
+        tmp_file = Path(black.dump_to_file(source, ensure_final_newline=False))
+        diff_header = re.compile(
+            rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
+            r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+        )
+        try:
+            result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
+            self.assertEqual(result.exit_code, 0)
+        finally:
+            os.unlink(tmp_file)
+        actual = result.output
+        actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+        self.assertEqual(actual, expected)
+
 
 with open(black.__file__, "r", encoding="utf-8") as _bf:
     black_source_lines = _bf.readlines()

Fixes: #1662

This works around [bpo2142](https://bugs.python.org/issue2142) from 2008.