Quite often when updating abstractions there will be some documentation
that needs updating, but the build process is not very developer friendly
as it just gives out something like
"rust/doctests_kernel_generated.rs:12345:42" and it is a hassle to find
where the documentation actually is.

Add a script as rustc wrapper, which translates the error message by
appending a "(generated from original_file:original_line:col)" message to
the original source info emitted by rustc.

This is the result of an example diagnostics:

    error: test
        --> rust/doctests_kernel_generated.rs:4490:1 (generated from 
rust/kernel/build_assert.rs:96:1)
         |
    4490 | compile_error!("test");
         | ^^^^^^^^^^^^^^^^^^^^^^

Signed-off-by: Gary Guo <[email protected]>
---
Changes in v2:
- Fixed SPDX header.
- Detect tty with stderr instead of stdout.
- Match doctest anchors more loosely without hardcoding i32.
- Link to v1: https://patch.msgid.link/[email protected]
---
 rust/Makefile                      |  6 ++++
 scripts/rustdoc_test_map_source.py | 73 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)

diff --git a/rust/Makefile b/rust/Makefile
index da1a7409d984..23f603d1356e 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -406,6 +406,12 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
     $(objtree)/scripts/rustdoc_test_gen FORCE
        +$(call if_changed,rustdoc_test_kernel)
 
+# When building rustdoc tests, instead of using the actual RUSTC_OR_CLIPPY, 
use a wrapper to build
+# so we can convert line number in diagnostics.
+ACTUAL_RUSTC := $(RUSTC_OR_CLIPPY)
+%/doctests_kernel_generated.o: private RUSTC_OR_CLIPPY = \
+       PYTHONDONTWRITEBYTECODE=1 ACTUAL_RUSTC=$(ACTUAL_RUSTC) $(PYTHON3) 
$(srctree)/scripts/rustdoc_test_map_source.py
+
 # We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
 # so for the moment we skip `-Cpanic=abort`.
 quiet_cmd_rustc_test = $(RUSTC_OR_CLIPPY_QUIET) T  $<
diff --git a/scripts/rustdoc_test_map_source.py 
b/scripts/rustdoc_test_map_source.py
new file mode 100644
index 000000000000..673021bedf4c
--- /dev/null
+++ b/scripts/rustdoc_test_map_source.py
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0
+
+import sys
+import re
+import os
+import subprocess
+
+LOCATION_REGEX = re.compile(r"\.location: (.*):(\d+)")
+ANCHOR_REGEX = re.compile(r"static __DOCTEST_ANCHOR: .+ = .* \+ (\d+) \+ 
(\d+);")
+SOURCE_INFO_REGEX = re.compile(r"(rust/[\w/.-]+\.rs):(\d+):(\d+)")
+
+
+def transform(file, line):
+    with open(file, "r") as f:
+        lines = f.readlines()
+
+    line_idx = line - 1
+
+    # Find the last location and anchor before the erroring line
+    real_path = None
+    orig_line = None
+    anchor_line = None
+
+    for i in range(line_idx, -1, -1):
+        if real_path is None:
+            match = LOCATION_REGEX.search(lines[i])
+            if match:
+                real_path = match.group(1)
+                orig_line = int(match.group(2))
+
+        if anchor_line is None:
+            match = ANCHOR_REGEX.search(lines[i])
+            if match:
+                anchor_line = i + int(match.group(1)) + int(match.group(2))
+
+        if real_path is not None and anchor_line is not None:
+            break
+
+    new_line = orig_line + (line_idx - anchor_line)
+    return real_path, new_line
+
+
+def main():
+    actual_rustc = os.environ.get("ACTUAL_RUSTC")
+    args = sys.argv[1:]
+
+    # We redirected Rust output so it is not TTY anymore.
+    # Add `--color=always` back to preserve the color behaviour.
+    if sys.stderr.isatty() and not any(arg.startswith("--color") for arg in 
args):
+        args.append("--color=always")
+
+    result = subprocess.run([actual_rustc] + args, stderr=subprocess.PIPE, 
text=True)
+
+    def replacer(match):
+        orig = match.group(0)
+        file = match.group(1)
+        line = int(match.group(2))
+        col = int(match.group(3))
+
+        if file != "rust/doctests_kernel_generated.rs":
+            return orig
+
+        new_file, new_line = transform(file, line)
+        return f"{orig} (generated from {new_file}:{new_line}:{col})"
+
+    if result.stderr:
+        sys.stderr.write(SOURCE_INFO_REGEX.sub(replacer, result.stderr))
+
+    sys.exit(result.returncode)
+
+
+if __name__ == "__main__":
+    main()

---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260903-rustdoc-sourcemap-160f904fe78f

Best regards,
--  
Gary Guo <[email protected]>


Reply via email to