[flang-commits] [flang] [flang] Reorder messages wrt line number before diff(actual, expect) (PR #186812)

Krzysztof Parzyszek via flang-commits flang-commits at lists.llvm.org
Mon Mar 16 07:48:02 PDT 2026


https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/186812

When messages are attached together, the source locations to which they refer are not necessarily monotonically increasing. For example
```
error: foo.f90:10: There is a problem here         # line 10
because: foo.f90:12: This thing is invalid         # line 12 (attached)
error: foo.f90:11: There is another problem here   # line 11
```
There is no way to represent that in the source flle via ERROR annotations, so before running unified_diff "canonicalize" the list of messages into an order that corresponds to the line numbers.

>From 466dba8948313acebe744024750ba973257ddef4 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 16 Mar 2026 09:28:02 -0500
Subject: [PATCH] [flang] Reorder messages wrt line number before diff(actual,
 expect)

When messages are attached together, the source locations to which they
refer are not necessarily monotonically increasing. For example
```
error: foo.f90:10: There is a problem here         # line 10
because: foo.f90:12: This thing is invalid         # line 12 (attached)
error: foo.f90:11: There is another problem here   # line 11
```
There is no way to represent that in the source flle via ERROR annotations,
so before running unified_diff "canonicalize" the list of messages into an
order that corresponds to the line numbers.
---
 flang/test/Semantics/test_errors.py | 55 +++++++++++++++++++++++++----
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/flang/test/Semantics/test_errors.py b/flang/test/Semantics/test_errors.py
index 45684764a00e4..95d974954b7d1 100755
--- a/flang/test/Semantics/test_errors.py
+++ b/flang/test/Semantics/test_errors.py
@@ -15,12 +15,46 @@
 
 from difflib import unified_diff
 
+# When messages are attached together, the source locations to which they
+# refer are not necessarily monotonically increasing. For example
+#   error: foo.f90:10: There is a problem here         # line 10
+#   because: foo.f90:12: This thing is invalid         # line 12 (attached)
+#   error: foo.f90:11: There is another problem here   # line 11
+# There is no way to represent that in the source flle via ERROR annotations,
+# so before running unified_diff "canonicalize" the list of messages into an
+# order that corresponds to the line numbers.
+#
+# This also eliminates the issue with multiple messages emitted for the same
+# line: they can now be "expected" in the test file in any order, e.g.
+#   !ERROR: Not enough arguments in a call to foo
+#   !ERROR: `foo` is a subroutine, not a function
+#   a = foo()
+# has the same effect as:
+#   !ERROR: `foo` is a subroutine, not a function
+#   !ERROR: Not enough arguments in a call to foo
+#   a = foo()
+
+
+def join_per_line_map(m):
+    """Take a map {"line_no:": [message1, message2, ...], ...} and convert
+    it into a newline-separated string that follows the line ordering.
+    """
+    # Sort messages for each line, and prepend the line number to each
+    # message. Use numeric values of line numbers as keys to allow them
+    # to be sorted numerically.
+    sorted_lines_map = {
+        int(k.rstrip(":")): [k + s for s in sorted(m[k])] for k in m.keys()
+    }
+
+    joined_lines_list = []
+    for line in sorted(sorted_lines_map.keys()):
+        joined_lines_list.append("\n".join(sorted_lines_map[line]))
+    return "\n".join(joined_lines_list)
+
 cm.check_args(sys.argv)
 srcdir = cm.set_source(sys.argv[1])
 with open(srcdir, "r", encoding="utf-8") as f:
     src = f.readlines()
-actual = ""
-expect = ""
 diffs = ""
 log = ""
 
@@ -48,14 +82,21 @@
             sys.exit(1)
 
 # Cleans up the output from the compilation process to be easier to process
+actual_per_line = dict()
 for line in log.split("\n"):
-    m = re.search(r"[^:]*:(\d+:).*(?:error|warning|portability|because):(.*)", line)
+    m = re.search(
+        r"[^:]*:(\d+:).*(?:error|warning|portability|because):(.*)", line
+    )
     if m:
         if re.search(r"warning: .*fold.*host", line):
             continue  # ignore host-dependent folding warnings
-        actual += m.expand(r"\1\2\n")
+        line_colon = m.expand(r"\1")
+        actual_per_line[line_colon] = actual_per_line.get(line_colon, []) + [
+            m.expand(r"\2")
+        ]
 
 # Gets the expected errors and their line numbers
+expect_per_line = dict()
 errors = []
 for i, line in enumerate(src, 1):
     m = re.search(r"(?:^\s*!\s*(?:ERROR|WARNING|PORTABILITY|BECAUSE): )(.*)", line)
@@ -63,10 +104,12 @@
         errors.append(m.group(1))
         continue
     if errors:
-        for x in errors:
-            expect += f"{i}: {x}\n"
+        expect_per_line[f"{i}:"] = [f" {x}" for x in errors]
         errors = []
 
+actual = join_per_line_map(actual_per_line)
+expect = join_per_line_map(expect_per_line)
+
 # Compares the expected errors with the compiler errors
 for line in unified_diff(actual.split("\n"), expect.split("\n"), n=0):
     line = re.sub(r"(^\-)(\d+:)", r"\nactual at \g<2>", line)



More information about the flang-commits mailing list