[llvm-branch-commits] [llvm] [Support] Add prefix and comment options to SourceMgrDiagnosticVerifier (PR #227568)
Alexander Richardson via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Oct 18 16:09:25 PDT 2026
https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/227568
>From aecafbbdf02a88939d1eb582de62335b84edafd0 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Sun, 18 Oct 2026 16:08:41 -0700
Subject: [PATCH] [nspr] initial commit
---
.../Support/SourceMgrDiagnosticVerifier.h | 19 ++++--
.../Support/SourceMgrDiagnosticVerifier.cpp | 59 ++++++++++++++-----
.../SourceMgrDiagnosticVerifierTest.cpp | 20 +++++++
3 files changed, 80 insertions(+), 18 deletions(-)
diff --git a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
index 278c688921e2a..6bc0335bc9812 100644
--- a/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
+++ b/llvm/include/llvm/Support/SourceMgrDiagnosticVerifier.h
@@ -23,6 +23,7 @@
#include "llvm/Support/Regex.h"
#include "llvm/Support/SourceMgr.h"
#include <optional>
+#include <string>
namespace llvm {
@@ -64,7 +65,15 @@ class LLVM_ABI SourceMgrDiagnosticVerifier {
/// diagnostic is reported in them. Verifier errors (near misses, unmatched
/// expected diagnostics, malformed directives) are printed to \p OS,
/// bypassing \p Mgr's diagnostic handler.
- SourceMgrDiagnosticVerifier(const SourceMgr &Mgr, raw_ostream &OS);
+ ///
+ /// \p Prefixes are the directive prefixes to recognize (e.g. "expected"
+ /// for 'expected-error'); they must be non-empty. If \p CommentPrefixes is
+ /// non-empty, directives are only recognized after the first occurrence of
+ /// one of these strings on a line, so that text outside of comments is
+ /// ignored.
+ SourceMgrDiagnosticVerifier(const SourceMgr &Mgr, raw_ostream &OS,
+ ArrayRef<StringRef> Prefixes = {"expected"},
+ ArrayRef<StringRef> CommentPrefixes = {});
/// Matches a diagnostic against the expected diagnostics for \p Loc, or
/// against the '@unknown' expected diagnostics if \p Loc is std::nullopt.
@@ -127,9 +136,11 @@ 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.
+ Regex Expected;
+
+ /// The strings that start a comment, see the constructor.
+ SmallVector<std::string, 0> CommentPrefixes;
/// The expected diagnostics for each buffer that has been scanned so far,
/// keyed by buffer identifier (i.e. file name).
diff --git a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
index 68358400b3d2a..7a5873fe98e54 100644
--- a/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
+++ b/llvm/lib/Support/SourceMgrDiagnosticVerifier.cpp
@@ -7,14 +7,35 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/SourceMgrDiagnosticVerifier.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
using namespace llvm;
-SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(const SourceMgr &Mgr,
- raw_ostream &OS)
+SourceMgrDiagnosticVerifier::SourceMgrDiagnosticVerifier(
+ const SourceMgr &Mgr, raw_ostream &OS, ArrayRef<StringRef> Prefixes,
+ ArrayRef<StringRef> CommentPrefixes)
: Mgr(Mgr), OS(OS) {
+ assert(!Prefixes.empty() && "must have at least one prefix");
+ std::string PrefixAlt;
+ raw_string_ostream PrefixOS(PrefixAlt);
+ ListSeparator Sep("|");
+ for (StringRef Prefix : Prefixes) {
+ assert(!Prefix.empty() && "prefixes must be non-empty");
+ PrefixOS << Sep << Regex::escape(Prefix);
+ }
+ // 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))? *{{(.*)}}$");
+
+ for (StringRef Prefix : CommentPrefixes) {
+ assert(!Prefix.empty() && "comment prefixes must be non-empty");
+ this->CommentPrefixes.push_back(Prefix.str());
+ }
+
for (unsigned I = 0, E = Mgr.getNumBuffers(); I != E; ++I)
(void)computeExpectedDiags(Mgr.getMemoryBuffer(I + 1));
}
@@ -107,12 +128,22 @@ 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) {
- SmallVector<StringRef, 4> Matches;
- if (!Expected.match(Lines[LineNo].rtrim(), &Matches)) {
+ 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);
+ }
+
+ // Every directive contains '{{'; check that first to avoid running the
+ // regex on most lines.
+ SmallVector<StringRef, 8> Matches;
+ if (!Line.contains("{{") || !Expected.match(Line, &Matches)) {
// Check for designators that apply to this line.
if (!DesignatorsForNextLine.empty()) {
for (unsigned DiagIndex : DesignatorsForNextLine)
@@ -123,27 +154,27 @@ 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;
- if (Matches[1] == "error")
+ if (Matches[3] == "error")
Kind = SourceMgr::DK_Error;
- else if (Matches[1] == "warning")
+ else if (Matches[3] == "warning")
Kind = SourceMgr::DK_Warning;
- else if (Matches[1] == "remark")
+ else if (Matches[3] == "remark")
Kind = SourceMgr::DK_Remark;
else {
- assert(Matches[1] == "note");
+ assert(Matches[3] == "note");
Kind = 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 06e7f19ea2232..6fd5837c35921 100644
--- a/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
+++ b/llvm/unittests/Support/SourceMgrDiagnosticVerifierTest.cpp
@@ -121,3 +121,23 @@ TEST_F(SourceMgrDiagnosticVerifierTest, InstalledAsDiagHandler) {
StringRef::npos);
EXPECT_FALSE(Verifier.verify());
}
+
+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"
+ "// expected-error at -5 {{default prefix ignored}}\n");
+ SourceMgrDiagnosticVerifier Verifier(SM, OS, {"check", "alt"}, {"//", "#"});
+
+ EXPECT_EQ(
+ Verifier.process(getMessage(Buf, SourceMgr::DK_Error, "first prefix")),
+ MatchResult::Matched);
+ EXPECT_EQ(
+ Verifier.process(getMessage(Buf, SourceMgr::DK_Warning, "second prefix")),
+ MatchResult::Matched);
+ EXPECT_TRUE(Verifier.verify());
+ EXPECT_EQ(Output, "");
+}
More information about the llvm-branch-commits
mailing list