[llvm-branch-commits] [llvm] [Support] Add custom-prefix and comment-scoping support to SourceMgrDiagnosticVerifier (PR #227568)
Alexander Richardson via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Oct 18 12:42:19 PDT 2026
https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/227568
>From ae13303b52ae3cdbab6d1e955c0d50466d69ab78 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Sun, 18 Oct 2026 12:40:41 -0700
Subject: [PATCH] [nspr] initial commit
---
.../Support/SourceMgrDiagnosticVerifier.h | 25 ++++++--
.../Support/SourceMgrDiagnosticVerifier.cpp | 60 +++++++++++++++----
.../SourceMgrDiagnosticVerifierTest.cpp | 20 +++++++
3 files changed, 89 insertions(+), 16 deletions(-)
diff --git a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
index 0c1dc380065c1..9252b926eabb6 100644
--- a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
+++ b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
@@ -60,8 +60,19 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
/// diagnostics.
/// \param OS The stream to which verifier diagnostics (near misses,
/// unmatched expected diagnostics, malformed directives) are printed.
- explicit SourceMgrDiagnosticVerifier(const SourceMgr &Mgr,
- raw_ostream &OS = errs());
+ /// \param Prefixes The comment prefixes that introduce an expected
+ /// diagnostic, e.g. \c {"expected"} to recognize 'expected-error'. Matched
+ /// literally, not as a regex. Must be non-empty and must not contain an
+ /// empty string.
+ /// \param CommentPrefixes If non-empty, only text at or after the earliest
+ /// occurrence of one of these strings on a line is scanned for expected
+ /// diagnostics, so a magic string that happens to appear outside of a
+ /// comment (e.g. in an instruction operand) is ignored. Must not contain an
+ /// empty string. If empty, the whole line is eligible.
+ explicit SourceMgrDiagnosticVerifier(
+ const SourceMgr &Mgr, raw_ostream &OS = errs(),
+ ArrayRef<std::string> Prefixes = {"expected"},
+ ArrayRef<std::string> CommentPrefixes = {});
/// Matches a single actual diagnostic against the expected diagnostics
/// recorded for \p Buf / \p LineNo, scanning \p Buf first if it hasn't been
@@ -132,9 +143,13 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
const SourceMgr &Mgr;
raw_ostream &OS;
- /// Regex used to recognize 'expected-<kind>' comments.
- Regex Expected{"expected-(error|note|remark|warning)(-re)?"
- " *(@([+-][0-9]+|above|below|unknown))? *{{(.*)}}$"};
+ /// Regex used to recognize '<prefix>-<kind>' comments, built from the
+ /// \p Prefixes passed to the constructor.
+ Regex Expected;
+
+ /// If non-empty, only text at or after the earliest occurrence of one of
+ /// these strings on a line is eligible to match \p Expected.
+ std::vector<std::string> CommentPrefixes;
/// The expected diagnostics for each buffer that has been scanned so far.
DenseMap<const MemoryBuffer *, SmallVector<ExpectedDiag, 2>>
diff --git a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
index 71f4b0e1c1c1b..7efca8ce20d4c 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -8,15 +8,41 @@
#include "llvm/Support/SourceMgrDiagnosticVerifier.h"
+#include <algorithm>
+
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
-SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(const SourceMgr &Mgr,
- raw_ostream &OS)
- : Mgr(Mgr), OS(OS) {}
+SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(
+ const SourceMgr &Mgr, raw_ostream &OS, ArrayRef<std::string> Prefixes,
+ ArrayRef<std::string> CommentPrefixes)
+ : Mgr(Mgr), OS(OS),
+ CommentPrefixes(CommentPrefixes.begin(), CommentPrefixes.end()) {
+ assert(!Prefixes.empty() && "must have at least one prefix");
+ assert(
+ llvm::all_of(CommentPrefixes, [](StringRef P) { return !P.empty(); }) &&
+ "comment prefixes must be non-empty");
+ std::string PrefixAlt;
+ raw_string_ostream PrefixOS(PrefixAlt);
+ ListSeparator Sep("|");
+ for (const std::string &Prefix : Prefixes) {
+ assert(!Prefix.empty() && "prefixes must be non-empty");
+ PrefixOS << Sep << Regex::escape(Prefix);
+ }
+ // Require start-of-line or a non-[[:alnum:]_-] character before the prefix
+ // so that e.g. 'check' or 'expected' does not match the suffix of a longer
+ // prefix like 'uncheck-error' or 'foo-expected-error'.
+ // clang-format off
+ Expected = Regex("(^|[^[:alnum:]_-])(" + PrefixAlt +
+ ")-(error|note|remark|warning)(-re)?"
+ " *(@([+-][0-9]+|above|below|unknown))?"
+ " *{{(.*)}}$");
+ // clang-format on
+}
/// Given a diagnostic kind, return a human readable string for it.
static StringRef getDiagKindStr(SourceMgr::DiagKind Kind) {
@@ -108,12 +134,24 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const MemoryBuffer *Buf) {
// The indices of designators that apply to the next non designator line.
SmallVector<unsigned, 1> DesignatorsForNextLine;
- // Scan the file for expected-* designators.
+ // Scan the file for <prefix>-* designators.
SmallVector<StringRef, 100> Lines;
Buf->getBuffer().split(Lines, '\n');
for (unsigned LineNo = 0, E = Lines.size(); LineNo < E; ++LineNo) {
+ // If comment-scoping is enabled, only the text at or after the earliest
+ // recognized comment prefix on the line is eligible to match, so a magic
+ // string appearing outside of a comment (e.g. in an instruction operand)
+ // is ignored.
+ StringRef Line = Lines[LineNo].rtrim();
+ if (!CommentPrefixes.empty()) {
+ size_t CommentStart = StringRef::npos;
+ for (StringRef Prefix : CommentPrefixes)
+ CommentStart = std::min(CommentStart, Line.find(Prefix));
+ Line = Line.substr(CommentStart);
+ }
+
SmallVector<StringRef, 10> Matches;
- if (!Expected.match(Lines[LineNo].rtrim(), &Matches)) {
+ if (!Expected.match(Line, &Matches)) {
// Check for designators that apply to this line.
if (!DesignatorsForNextLine.empty()) {
for (unsigned DiagIndex : DesignatorsForNextLine)
@@ -124,21 +162,21 @@ SourceMgrDiagnosticVerifier::computeExpectedDiags(const MemoryBuffer *Buf) {
continue;
}
- // Point to the start of expected-*.
- SMLoc ExpectedStart = SMLoc::getFromPointer(Matches[0].data());
+ // Point to the start of <prefix>-*.
+ SMLoc ExpectedStart = SMLoc::getFromPointer(Matches[2].data());
- SourceMgr::DiagKind Kind = StringSwitch<SourceMgr::DiagKind>(Matches[1])
+ SourceMgr::DiagKind Kind = StringSwitch<SourceMgr::DiagKind>(Matches[3])
.Case("error", SourceMgr::DK_Error)
.Case("warning", SourceMgr::DK_Warning)
.Case("remark", SourceMgr::DK_Remark)
.Case("note", SourceMgr::DK_Note);
- ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[5]);
+ ExpectedDiag Record(Kind, LineNo + 1, ExpectedStart, Matches[7]);
// Check to see if this is a regex match, i.e. it includes the `-re`.
- if (!Matches[2].empty() && !computeRegex(Record))
+ if (!Matches[4].empty() && !computeRegex(Record))
continue;
- StringRef OffsetMatch = Matches[3];
+ StringRef OffsetMatch = Matches[5];
if (!OffsetMatch.empty()) {
OffsetMatch = OffsetMatch.drop_front(1);
diff --git a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
index 1e7f65e2c0b79..222ed92ce5df1 100644
--- a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
+++ b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
@@ -172,3 +172,23 @@ TEST_F(SourceMgrDiagnosticVerifierTest, AlreadyMatchedDoesNotTriggerNearMiss) {
EXPECT_EQ(StringRef(Output).find("diagnostic emitted when expecting"),
StringRef::npos);
}
+
+TEST_F(SourceMgrDiagnosticVerifierTest, CustomPrefixesAndCommentScoping) {
+ const MemoryBuffer *Buf =
+ addBuffer("op check-error {{outside comment ignored}}\n"
+ "// check-error at -1 {{first prefix}}\n"
+ "# alt-warning at -2 {{second prefix}}\n"
+ "// uncheck-error at -3 {{longer prefix ignored}}\n"
+ "// other-check-error at -4 {{hyphenated prefix ignored}}\n");
+ SourceMgrDiagnosticVerifier Verifier(SM, OS, {"check", "alt"}, {"//", "#"});
+
+ SMLoc Loc = SMLoc::getFromPointer(Buf->getBufferStart());
+ EXPECT_EQ(
+ Verifier.process(SM.GetMessage(Loc, SourceMgr::DK_Error, "first prefix")),
+ MatchResult::Matched);
+ EXPECT_EQ(Verifier.process(
+ SM.GetMessage(Loc, SourceMgr::DK_Warning, "second prefix")),
+ MatchResult::Matched);
+ EXPECT_TRUE(Verifier.verify());
+ EXPECT_EQ(Output, "");
+}
More information about the llvm-branch-commits
mailing list