[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
Tue Sep 29 22:50:16 PDT 2026
https://github.com/arichardson created https://github.com/llvm/llvm-project/pull/227570
Extend `SourceMgrDiagnosticVerifier` directives with an optional `:<col>`
suffix after the line specifier (`expected-error:10 {{...}}`,
`expected-error at +1:15 {{...}}`, `expected-error at 42:8 {{...}}`) so tests can
verify diagnostic column locations when desired while keeping column
checking opt-in.
This commit was created with the help of AI tools
>From 950d5d1ec861acfc80561a0c0bfe6fc1c60f29d1 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Tue, 29 Sep 2026 22:49:13 -0700
Subject: [PATCH] [Support][llvm-mc] Add column checking to -verify directives
Extend `SourceMgrDiagnosticVerifier` directives with an optional `:<col>`
suffix after the line specifier (`expected-error:10 {{...}}`,
`expected-error at +1:15 {{...}}`, `expected-error at 42:8 {{...}}`) so tests can
verify diagnostic column locations when desired while keeping column
checking opt-in.
This commit was created with the help of AI tools
---
llvm/docs/CommandGuide/llvm-mc.md | 25 +++++++++++-------
.../Support/SourceMgrDiagnosticVerifier.h | 22 ++++++++++------
.../Support/SourceMgrDiagnosticVerifier.cpp | 26 +++++++++++++++----
llvm/test/tools/llvm-mc/verify/basic.s | 20 ++++++++++++--
llvm/tools/llvm-mc/Verifier.cpp | 7 ++++-
mlir/lib/IR/Diagnostics.cpp | 3 ++-
6 files changed, 77 insertions(+), 26 deletions(-)
diff --git a/llvm/docs/CommandGuide/llvm-mc.md b/llvm/docs/CommandGuide/llvm-mc.md
index 7ca33b7d7ed99..ca138760ba707 100644
--- a/llvm/docs/CommandGuide/llvm-mc.md
+++ b/llvm/docs/CommandGuide/llvm-mc.md
@@ -166,24 +166,31 @@ comma-separated list of prefixes to recognize; a bare `--verify` (with no
`=<prefixes>`) is equivalent to `--verify=expected`.
A directive has the form:
-```asm
-<prefix>-<kind>[-re][@<offset>] {{<text>}}
-```
+
+``asm
+<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, and `@above`/`@below` apply to
-the nearest non-directive line above/below. 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:
+the nearest non-directive line above/below. 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 at -1 {{unknown directive}}
+## Also requires the diagnostic to be at column 1.
+.foo
+# expected-error at -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 2dc5566a48f47..4ff2e96e1090a 100644
--- a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
+++ b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
@@ -58,6 +58,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.
@@ -114,18 +117,21 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
}
/// Matches a single actual diagnostic against the expected diagnostics
- /// recorded for \p Buf / \p LineNo, computing them first via \p
+ /// recorded for \p Buf / \p LineNo / \p ColNo, computing them first via \p
/// computeExpectedDiags if they haven't been already. 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
- /// through \p Mgr. \p ReportUnexpected controls whether near misses /
- /// unexpected diagnostics are reported at all.
+ /// '@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 through \p Mgr. \p ReportUnexpected controls whether
+ /// near misses / unexpected diagnostics are reported at all.
MatchResult process(raw_ostream &OS, SourceMgr &Mgr, SourceMgr::DiagKind Kind,
bool HasLoc, const MemoryBuffer *Buf, unsigned LineNo,
- StringRef Message, bool ReportUnexpected = true);
+ unsigned ColNo, StringRef Message,
+ bool ReportUnexpected = true);
/// Reports (through \p Mgr) any expected diagnostic that was never matched
/// by a call to \p process. Returns whether verification succeeded overall,
diff --git a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
index f04602a6e7cc2..e640dea0b9723 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -28,7 +28,8 @@ SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(
}
Expected = Regex("(" + PrefixAlt +
")-(error|note|remark|warning)(-re)? "
- "*(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$");
+ "*(@([+-][0-9]+|above|below|unknown))?(:([0-9]+))? "
+ "*{{(.*)}}$");
}
/// Given a diagnostic kind, return a human readable string for it.
@@ -140,7 +141,7 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(raw_ostream &OS,
: Line.substr(CommentStart);
}
- SmallVector<StringRef, 5> Matches;
+ SmallVector<StringRef, 9> Matches;
if (!Expected.match(Line, &Matches)) {
// Check for designators that apply to this line.
if (!DesignatorsForNextLine.empty()) {
@@ -166,7 +167,7 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(raw_ostream &OS,
assert(Matches[2] == "note");
Kind = SourceMgr::DK_Note;
}
- ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[6]);
+ ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[8]);
// Check to see if this is a regex match, i.e. it includes the `-re`.
if (!Matches[3].empty() && !Record.computeRegex(OS, Mgr)) {
@@ -174,6 +175,17 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(raw_ostream &OS,
continue;
}
+ // Parse an optional ':<col>' column requirement. Applied before the
+ // offset handling below since some offset forms (e.g. '@unknown')
+ // 'continue' early, and a column requirement should still take effect
+ // for those.
+ if (!Matches[7].empty() &&
+ (Matches[7].getAsInteger(10, Record.ColNo) || Record.ColNo == 0)) {
+ OK = false;
+ Record.emitError(OS, Mgr, "invalid column '" + Matches[6] + "'");
+ continue;
+ }
+
StringRef OffsetMatch = Matches[4];
if (!OffsetMatch.empty()) {
OffsetMatch = OffsetMatch.drop_front(1);
@@ -240,8 +252,8 @@ SourceMgrDiagnosticVerifier::getExpectedDiags(StringRef BufName) {
SourceMgrDiagnosticVerifier::MatchResult SourceMgrDiagnosticVerifier::process(
raw_ostream &OS, SourceMgr &Mgr, SourceMgr::DiagKind Kind, bool HasLoc,
- const MemoryBuffer *Buf, unsigned LineNo, StringRef Message,
- bool ReportUnexpected) {
+ 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
@@ -261,6 +273,10 @@ SourceMgrDiagnosticVerifier::MatchResult SourceMgrDiagnosticVerifier::process(
// 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)) {
if (E.Kind == Kind) {
E.Matched = true;
diff --git a/llvm/test/tools/llvm-mc/verify/basic.s b/llvm/test/tools/llvm-mc/verify/basic.s
index ce4b717c91132..7b72e88b344c2 100644
--- a/llvm/test/tools/llvm-mc/verify/basic.s
+++ b/llvm/test/tools/llvm-mc/verify/basic.s
@@ -4,10 +4,16 @@
# RUN: rm -rf %t && split-file %s %t
## Matching expected-error directives: a plain substring match, a '-re'
-## regex match (note the nested {{...}} for the regex portion itself), and
-## an '@+1' forward offset all pass.
+## regex match (note the nested {{...}} for the regex portion itself), an
+## '@+1' forward offset, 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 fails verification, same as a line mismatch would.
+# RUN: not llvm-mc -triple riscv32 -verify %t/wrong-column.s 2>&1 \
+# RUN: | FileCheck %t/wrong-column.s --check-prefix=WRONG-COLUMN
+
## A custom '-verify=<prefix>' prefix is recognized instead of 'expected'.
# RUN: llvm-mc -triple riscv32 -verify=check %t/match-custom-prefix.s
@@ -66,6 +72,16 @@
# expected-error at +1 {{unknown directive}}
.baz_directive
+.qux_directive # expected-error:1 {{unknown directive}}
+
+.quux_directive
+# expected-error at -1:1 {{unknown directive}}
+
+#--- wrong-column.s
+.foo_directive # expected-error:99 {{unknown directive}}
+# WRONG-COLUMN: error: unknown directive
+# WRONG-COLUMN: expected error "unknown directive" was not produced
+
#--- match-custom-prefix.s
.foo_directive
# check-error at -1 {{unknown directive}}
diff --git a/llvm/tools/llvm-mc/Verifier.cpp b/llvm/tools/llvm-mc/Verifier.cpp
index 19de8d7f785cd..a87455f4f78b9 100644
--- a/llvm/tools/llvm-mc/Verifier.cpp
+++ b/llvm/tools/llvm-mc/Verifier.cpp
@@ -89,9 +89,14 @@ void MCVerifier::process(const SMDiagnostic &Diag) {
Buf = SrcMgr.getMemoryBuffer(ID);
}
+ // SMDiagnostic::getColumnNo() is 0-based; -verify directives are written
+ // against the printed (1-based) column, matching what a user actually sees
+ // in "file:line:col:" diagnostic output.
auto Result = Verifier.process(
errs(), SrcMgr, Diag.getKind(), HasLoc, Buf,
- HasLoc ? static_cast<unsigned>(Diag.getLineNo()) : 0, Diag.getMessage());
+ HasLoc ? static_cast<unsigned>(Diag.getLineNo()) : 0,
+ HasLoc ? static_cast<unsigned>(Diag.getColumnNo()) + 1 : 0,
+ Diag.getMessage());
if (Result == SourceMgrDiagnosticVerifier::MatchResult::Unexpected)
SrcMgr.PrintMessage(errs(), Diag);
}
diff --git a/mlir/lib/IR/Diagnostics.cpp b/mlir/lib/IR/Diagnostics.cpp
index ae678e1c44dfe..255dc187e897f 100644
--- a/mlir/lib/IR/Diagnostics.cpp
+++ b/mlir/lib/IR/Diagnostics.cpp
@@ -726,7 +726,8 @@ void SourceMgrDiagnosticVerifierHandler::process(LocationAttr loc,
using MatchResult = llvm::SourceMgrDiagnosticVerifier::MatchResult;
MatchResult result = impl->verifier.process(
os, mgr, 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);
More information about the llvm-branch-commits
mailing list