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

>From 9835278ca15bfd9aa8ef3e4f6feb4a32b1e84826 Mon Sep 17 00:00:00 2001
From: Alex Richardson <[email protected]>
Date: Tue, 20 Oct 2026 14:56:11 -0700
Subject: [PATCH] [nspr] initial commit

---
 llvm/docs/CommandGuide/llvm-mc.md             |  7 +++--
 .../Support/SourceMgrDiagnosticVerifier.h     |  5 ++++
 .../Support/SourceMgrDiagnosticVerifier.cpp   | 24 ++++++++++++++--
 llvm/test/tools/llvm-mc/verify/basic.s        | 28 ++++++++++++++++---
 .../SourceMgrDiagnosticVerifierTest.cpp       | 20 ++++++++++++-
 mlir/lib/IR/Diagnostics.cpp                   |  2 +-
 6 files changed, 75 insertions(+), 11 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-mc.md 
b/llvm/docs/CommandGuide/llvm-mc.md
index cb626c89b6e11..1e8878ba89bb3 100644
--- a/llvm/docs/CommandGuide/llvm-mc.md
+++ b/llvm/docs/CommandGuide/llvm-mc.md
@@ -163,7 +163,7 @@ printing them, similar to Clang's `-verify`. `<prefixes>` 
is a comma-separated
 list of directive prefixes (default: `expected`). A directive has the form
 
 ```text
-<prefix>-<kind>[-re][@<offset>] {{<text>}}
+<prefix>-<kind>[-re][@<offset>][:<col>] {{<text>}}
 ```
 
 where `<kind>` is `error`, `warning`, `note` or `remark`, and `<text>` must be
@@ -171,10 +171,13 @@ a substring of the diagnostic message (with `-re`, 
`{{...}}` blocks inside
 `<text>` are regular expressions). Without `@<offset>`, the directive applies
 to its own line; `@+N` and `@-N` refer to the line `N` lines below or above,
 `@above` and `@below` to the nearest non-directive line, and `@unknown` to
-diagnostics without a location. For example:
+diagnostics without a location. `:<col>` additionally requires the diagnostic
+to be at the given 1-based column (note that in Clang, `@<file>:<line>` instead
+refers to a line in another file). For example:
 
 ```asm
 .foo # expected-error {{unknown directive}}
+.bar # expected-error:1 {{unknown directive}}
 ```
 
 Directives are only recognized in single-line comments, at most one per line.
diff --git a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h 
b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
index a9004b322a539..574e717814098 100644
--- a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
+++ b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
@@ -58,6 +58,8 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
     const MemoryBuffer *buf;
     /// The 1-based line number of the diagnostic.
     unsigned lineNo;
+    /// The 1-based column number of the diagnostic.
+    unsigned colNo;
   };
 
   /// Scans all buffers currently registered with \p mgr for expected
