[llvm] [UTC] Add --dry-run/-n functionality (PR #193739)

Madhur Amilkanthwar via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 06:28:50 PDT 2026


https://github.com/madhur13490 created https://github.com/llvm/llvm-project/pull/193739

Often, developers need to see the diff before updating the tests with the scripts. This patch adds --dry-run option to UTC which one can use to see diff on stdout. The stdout can further be piped to tools like delta or diff for better viewing. 

>From 09a455a08eb2ea3bc1c54870b95721a233c541c5 Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Wed, 22 Apr 2026 22:56:01 -0700
Subject: [PATCH] [UTC] Add --dry-run/-n functionality

Often, developers needs to see the diff before updating
the tests with the scripts. This patch adds --dry-run option to UTC
which one can use to see diff on stdout. The stdout can further be piped
to tools like delta or diff for better viewing.
---
 .../update_test_checks/dry-run.test           | 36 +++++++++++++++++++
 llvm/utils/UpdateTestChecks/common.py         |  1 +
 llvm/utils/update_test_checks.py              | 27 +++++++++++++-
 3 files changed, 63 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/UpdateTestChecks/update_test_checks/dry-run.test

diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/dry-run.test b/llvm/test/tools/UpdateTestChecks/update_test_checks/dry-run.test
new file mode 100644
index 0000000000000..e2da30ef81e87
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/dry-run.test
@@ -0,0 +1,36 @@
+## Verify that `update_test_checks.py --dry-run` prints a unified diff to
+## stdout and does not modify the test file on disk.
+
+## Case 1: running --dry-run on a file that is already up-to-date with UTC
+## must produce no diff on stdout and must leave the file unchanged.
+# RUN: cp -f %S/Inputs/basic.ll.expected %t.ll
+# RUN: %update_test_checks --dry-run %t.ll > %t.diff
+# RUN: diff -u %t.ll %S/Inputs/basic.ll.expected
+# RUN: count 0 < %t.diff
+
+## Case 2: running --dry-run on a file with a stale CHECK line must emit a
+## non-empty unified diff on stdout, must leave the file unchanged on
+## disk, and must not record --dry-run / UTC_ARGS into the diff (i.e. the
+## flag must not leak into the in-file footer).
+# RUN: cp -f %S/Inputs/basic.ll.expected %t.ll
+# RUN: sed -i.bak -e 's/ret i32 0/ret i32 WRONG/' %t.ll && rm -f %t.ll.bak
+# RUN: %update_test_checks --dry-run %t.ll > %t.diff
+# RUN: FileCheck --input-file=%t.diff %s --check-prefix=DIFF
+# RUN: not grep -q UTC_ARGS %t.diff
+# RUN: FileCheck --input-file=%t.ll %s --check-prefix=STALE
+
+## Case 3: the short flag -n behaves identically to --dry-run.
+# RUN: cp -f %S/Inputs/basic.ll.expected %t.ll
+# RUN: %update_test_checks -n %t.ll > %t.diff
+# RUN: diff -u %t.ll %S/Inputs/basic.ll.expected
+# RUN: count 0 < %t.diff
+
+## The diff emitted in case 2 must show exactly the correction UTC would
+## apply: replacing the stale `ret i32 WRONG` check with `ret i32 0`.
+# DIFF: @@
+# DIFF: -; CHECK-NEXT:    ret i32 WRONG
+# DIFF: +; CHECK-NEXT:    ret i32 0
+
+## The on-disk file in case 2 must still contain the stale check; UTC must
+## not have written anything.
+# STALE: ret i32 WRONG
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index f01b6900b38b0..635b8598da3bf 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2738,6 +2738,7 @@ def get_autogennote_suffix(parser, args):
             "force_update",
             "reset_variable_names",
             "llvm_mc_binary",
+            "dry_run",
         ):
             continue
         value = getattr(args, action.dest)
diff --git a/llvm/utils/update_test_checks.py b/llvm/utils/update_test_checks.py
index 74e87787fd5b8..0462624f7adfe 100755
--- a/llvm/utils/update_test_checks.py
+++ b/llvm/utils/update_test_checks.py
@@ -37,6 +37,7 @@
 from sys import stderr
 from traceback import print_exc
 import argparse
+import difflib
 import os  # Used to advertise this file's name ("autogenerated_note").
 import re
 import sys
@@ -315,10 +316,27 @@ def update_test(ti: common.TestInfo):
         output_lines.extend(
             ti.get_checks_for_unused_prefixes(prefix_list, generated_prefixes)
         )
+    new_content = "".join("{}\n".format(l) for l in output_lines)
+
+    if ti.args.dry_run:
+        try:
+            with open(ti.path, "r", encoding="utf-8") as f:
+                old_content = f.read()
+        except FileNotFoundError:
+            old_content = ""
+        diff = difflib.unified_diff(
+            old_content.splitlines(keepends=True),
+            new_content.splitlines(keepends=True),
+            fromfile=ti.path,
+            tofile=ti.path,
+        )
+        sys.stdout.writelines(diff)
+        return
+
     common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
 
     with open(ti.path, "wb") as f:
-        f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
+        f.write(new_content.encode("utf-8"))
 
 
 def main():
@@ -376,6 +394,13 @@ def main():
         help="Reset all variable names to correspond closely to the variable names in IR. "
         "This tends to result in larger diffs.",
     )
+    parser.add_argument(
+        "-n",
+        "--dry-run",
+        action="store_true",
+        help="Do not modify the test file. Emit a unified diff of the "
+        "changes that would be written to stdout.",
+    )
     parser.add_argument("tests", nargs="+")
     initial_args = common.parse_commandline_args(parser)
 



More information about the llvm-commits mailing list