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 <[email protected]>
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@-1 {{first prefix}}\n"
+                "# alt-warning@-2 {{second prefix}}\n"
+                "// uncheck-error@-3 {{longer prefix ignored}}\n"
+                "// other-check-error@-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, "");
+}

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to