[llvm-branch-commits] [llvm] [mlir] [Support][llvm-mc] Support column checks in verify directives (PR #227570)
Alexander Richardson via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 20 14:56:53 PDT 2026
https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/227570
>From f0b5bb1667696fca43a3e90e5a7b29e9a305e7d6 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
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 at -2 {{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 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 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 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(
More information about the llvm-branch-commits
mailing list