https://github.com/arichardson updated 
https://github.com/llvm/llvm-project/pull/227570

>From f252ad664d3587daba5197bd4953ebd68aa2885c Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Sun, 18 Oct 2026 12:40:42 -0700
Subject: [PATCH] [nspr] initial commit

---
 llvm/docs/CommandGuide/llvm-mc.md             | 13 +++++--
 .../Support/SourceMgrDiagnosticVerifier.h     | 36 +++++++++++--------
 .../Support/SourceMgrDiagnosticVerifier.cpp   | 35 +++++++++++++-----
 llvm/test/tools/llvm-mc/verify/basic.s        | 28 ++++++++++++++-
 .../SourceMgrDiagnosticVerifierTest.cpp       | 24 +++++++++++--
 mlir/lib/IR/Diagnostics.cpp                   |  3 +-
 6 files changed, 107 insertions(+), 32 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-mc.md 
b/llvm/docs/CommandGuide/llvm-mc.md
index 1ab1c1dbf1b12..037eb57393ce5 100644
--- a/llvm/docs/CommandGuide/llvm-mc.md
+++ b/llvm/docs/CommandGuide/llvm-mc.md
@@ -168,23 +168,30 @@ comma-separated list of prefixes to recognize; a bare 
`--verify` (with no
 A directive has the form:
 
 ```text
-<prefix>-<kind>[-re][@<offset>] {{<text>}}
+<prefix>-<kind>[-re][@<offset>][:<col>] {{<text>}}
 ```
 
 `<kind>` is one of `error`, `warning`, `note` or `remark`. Without
 an `@<offset>`, the directive applies to its own line; `@+N`/`@-N`
 apply to the line `N` lines below/above, `@above`/`@below` apply to
 the nearest non-directive line above/below, and `@unknown` matches
-diagnostics reported without a source location. By default `<text>` is matched 
as a substring of
+diagnostics reported without a source location. An optional `:<col>` (matching
+the column shown in the tool's own `file:line:col:` diagnostic output)
+additionally requires the diagnostic to be at that exact column; without it,
+only the line is checked. By default `<text>` is matched as a substring of
 the diagnostic's message; with the `-re` suffix, any `{{...}}` block
 nested inside `<text>` is matched as a regular expression instead (the
 rest of `<text>` is matched literally), for example:
 
 ```asm
-## Matches only diagnostics containing this exact text.
+## Matches only diagnostics containing this exact text, on this line.
 .foo
 # expected-error@-1 {{unknown directive}}
 
+## Also requires the diagnostic to be at column 1.
+.foo
+# expected-error@-1:1 {{unknown directive}}
+
 ## Matches any diagnostic starting with "unknown" and ending with
 ## "directive"; only the nested {{.*}} is a regex, the surrounding text
 ## is literal.
diff --git a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h 
b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
index 9252b926eabb6..ba89052447011 100644
--- a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
+++ b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
@@ -16,10 +16,6 @@
 #ifndef LLVM_SUPPORT_SOURCEMGRDIAGNOSTICVERIFIER_H
 #define LLVM_SUPPORT_SOURCEMGRDIAGNOSTICVERIFIER_H
 
-#include <optional>
-#include <string>
-#include <vector>
-
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
@@ -27,6 +23,9 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
+#include <optional>
+#include <string>
+#include <vector>
 
 namespace llvm {
 
@@ -75,22 +74,26 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
       ArrayRef<std::string> CommentPrefixes = {});
 
   /// Matches a single actual diagnostic against the expected diagnostics
-  /// recorded for \p Buf / \p LineNo, scanning \p Buf first if it hasn't been
-  /// scanned yet. If \p HasLoc is false, the diagnostic has no location and is
-  /// matched against the '@unknown' list instead (\p Buf / \p LineNo are
-  /// ignored). If \p HasLoc is true but \p Buf is null (e.g. the diagnostic's
-  /// file isn't a known buffer), the diagnostic is matched against an empty
-  /// list, i.e. it can never match and is always unexpected. On a near miss,
-  /// prints a message to \c OS. \p ReportUnexpected controls whether near
-  /// misses / unexpected diagnostics are reported at all.
+  /// recorded for \p Buf / \p LineNo / \p ColNo, scanning \p Buf first if it
+  /// hasn't been scanned yet. If \p HasLoc is false, the diagnostic has no
+  /// location and is matched against the '@unknown' list instead (\p Buf /
+  /// \p LineNo / \p ColNo are ignored). If \p HasLoc is true but \p Buf is
+  /// null (e.g. the diagnostic's file isn't a known buffer), the diagnostic
+  /// is matched against an empty list, i.e. it can never match and is always
+  /// unexpected. \p ColNo is only checked against expected diagnostics that
+  /// requested a column (via ':<col>'); others match on line alone regardless
+  /// of \p ColNo. On a near miss, prints a message to \c OS. \p
+  /// ReportUnexpected controls whether near misses / unexpected diagnostics
+  /// are reported at all.
   MatchResult process(SourceMgr::DiagKind Kind, bool HasLoc,
-                      const MemoryBuffer *Buf, unsigned LineNo,
+                      const MemoryBuffer *Buf, unsigned LineNo, unsigned ColNo,
                       StringRef Message, bool ReportUnexpected = true);
 
   /// Convenience overload for diagnostics already packaged as an
   /// \c SMDiagnostic (e.g. from a \c SourceMgr diagnostic handler). Resolves
-  /// the source buffer and line from \p Diag, and if the diagnostic is
-  /// unexpected and \p ReportUnexpected is true, prints \p Diag to \c OS.
+  /// the source buffer and 1-based line/column from \p Diag, and if the
+  /// diagnostic is unexpected and \p ReportUnexpected is true, prints \p Diag
+  /// to \c OS.
   MatchResult process(const SMDiagnostic &Diag, bool ReportUnexpected = true);
 
   /// Reports (to \c OS) any expected diagnostic that was never matched by a
@@ -112,6 +115,9 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
     SourceMgr::DiagKind Kind;
     /// The line number the expected diagnostic should be on.
     unsigned LineNo;
+    /// The column the expected diagnostic should be at, or 0 if the column
+    /// isn't checked (only the line is).
+    unsigned ColNo = 0;
     /// The location of the expected diagnostic within the input file.
     SMLoc FileLoc;
     /// A flag indicating if the expected diagnostic has been matched yet.
diff --git a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp 
b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
index 7efca8ce20d4c..f4bb90ddb5a5e 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -7,13 +7,11 @@
 
//===----------------------------------------------------------------------===//
 
 #include "llvm/Support/SourceMgrDiagnosticVerifier.h"
-
-#include <algorithm>
-
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 
 using namespace llvm;
 
@@ -39,7 +37,7 @@ SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(
   // clang-format off
   Expected = Regex("(^|[^[:alnum:]_-])(" + PrefixAlt +
                    ")-(error|note|remark|warning)(-re)?"
-                   " *(@([+-][0-9]+|above|below|unknown))?"
+                   " *(@([+-][0-9]+|above|below|unknown))?(:([0-9]+))?"
                    " *{{(.*)}}$");
   // clang-format on
 }
@@ -170,12 +168,19 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const 
MemoryBuffer *Buf) {
                                    .Case("warning", SourceMgr::DK_Warning)
                                    .Case("remark", SourceMgr::DK_Remark)
                                    .Case("note", SourceMgr::DK_Note);
-    ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[7]);
+    ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[9]);
 
     // Check to see if this is a regex match, i.e. it includes the `-re`.
     if (!Matches[4].empty() && !computeRegex(Record))
       continue;
 
+    // Parse an optional ':<col>' column requirement.
+    if (!Matches[8].empty() &&
+        (Matches[8].getAsInteger(10, Record.ColNo) || Record.ColNo == 0)) {
+      emitError(Record, "invalid column '" + Matches[7] + "'");
+      continue;
+    }
+
     StringRef OffsetMatch = Matches[5];
     if (!OffsetMatch.empty()) {
       OffsetMatch = OffsetMatch.drop_front(1);
@@ -207,6 +212,12 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const 
MemoryBuffer *Buf) {
           Record.LineNo -= Offset;
         }
       } else if (OffsetMatch.consume_front("unknown")) {
+        // An '@unknown' diagnostic has no source location, so it cannot
+        // also require a column.
+        if (Record.ColNo != 0) {
+          emitError(Record, "cannot specify a column with '@unknown'");
+          continue;
+        }
         // This is matching unknown locations.
         Record.FileLoc = SMLoc();
         ExpectedUnknownLocDiags.emplace_back(std::move(Record));
@@ -231,10 +242,9 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const 
MemoryBuffer *Buf) {
   return ExpectedDiags;
 }
 
-SourceMgrDiagnosticVerifier::MatchResult
-SourceMgrDiagnosticVerifier::process(SourceMgr::DiagKind Kind, bool HasLoc,
-                                     const MemoryBuffer *Buf, unsigned LineNo,
-                                     StringRef Message, bool ReportUnexpected) 
{
+SourceMgrDiagnosticVerifier::MatchResult SourceMgrDiagnosticVerifier::process(
+    SourceMgr::DiagKind Kind, bool HasLoc, const MemoryBuffer *Buf,
+    unsigned LineNo, unsigned ColNo, StringRef Message, bool ReportUnexpected) 
{
   MutableArrayRef<ExpectedDiag> Diags;
   if (HasLoc) {
     // If the buffer couldn't be resolved, `Diags` stays empty: a diagnostic
@@ -258,6 +268,10 @@ SourceMgrDiagnosticVerifier::process(SourceMgr::DiagKind 
Kind, bool HasLoc,
     // File line must match (unless it's an unknown location).
     if (HasLoc && E.LineNo != LineNo)
       continue;
+    // Column is only checked against expected diagnostics that requested one
+    // (via ':<col>'); others match on line alone.
+    if (HasLoc && E.ColNo != 0 && E.ColNo != ColNo)
+      continue;
     if (!E.match(Message))
       continue;
     if (E.Kind == Kind) {
@@ -300,9 +314,12 @@ SourceMgrDiagnosticVerifier::process(const SMDiagnostic 
&Diag,
     if (unsigned ID = Mgr.FindBufferContainingLoc(Diag.getLoc()))
       Buf = Mgr.getMemoryBuffer(ID);
   }
+  // SMDiagnostic::getColumnNo() is 0-based; -verify directives are written
+  // against the printed (1-based) column, matching "file:line:col:" output.
   MatchResult Result =
       process(Diag.getKind(), HasLoc, Buf,
               HasLoc ? static_cast<unsigned>(Diag.getLineNo()) : 0,
+              HasLoc ? static_cast<unsigned>(Diag.getColumnNo()) + 1 : 0,
               Diag.getMessage(), ReportUnexpected);
   if (Result == MatchResult::Unexpected)
     printDiag(Diag);
diff --git a/llvm/test/tools/llvm-mc/verify/basic.s 
b/llvm/test/tools/llvm-mc/verify/basic.s
index eb3e6236207ce..ea6a08717b9e4 100644
--- a/llvm/test/tools/llvm-mc/verify/basic.s
+++ b/llvm/test/tools/llvm-mc/verify/basic.s
@@ -5,9 +5,18 @@
 
 ## Matching expected-error directives: a plain substring match, a '-re'
 ## regex match (note the nested {{...}} for the regex portion itself), an
-## '@+1' forward offset, and '@above'/'@below' offsets all pass.
+## '@+1' forward offset, '@above'/'@below' offsets, a bare ':<col>' column
+## check, and a combined '@offset:col' all pass.
 # RUN: llvm-mc -triple riscv32 -verify %t/match.s
 
+## A directive with a column that doesn't match the actual diagnostic's
+## column (or with an invalid column such as ':0' or '@unknown:<col>') fails
+## verification.
+# RUN: not llvm-mc -triple riscv32 -verify %t/wrong-column.s 2>&1 \
+# RUN:   | FileCheck %t/wrong-column.s --check-prefix=WRONG-COLUMN
+# RUN: not llvm-mc -triple riscv32 -verify %t/invalid-column.s 2>&1 \
+# RUN:   | FileCheck %t/invalid-column.s --check-prefix=INVALID-COLUMN
+
 ## Malformed '-re' regex directives (missing closing '}}' or invalid regex
 ## syntax) and out-of-bounds line offsets fail verification with an error at
 ## the directive.
@@ -79,6 +88,11 @@
 # expected-error@below {{unknown directive}}
 .below_directive
 
+.qux_directive # expected-error:1 {{unknown directive}}
+
+.quux_directive
+# expected-error@-1:1 {{unknown directive}}
+
 ## Multiple expected-note directives on the same line whose substrings both
 ## match the first emitted note ("unexpected extra operand for instruction")
 ## still match both notes because unmatched directives are preferred over
@@ -88,6 +102,18 @@ sfence.vma a0, 0x10
 # expected-note@-2 {{r}}
 # expected-note@-3 {{register must be a GPR}}
 
+#--- wrong-column.s
+.foo_directive # expected-error:99 {{unknown directive}}
+# WRONG-COLUMN: error: unknown directive
+# WRONG-COLUMN: expected error "unknown directive" was not produced
+
+#--- invalid-column.s
+addi a0, a0, 1
+# expected-error@-1:0 {{never happens}}
+# expected-error@unknown:1 {{never happens}}
+# INVALID-COLUMN: error: invalid column ':0'
+# INVALID-COLUMN: error: cannot specify a column with '@unknown'
+
 #--- invalid-regex.s
 addi a0, a0, 1
 # expected-error-re@-1 {{unclosed {{regex}}
diff --git a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp 
b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
index 222ed92ce5df1..74835df8ab396 100644
--- a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
+++ b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
@@ -99,7 +99,7 @@ TEST_F(SourceMgrDiagnosticVerifierTest, 
NullBufferWithHasLocIsUnexpected) {
   SourceMgrDiagnosticVerifier Verifier(SM, OS);
 
   EXPECT_EQ(Verifier.process(SourceMgr::DK_Error, /*HasLoc=*/true,
-                             /*Buf=*/nullptr, 1, "anything"),
+                             /*Buf=*/nullptr, 1, 1, "anything"),
             MatchResult::Unexpected);
   EXPECT_FALSE(Verifier.verify());
 }
@@ -129,10 +129,11 @@ TEST_F(SourceMgrDiagnosticVerifierTest, 
LineZeroAndLeadingZeroOffsets) {
   SourceMgrDiagnosticVerifier Verifier(SM, OS);
 
   EXPECT_EQ(Verifier.process(SourceMgr::DK_Remark, /*HasLoc=*/true, Buf,
-                             /*LineNo=*/0, "implicit module at line 0"),
+                             /*LineNo=*/0, /*ColNo=*/0,
+                             "implicit module at line 0"),
             MatchResult::Matched);
   EXPECT_EQ(Verifier.process(SourceMgr::DK_Error, /*HasLoc=*/true, Buf,
-                             /*LineNo=*/3, "decimal leading zero"),
+                             /*LineNo=*/3, /*ColNo=*/1, "decimal leading 
zero"),
             MatchResult::Matched);
   EXPECT_TRUE(Verifier.verify());
   EXPECT_EQ(Output, "");
@@ -192,3 +193,20 @@ TEST_F(SourceMgrDiagnosticVerifierTest, 
CustomPrefixesAndCommentScoping) {
   EXPECT_TRUE(Verifier.verify());
   EXPECT_EQ(Output, "");
 }
+
+TEST_F(SourceMgrDiagnosticVerifierTest, ColumnChecking) {
+  const MemoryBuffer *Buf = addBuffer("bad_op arg1, arg2\n"
+                                      "# expected-error@-1:8 {{bad arg1}}\n");
+  SourceMgrDiagnosticVerifier Verifier(SM, OS);
+
+  // Column 8 ('arg1') matches; column 1 ('bad_op') does not.
+  SMLoc Col1Loc = SMLoc::getFromPointer(Buf->getBufferStart());
+  SMLoc Col8Loc = SMLoc::getFromPointer(Buf->getBufferStart() + 7);
+  EXPECT_EQ(
+      Verifier.process(SM.GetMessage(Col1Loc, SourceMgr::DK_Error, "bad 
arg1")),
+      MatchResult::Unexpected);
+  EXPECT_EQ(
+      Verifier.process(SM.GetMessage(Col8Loc, SourceMgr::DK_Error, "bad 
arg1")),
+      MatchResult::Matched);
+  EXPECT_FALSE(Verifier.verify());
+}
diff --git a/mlir/lib/IR/Diagnostics.cpp b/mlir/lib/IR/Diagnostics.cpp
index 5f1a86e1ed495..3855fc70d4db7 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -721,7 +721,8 @@ void 
SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
   using MatchResult = llvm::SourceMgrDiagnosticVerifier::MatchResult;
   MatchResult result = impl->verifier.process(
       getDiagKind(kind), /*hasLoc=*/static_cast<bool>(fileLoc), buf,
-      fileLoc ? fileLoc.getLine() : 0, msg, reportUnexpected);
+      fileLoc ? fileLoc.getLine() : 0, fileLoc ? fileLoc.getColumn() : 0, msg,
+      reportUnexpected);
   if (result == MatchResult::Unexpected)
     emitDiagnostic(loc, "unexpected " + getDiagKindStr(kind) + ": " + msg,
                    DiagnosticSeverity::Error);

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to