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 <[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
