[llvm-branch-commits] [llvm] [Support] Fix offset parsing and buffer caching in SourceMgrDiagnosticVerifier (PR #227567)

Alexander Richardson via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 29 22:49:53 PDT 2026


https://github.com/arichardson created https://github.com/llvm/llvm-project/pull/227567

Fix three bugs inherited from the original MLIR implementation:
- Parse `@+N` / `@-N` line offsets explicitly in base 10 so leading zeros
  like `@+010` are not interpreted as octal, and reject negative offsets
  whose magnitude exceeds the current line number instead of silently
  underflowing `unsigned`.
- Make `computeExpectedDiags` idempotent when called multiple times on the
  same buffer so existing matched diagnostics are not wiped out.
- Rescan all buffers currently registered in `SourceMgr` inside `verify()`
  so buffers that never emitted a diagnostic still have their `expected-*`
  directives checked.

This commit was created with the help of AI tools

>From 765abe0d175f55ab33326c66a67fac841e0fed40 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Tue, 29 Sep 2026 22:49:12 -0700
Subject: [PATCH] [Support] Fix offset parsing and buffer caching in
 SourceMgrDiagnosticVerifier

Fix three bugs inherited from the original MLIR implementation:
- Parse `@+N` / `@-N` line offsets explicitly in base 10 so leading zeros
  like `@+010` are not interpreted as octal, and reject negative offsets
  whose magnitude exceeds the current line number instead of silently
  underflowing `unsigned`.
- Make `computeExpectedDiags` idempotent when called multiple times on the
  same buffer so existing matched diagnostics are not wiped out.
- Rescan all buffers currently registered in `SourceMgr` inside `verify()`
  so buffers that never emitted a diagnostic still have their `expected-*`
  directives checked.

This commit was created with the help of AI tools
---
 .../Support/SourceMgrDiagnosticVerifier.cpp   | 45 +++++++++++++++----
 1 file changed, 36 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
index bb36c9263c88c..b2102201b43ab 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -88,6 +88,10 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(raw_ostream &OS,
   // If the buffer is invalid, return an empty list.
   if (!Buf)
     return {};
+  // Idempotent: a buffer that's already been scanned is returned as-is,
+  // rather than being rescanned and appending duplicate entries.
+  if (auto MaybeDiags = getExpectedDiags(Buf->getBufferIdentifier()))
+    return *MaybeDiags;
   auto &ExpectedDiags = ExpectedDiagsPerFile[Buf->getBufferIdentifier()];
 
   // The number of the last line that did not correlate to a designator.
@@ -141,12 +145,30 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(raw_ostream &OS,
       // Get the integer value without the @ and +/- prefix.
       if (OffsetMatch[0] == '+' || OffsetMatch[0] == '-') {
         int Offset;
-        OffsetMatch.drop_front().getAsInteger(0, Offset);
+        // The regex only ever captures decimal digits here, so parse as
+        // decimal explicitly: a leading zero (e.g. '@+08') would otherwise
+        // be mis-parsed as octal by getAsInteger's radix auto-detection
+        // (and '08' isn't valid octal, so that already-broken corner case
+        // would silently discard the offset instead of reporting an error).
+        if (OffsetMatch.drop_front().getAsInteger(10, Offset)) {
+          OK = false;
+          Record.emitError(OS, Mgr, "invalid line offset '" + OffsetMatch + "'");
+          continue;
+        }
 
-        if (OffsetMatch.front() == '+')
+        if (OffsetMatch.front() == '+') {
           Record.LineNo += Offset;
-        else
+        } else if (static_cast<unsigned>(Offset) >= Record.LineNo) {
+          // Line numbers are 1-based, so an offset that would take LineNo to
+          // 0 or below is out of range.
+          OK = false;
+          Record.emitError(OS, Mgr,
+                           "line offset '" + OffsetMatch +
+                               "' is before the start of the file");
+          continue;
+        } else {
           Record.LineNo -= Offset;
+        }
       } else if (OffsetMatch.consume_front("unknown")) {
         // This is matching unknown locations.
         Record.FileLoc = SMLoc();
@@ -188,12 +210,8 @@ SourceMgrDiagnosticVerifier::MatchResult SourceMgrDiagnosticVerifier::process(
   if (HasLoc) {
     // If the buffer couldn't be resolved, `Diags` stays empty: a diagnostic
     // with a location in an unknown file can never match anything.
-    if (Buf) {
-      if (auto MaybeDiags = getExpectedDiags(Buf->getBufferIdentifier()))
-        Diags = *MaybeDiags;
-      else
-        Diags = computeExpectedDiags(OS, Mgr, Buf);
-    }
+    if (Buf)
+      Diags = computeExpectedDiags(OS, Mgr, Buf);
   } else {
     Diags = ExpectedUnknownLocDiags;
   }
@@ -236,6 +254,15 @@ SourceMgrDiagnosticVerifier::MatchResult SourceMgrDiagnosticVerifier::process(
 }
 
 bool SourceMgrDiagnosticVerifier::verify(raw_ostream &OS, SourceMgr &Mgr) {
+  // Scan any buffers that process() never had a reason to look at (e.g. an
+  // included file where the diagnostic its 'expected-*' comment names never
+  // actually fired): otherwise that expectation would never be recorded, and
+  // so never reported as missing below, letting verification silently
+  // pass when it shouldn't. computeExpectedDiags is idempotent, so
+  // re-scanning an already-known buffer here is harmless.
+  for (unsigned I = 0, E = Mgr.getNumBuffers(); I != E; ++I)
+    (void)computeExpectedDiags(OS, Mgr, Mgr.getMemoryBuffer(I + 1));
+
   // Verify that all expected errors were seen.
   auto CheckExpectedDiags = [&](ExpectedDiag &Diag) {
     if (!Diag.Matched) {



More information about the llvm-branch-commits mailing list