@@ -112,6 +114,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 any column
+    /// matches.
+    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 59faacc901444..bf551f90e13d0 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -29,7 +29,8 @@ SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(
   // Don't match 'expected' inside a longer word such as 'unexpected'.
   expected = Regex("(^|[^[:alnum:]_-])(" + prefixAlt +
                    ")-(error|note|remark|warning)(-re)?"
-                   " *(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$");
+                   " *(@([+-][0-9]+|above|below|unknown))?(:([0-9]+))?"
+                   " *{{(.*)}}$");
 
   for (StringRef prefix : commentPrefixes) {
     assert(!prefix.empty() && "comment prefixes must be non-empty");
@@ -168,12 +169,19 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const 
MemoryBuffer *buf) {
       assert(matches[3] == "note");
       kind = 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 the optional ':<col>'.
+    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);
@@ -188,6 +196,10 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const 
MemoryBuffer *buf) {
         else
           record.lineNo -= offset;
       } else if (offsetMatch.consume_front("unknown")) {
+        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));
@@ -240,6 +252,9 @@ SourceMgrDiagnosticVerifier::process(SourceMgr::DiagKind 
kind,
     // File line must match (unless it's an unknown location).
     if (loc && e.lineNo != loc->lineNo)
       continue;
+    // The column only needs to match if the directive specified one.
+    if (loc && e.colNo != 0 && e.colNo != loc->colNo)
+      continue;
     // A directive that already matched a diagnostic is used up.
     if (oneDiagPerDirective && e.matched)
       continue;
@@ -279,7 +294,10 @@ SourceMgrDiagnosticVerifier::process(const SMDiagnostic 
&diag,
     const MemoryBuffer *buf = nullptr;
     if (unsigned id = mgr.FindBufferContainingLoc(diag.getLoc()))
       buf = mgr.getMemoryBuffer(id);
-    loc = Location{buf, static_cast<unsigned>(diag.getLineNo())};
+    // SMDiagnostic columns are 0-based, but printed (and written in
+    // directives) as 1-based.
+    loc = Location{buf, static_cast<unsigned>(diag.getLineNo()),
+                   static_cast<unsigned>(diag.getColumnNo()) + 1};
   }
   MatchResult result =
       process(diag.getKind(), loc, diag.getMessage(), reportUnexpected);
diff --git a/llvm/test/tools/llvm-mc/verify/basic.s 
b/llvm/test/tools/llvm-mc/verify/basic.s
index 6f7e7db1cfe43..d79c6006f4be8 100644
--- a/llvm/test/tools/llvm-mc/verify/basic.s
+++ b/llvm/test/tools/llvm-mc/verify/basic.s
@@ -4,9 +4,16 @@
 # RUN: rm -rf %t && split-file %s %t && cd %t
 
 ## Matching directives: a substring match, a '-re' regex match, '@+N' and
-## '@-N' offsets, '@above'/'@below', and directives in '#' and '//' comments.
+## '@-N' offsets, '@above'/'@below', ':<col>' columns, and directives in '#'
+## and '//' comments.
 # RUN: llvm-mc --triple=riscv32 --verify match.s
 
+## A directive with the wrong column, or an invalid column, fails.
+# RUN: not llvm-mc --triple=riscv32 --verify wrong-column.s 2>&1 \
+# RUN:   | FileCheck wrong-column.s
+# RUN: not llvm-mc --triple=riscv32 --verify invalid-column.s 2>&1 \
+# RUN:   | FileCheck invalid-column.s
+
 ## Custom prefixes are recognized instead of 'expected', but not as the suffix
 ## of a longer word such as 'uncheck'.
 # RUN: llvm-mc --triple=riscv32 --verify=check,alt custom-prefix.s
@@ -80,6 +87,7 @@
 
 .qux_directive # expected-error {{unknown directive}}
 .quux_directive // expected-error {{unknown directive}}
+.col_directive # expected-error:1 {{unknown directive}}
 
 ## A diagnostic that is reported twice on a line needs two directives.
 .dup_directive; .dup_directive
@@ -87,9 +95,21 @@
 # expected-error@-2 {{unknown directive}}
 
 sfence.vma a0, 0x10
-# expected-error@-1 {{invalid instruction, any one of the following would fix 
this:}}
-# expected-note@-2 {{unexpected extra operand for instruction}}
-# expected-note@-3 {{register must be a GPR}}
+# expected-error@-1:1 {{invalid instruction, any one of the following would 
fix this:}}
+# expected-note@-2:16 {{unexpected extra operand for instruction}}
+# expected-note@-3:16 {{register must be a GPR}}
+
+#--- wrong-column.s
+.foo_directive # expected-error:2 {{unknown directive}}
+# CHECK: error: unknown directive
+# CHECK: error: 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}}
+# CHECK: error: invalid column ':0'
+# CHECK: error: cannot specify a column with '@unknown'
 
 #--- custom-prefix.s
 .foo_directive
diff --git a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp 
b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
index 794d2b68ce6f2..a749c337dd12e 100644
--- a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
+++ b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
@@ -96,7 +96,8 @@ TEST_F(SourceMgrDiagnosticVerifierTest, 
UnknownFileNeverMatches) {
   addBuffer("# expected-error {{anything}}\n");
   SourceMgrDiagnosticVerifier Verifier(SM, OS);
 
-  SourceMgrDiagnosticVerifier::Location Loc{/*buf=*/nullptr, /*lineNo=*/1};
+  SourceMgrDiagnosticVerifier::Location Loc{/*buf=*/nullptr, /*lineNo=*/1,
+                                            /*colNo=*/1};
   EXPECT_EQ(Verifier.process(SourceMgr::DK_Error, Loc, "anything"),
             MatchResult::Unexpected);
   EXPECT_FALSE(Verifier.verify());
@@ -170,3 +171,20 @@ TEST_F(SourceMgrDiagnosticVerifierTest, 
OneDiagPerDirective) {
   EXPECT_EQ(StringRef(Output).count("error: dup"), 1u);
   EXPECT_EQ(StringRef(Output).find("was not produced"), StringRef::npos);
 }
+
+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 6b048322cbbc5..1a282489dd44f 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -718,7 +718,7 @@ void 
SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
   std::optional<Verifier::Location> verifierLoc;
   if (FileLineColLoc fileLoc = loc.findInstanceOf<FileLineColLoc>())
     verifierLoc = Verifier::Location{getBufferForFile(fileLoc.getFilename()),
-                                     fileLoc.getLine()};
+                                     fileLoc.getLine(), fileLoc.getColumn()};
   bool reportUnexpected = impl->level != Level::OnlyExpected;
 
   Verifier::MatchResult result = impl->verifier.process(

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

Reply via email to