[llvm-branch-commits] [llvm] [mlir] [Support][llvm-mc] Add column checking to -verify directives (PR #227570)
Alexander Richardson via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Oct 18 15:09:58 PDT 2026
https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/227570
>From f5378ddb94c753cba896965cc54f33bab34194a2 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Sun, 18 Oct 2026 15:09:09 -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 f49861c88173f..9d154dd1638f1 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 6bc0335bc9812..5389ddf09701a 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
@@ -106,6 +108,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 6a11c7b95f222..75b0e27abe612 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -30,7 +30,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");
@@ -169,12 +170,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);
@@ -189,6 +197,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));
@@ -241,6 +253,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;
if (E.match(Message)) {
if (E.Kind == Kind) {
E.Matched = true;
@@ -277,7 +292,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 cd5ee445c45e1..e38d76fcc9777 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
@@ -76,11 +83,24 @@
.qux_directive # expected-error {{unknown directive}}
.quux_directive // expected-error {{unknown directive}}
+.col_directive # expected-error:1 {{unknown directive}}
sfence.vma a0, 0x10
-# expected-error at -1 {{invalid instruction, any one of the following would fix this:}}
-# expected-note at -2 {{unexpected extra operand for instruction}}
-# expected-note at -3 {{register must be a GPR}}
+# expected-error at -1:1 {{invalid instruction, any one of the following would fix this:}}
+# expected-note at -2:16 {{unexpected extra operand for instruction}}
+# expected-note at -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 at -1:0 {{never happens}}
+# expected-error at 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 6fd5837c35921..433c4d9de0291 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());
@@ -141,3 +142,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 at -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 dd4494a6d1ba6..b840edac3c8ee 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -717,7 +717,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(
More information about the llvm-branch-commits
mailing list