[clang] [llvm] [clang] Add `-verify-directives` cc1 flag (PR #179835)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Sat Feb 7 07:36:53 PST 2026


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/179835

>From ca1232f1945f6a286e14233bd8dfe46fd52960e1 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 31 Oct 2025 21:29:05 +0300
Subject: [PATCH 01/30] Initial implementation of full match check

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |   2 +
 .../include/clang/Basic/DiagnosticOptions.def |   2 +
 clang/include/clang/Driver/Options.td         |   4 +
 .../clang/Frontend/VerifyDiagnosticConsumer.h |  17 ++-
 clang/lib/Frontend/CompilerInvocation.cpp     |   5 +
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 101 +++++++++++++++---
 6 files changed, 111 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 9e344160ff934..464b9de903ee4 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -188,6 +188,8 @@ def err_verify_no_directives : Error<
     "no expected directives found: consider use of '%0-no-diagnostics'">;
 def err_verify_nonconst_addrspace : Error<
   "qualifier 'const' is needed for variables in address space '%0'">;
+def err_verify_message_partial_match
+    : Error<"diagnostic messages not fully matched: %0">;
 
 def note_fixit_applied : Note<"FIX-IT applied suggested code changes">;
 def note_fixit_in_macro : Note<
diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def
index 6d0c1b14acc12..960d23b31dfb7 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.def
+++ b/clang/include/clang/Basic/DiagnosticOptions.def
@@ -76,6 +76,8 @@ ENUM_DIAGOPT(VerifyIgnoreUnexpected, DiagnosticLevelMask, 4,
              DiagnosticLevelMask::None) /// Ignore unexpected diagnostics of
                                         /// the specified levels when using
                                         /// -verify.
+DIAGOPT(VerifyDiagnosticsStrict, 1, 0) /// Enable additional checks of
+                                       /// directives to improve readability
 DIAGOPT(ElideType, 1, 0)         /// Elide identical types in template diffing
 DIAGOPT(ShowTemplateTree, 1, 0)  /// Print a template tree when diffing
 
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index cb5cb888c6da7..77cb21e3b7d8a 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8145,6 +8145,10 @@ def verify_ignore_unexpected : Flag<["-"], "verify-ignore-unexpected">,
   HelpText<"Ignore unexpected diagnostic messages">;
 def verify_ignore_unexpected_EQ : CommaJoined<["-"], "verify-ignore-unexpected=">,
   HelpText<"Ignore unexpected diagnostic messages">;
+def verify_strict
+    : Flag<["-"], "verify-strict">,
+      HelpText<
+          "Enable additional checks on directives that improve readability">;
 def Wno_rewrite_macros : Flag<["-"], "Wno-rewrite-macros">,
   HelpText<"Silence ObjC rewriting warnings">,
   MarshallingInfoFlag<DiagnosticOpts<"NoRewriteMacros">>;
diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index b4c613712ed9b..bedb6e12c59e6 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -30,6 +30,12 @@ class LangOptions;
 class SourceManager;
 class TextDiagnosticBuffer;
 
+enum class DiagnosticMatchResult {
+  NoMatch,
+  Match,
+  PartialMatch,
+};
+
 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use
 /// markers in the input source to check that all the emitted diagnostics match
 /// those expected. See clang/docs/InternalsManual.rst for details about how to
@@ -46,7 +52,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
     create(bool RegexKind, SourceLocation DirectiveLoc,
            SourceLocation DiagnosticLoc, StringRef Spelling,
            bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
-           unsigned Min, unsigned Max);
+           unsigned Min, unsigned Max, bool FullMatchRequired);
 
   public:
     /// Constant representing n or more matches.
@@ -59,6 +65,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
     unsigned Min, Max;
     bool MatchAnyLine;
     bool MatchAnyFileAndLine; // `MatchAnyFileAndLine` implies `MatchAnyLine`.
+    bool FullMatchRequired;
 
     Directive(const Directive &) = delete;
     Directive &operator=(const Directive &) = delete;
@@ -69,16 +76,18 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
     virtual bool isValid(std::string &Error) = 0;
 
     // Returns true on match.
-    virtual bool match(StringRef S) = 0;
+    virtual DiagnosticMatchResult match(StringRef S) = 0;
 
   protected:
     Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
               StringRef Spelling, bool MatchAnyFileAndLine, bool MatchAnyLine,
-              StringRef Text, unsigned Min, unsigned Max)
+              StringRef Text, unsigned Min, unsigned Max,
+              bool FullMatchRequired)
         : DirectiveLoc(DirectiveLoc), DiagnosticLoc(DiagnosticLoc),
           Spelling(Spelling), Text(Text), Min(Min), Max(Max),
           MatchAnyLine(MatchAnyLine || MatchAnyFileAndLine),
-          MatchAnyFileAndLine(MatchAnyFileAndLine) {
+          MatchAnyFileAndLine(MatchAnyFileAndLine),
+          FullMatchRequired(FullMatchRequired) {
       assert(!DirectiveLoc.isInvalid() && "DirectiveLoc is invalid!");
       assert((!DiagnosticLoc.isInvalid() || MatchAnyLine) &&
              "DiagnosticLoc is invalid!");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index bd36eb4ecf9da..7e9365573b82d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2599,6 +2599,10 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
     if (Prefix != "expected")
       GenerateArg(Consumer, OPT_verify_EQ, Prefix);
 
+  if (Opts.VerifyDiagnosticsStrict) {
+    GenerateArg(Consumer, OPT_verify_strict);
+  }
+
   DiagnosticLevelMask VIU = Opts.getVerifyIgnoreUnexpected();
   if (VIU == DiagnosticLevelMask::None) {
     // This is the default, don't generate anything.
@@ -2697,6 +2701,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
   Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor);
 
   Opts.VerifyDiagnostics = Args.hasArg(OPT_verify) || Args.hasArg(OPT_verify_EQ);
+  Opts.VerifyDiagnosticsStrict = Args.hasArg(OPT_verify_strict);
   Opts.VerifyPrefixes = Args.getAllArgValues(OPT_verify_EQ);
   if (Args.hasArg(OPT_verify))
     Opts.VerifyPrefixes.push_back("expected");
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index c6cbb36d84156..a2358762971e7 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -90,16 +90,25 @@ class StandardDirective : public Directive {
   StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
                     StringRef Spelling, bool MatchAnyFileAndLine,
                     bool MatchAnyLine, StringRef Text, unsigned Min,
-                    unsigned Max)
+                    unsigned Max, bool FullMatchRequired)
       : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
-                  MatchAnyLine, Text, Min, Max) {}
+                  MatchAnyLine, Text, Min, Max, FullMatchRequired) {}
 
   bool isValid(std::string &Error) override {
     // all strings are considered valid; even empty ones
     return true;
   }
 
-  bool match(StringRef S) override { return S.contains(Text); }
+  DiagnosticMatchResult match(StringRef S) override {
+    if (!S.contains(Text)) {
+      return DiagnosticMatchResult::NoMatch;
+    }
+    if (!FullMatchRequired) {
+      return DiagnosticMatchResult::Match;
+    }
+    return S == Text ? DiagnosticMatchResult::Match
+                     : DiagnosticMatchResult::PartialMatch;
+  }
 };
 
 /// RegexDirective - Directive with regular-expression matching.
@@ -108,17 +117,28 @@ class RegexDirective : public Directive {
   RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
                  StringRef Spelling, bool MatchAnyFileAndLine,
                  bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max,
-                 StringRef RegexStr)
+                 StringRef RegexStr, bool FullMatchRequired)
       : Directive(DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
-                  MatchAnyLine, Text, Min, Max),
+                  MatchAnyLine, Text, Min, Max, FullMatchRequired),
         Regex(RegexStr) {}
 
   bool isValid(std::string &Error) override {
     return Regex.isValid(Error);
   }
 
-  bool match(StringRef S) override {
-    return Regex.match(S);
+  DiagnosticMatchResult match(StringRef S) override {
+    if (!FullMatchRequired) {
+      return Regex.match(S) ? DiagnosticMatchResult::Match
+                            : DiagnosticMatchResult::NoMatch;
+    }
+
+    llvm::SmallVector<StringRef, 4> Matches;
+    Regex.match(S, &Matches);
+    if (Matches.empty()) {
+      return DiagnosticMatchResult::NoMatch;
+    }
+    return Matches[0].size() == S.size() ? DiagnosticMatchResult::Match
+                                         : DiagnosticMatchResult::PartialMatch;
   }
 
 private:
@@ -302,7 +322,8 @@ void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
   // Construct new directive.
   std::unique_ptr<Directive> D = Directive::create(
       UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
-      MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max);
+      MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max,
+      Diags.getDiagnosticOptions().VerifyDiagnosticsStrict);
 
   std::string Error;
   if (!D->isValid(Error)) {
@@ -940,6 +961,47 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags,
   return DL.size();
 }
 
+/// Takes a list of diagnostics that were partially matched,
+/// despite '-verify-strict' option being set.
+static unsigned PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
+                             llvm::SmallVector<Directive *> &DL) {
+  if (DL.empty())
+    return 0;
+
+  const bool IsSinglePrefix =
+      Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
+
+  SmallString<256> Fmt;
+  llvm::raw_svector_ostream OS(Fmt);
+  for (const auto *D : DL) {
+    if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
+      OS << "\n  File *";
+    else
+      OS << "\n  File " << SourceMgr.getFilename(D->DiagnosticLoc);
+    if (D->MatchAnyLine)
+      OS << " Line *";
+    else
+      OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
+    if (D->DirectiveLoc != D->DiagnosticLoc) {
+      if (SourceMgr.getFilename(D->DirectiveLoc) !=
+          SourceMgr.getFilename(D->DiagnosticLoc)) {
+        OS << " (directive at " << SourceMgr.getFilename(D->DirectiveLoc) << ':'
+           << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
+      } else {
+        OS << " (directive at line "
+           << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
+      }
+    }
+    if (!IsSinglePrefix)
+      OS << " \'" << D->Spelling << '\'';
+    OS << ": " << D->Text;
+  }
+
+  Diags.Report(diag::err_verify_message_partial_match).setForceEmit()
+      << OS.str();
+  return DL.size();
+}
+
 /// Determine whether two source locations come from the same file.
 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
                            SourceLocation DiagnosticLoc) {
@@ -966,6 +1028,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
                            bool IgnoreUnexpected) {
   std::vector<Directive *> LeftOnly;
   DiagList Right(d2_begin, d2_end);
+  llvm::SmallVector<Directive *> IncompleteMatches;
 
   for (auto &Owner : Left) {
     Directive &D = *Owner;
@@ -985,8 +1048,13 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
           continue;
 
         const std::string &RightText = II->second;
-        if (D.match(RightText))
+        DiagnosticMatchResult MatchResult = D.match(RightText);
+        if (MatchResult == DiagnosticMatchResult::Match)
           break;
+        if (MatchResult == DiagnosticMatchResult::PartialMatch) {
+          IncompleteMatches.push_back(&D);
+          break;
+        }
       }
       if (II == IE) {
         // Not found.
@@ -1002,6 +1070,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
   if (!IgnoreUnexpected)
     num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
+  num += PrintPartial(Diags, SourceMgr, IncompleteMatches);
   return num;
 }
 
@@ -1159,11 +1228,11 @@ std::unique_ptr<Directive>
 Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
                   SourceLocation DiagnosticLoc, StringRef Spelling,
                   bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text,
-                  unsigned Min, unsigned Max) {
+                  unsigned Min, unsigned Max, bool FullMatchRequired) {
   if (!RegexKind)
-    return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc,
-                                               Spelling, MatchAnyFileAndLine,
-                                               MatchAnyLine, Text, Min, Max);
+    return std::make_unique<StandardDirective>(
+        DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine,
+        MatchAnyLine, Text, Min, Max, FullMatchRequired);
 
   // Parse the directive into a regular expression.
   std::string RegexStr;
@@ -1187,7 +1256,7 @@ Directive::create(bool RegexKind, SourceLocation DirectiveLoc,
     }
   }
 
-  return std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc, Spelling,
-                                          MatchAnyFileAndLine, MatchAnyLine,
-                                          Text, Min, Max, RegexStr);
+  return std::make_unique<RegexDirective>(
+      DirectiveLoc, DiagnosticLoc, Spelling, MatchAnyFileAndLine, MatchAnyLine,
+      Text, Min, Max, RegexStr, FullMatchRequired);
 }

>From 9ed6fa55b742d20279ecc6d9b79531764a6146df Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 14:56:45 +0300
Subject: [PATCH 02/30] Initial implementation of ordering check

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |   1 +
 .../clang/Frontend/TextDiagnosticBuffer.h     |   7 +-
 .../clang/Frontend/VerifyDiagnosticConsumer.h |   3 +-
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 107 +++++++++++++++++-
 4 files changed, 114 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 464b9de903ee4..b73e5da018987 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -190,6 +190,7 @@ def err_verify_nonconst_addrspace : Error<
   "qualifier 'const' is needed for variables in address space '%0'">;
 def err_verify_message_partial_match
     : Error<"diagnostic messages not fully matched: %0">;
+def err_verify_directive_out_of_order : Error<"out of order directive found: %0\nSubsequent directives were skipped">;
 
 def note_fixit_applied : Note<"FIX-IT applied suggested code changes">;
 def note_fixit_in_macro : Note<
diff --git a/clang/include/clang/Frontend/TextDiagnosticBuffer.h b/clang/include/clang/Frontend/TextDiagnosticBuffer.h
index 5945caf89743a..3318d01c0aa6a 100644
--- a/clang/include/clang/Frontend/TextDiagnosticBuffer.h
+++ b/clang/include/clang/Frontend/TextDiagnosticBuffer.h
@@ -28,6 +28,8 @@ class TextDiagnosticBuffer : public DiagnosticConsumer {
   using iterator = DiagList::iterator;
   using const_iterator = DiagList::const_iterator;
 
+  using AllDiagList = std::vector<std::pair<DiagnosticsEngine::Level, size_t>>;
+
 private:
   DiagList Errors, Warnings, Remarks, Notes;
 
@@ -35,7 +37,7 @@ class TextDiagnosticBuffer : public DiagnosticConsumer {
   /// order likely doesn't correspond to user input order, but it at least
   /// keeps notes in the right places.  Each pair in the vector is a diagnostic
   /// level and an index into the corresponding DiagList above.
-  std::vector<std::pair<DiagnosticsEngine::Level, size_t>> All;
+  AllDiagList All;
 
 public:
   const_iterator err_begin() const { return Errors.begin(); }
@@ -50,6 +52,9 @@ class TextDiagnosticBuffer : public DiagnosticConsumer {
   const_iterator note_begin() const { return Notes.begin(); }
   const_iterator note_end() const { return Notes.end(); }
 
+  AllDiagList::const_iterator all_begin() const { return All.begin(); }
+  AllDiagList::const_iterator all_end() const { return All.end(); }
+
   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                         const Diagnostic &Info) override;
 
diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index bedb6e12c59e6..0d709d878c7ef 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -76,7 +76,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
     virtual bool isValid(std::string &Error) = 0;
 
     // Returns true on match.
-    virtual DiagnosticMatchResult match(StringRef S) = 0;
+    virtual DiagnosticMatchResult match(StringRef S) const = 0;
 
   protected:
     Directive(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc,
@@ -137,6 +137,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
   unsigned ActiveSourceFiles = 0;
   ParsingState State;
   ExpectedData ED;
+  bool CheckOrderOfDirectives;
 
   void CheckDiagnostics();
 
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index a2358762971e7..08f6713bed2e7 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -99,7 +99,7 @@ class StandardDirective : public Directive {
     return true;
   }
 
-  DiagnosticMatchResult match(StringRef S) override {
+  DiagnosticMatchResult match(StringRef S) const override {
     if (!S.contains(Text)) {
       return DiagnosticMatchResult::NoMatch;
     }
@@ -126,7 +126,7 @@ class RegexDirective : public Directive {
     return Regex.isValid(Error);
   }
 
-  DiagnosticMatchResult match(StringRef S) override {
+  DiagnosticMatchResult match(StringRef S) const override {
     if (!FullMatchRequired) {
       return Regex.match(S) ? DiagnosticMatchResult::Match
                             : DiagnosticMatchResult::NoMatch;
@@ -702,6 +702,7 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
       State{HasNoDirectives, {}} {
   if (Diags.hasSourceManager())
     setSourceManager(Diags.getSourceManager());
+  CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
 }
 
 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
@@ -1113,6 +1114,105 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   return NumProblems;
 }
 
+// Checks that directives are lexically in the same order as the emitted
+// diagnostics. Assumes that CheckResults returned 0 problems, i.e. that
+// every diagnostic was matched by every directive without considering the
+// order.
+static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
+                                       const TextDiagnosticBuffer &Buffer,
+                                       const ExpectedData &ED) {
+  // Building a set of all directives ordered by their location
+  auto directiveComparator = [] (const Directive *LHS, const Directive *RHS) {
+      return LHS->DirectiveLoc < RHS->DiagnosticLoc;
+    };
+  auto sortDirectives = [&] (const DirectiveList &Unordered) {
+    std::vector<const Directive *> Ordered(Unordered.size());
+    std::transform(Unordered.cbegin(), Unordered.cend(), Ordered.begin(), [] (const std::unique_ptr<Directive> &D) {
+      return &*D;
+    });
+    std::sort(Ordered.begin(), Ordered.end(), directiveComparator);
+    return Ordered;
+  };
+  std::vector<const Directive *> OrderedErrors = sortDirectives(ED.Errors);
+  std::vector<const Directive *> OrderedWarns = sortDirectives(ED.Warnings);
+  std::vector<const Directive *> OrderedNotes = sortDirectives(ED.Notes);
+  std::vector<const Directive *> OrderedRemarks = sortDirectives(ED.Remarks);
+
+  std::vector<const Directive *> OrderedDirectives = [&]{
+    std::vector<const Directive *> OrderedER(OrderedErrors.size() + OrderedRemarks.size());
+    std::merge(OrderedErrors.cbegin(), OrderedErrors.cend(), OrderedRemarks.cbegin(), OrderedRemarks.cend(), OrderedER.begin(), directiveComparator);
+    std::vector<const Directive *> OrderedWN(OrderedWarns.size() + OrderedNotes.size());
+    std::merge(OrderedWarns.cbegin(), OrderedWarns.cend(), OrderedNotes.cbegin(), OrderedNotes.cend(), OrderedWN.begin(), directiveComparator);
+    std::vector<const Directive *> OrderedDirectives(OrderedER.size() + OrderedWN.size());
+    std::merge(OrderedER.cbegin(), OrderedER.cend(), OrderedWN.cbegin(), OrderedWN.cend(), OrderedDirectives.begin(), directiveComparator);
+    return OrderedDirectives;
+  }();
+
+  auto getLocDiagPair = [&] (DiagnosticsEngine::Level DiagLevel, long DiagIndex) {
+    TextDiagnosticBuffer::const_iterator It = [&]{
+      switch (DiagLevel) {
+        case DiagnosticsEngine::Level::Fatal:
+        case DiagnosticsEngine::Level::Error:
+          assert(DiagIndex < Buffer.err_end() - Buffer.err_begin() && "DiagIndex is out of bounds!");
+          return Buffer.err_begin();
+        case DiagnosticsEngine::Level::Warning:
+          assert(DiagIndex < Buffer.warn_end() - Buffer.warn_begin() && "DiagIndex is out of bounds!");
+          return Buffer.warn_begin();
+        case DiagnosticsEngine::Level::Note:
+          assert(DiagIndex < Buffer.note_end() - Buffer.note_begin()  && "DiagIndex is out of bounds!");
+          return Buffer.note_begin();
+        case DiagnosticsEngine::Level::Remark:
+          assert(DiagIndex < Buffer.remark_end() - Buffer.remark_begin() && "DiagIndex is out of bounds!");
+          return Buffer.remark_begin();
+        case DiagnosticsEngine::Level::Ignored:
+          llvm_unreachable("Unexpected diagnostic level!");
+      }
+    }();
+    
+    std::advance(It, DiagIndex);
+    return *It;
+  };
+
+  using LevelDiagPairT = std::pair<DiagnosticsEngine::Level, size_t>;
+  static_assert(std::is_same_v<LevelDiagPairT, TextDiagnosticBuffer::AllDiagList::value_type>);
+
+  // CheckResults already ensured that there are as many directives as emitted
+  // diagnostics
+  for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(OrderedDirectives, llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
+    assert(!Directive->MatchAnyFileAndLine);
+    assert(!Directive->MatchAnyLine);
+    const auto [DiagLevel, DiagIndex] = LevelDiagPair;
+    const auto [DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
+    const SourceLocation DirLoc = Directive->DirectiveLoc;
+    bool LocsMatch = SourceMgr.getPresumedLineNumber(DiagLoc) == SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) && IsFromSameFile(SourceMgr, DiagLoc, Directive->DiagnosticLoc);
+    if (!LocsMatch || Directive->match(DiagText) != DiagnosticMatchResult::Match) {
+        SmallString<256> Fmt;
+        llvm::raw_svector_ostream OS(Fmt);
+        OS << "\n  directive at " << SourceMgr.getFilename(DirLoc)
+             << ":" << SourceMgr.getPresumedLineNumber(DirLoc)
+             << ": " << Directive->Text
+           << "\n  that expects diagnostic at ";
+        if (IsFromSameFile(SourceMgr, DirLoc, Directive->DiagnosticLoc)) {
+          OS << "line " << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+        }
+        else {
+          OS << SourceMgr.getFilename(Directive->DiagnosticLoc) << ":" << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+        }
+           OS << "\n  does not match diagnostic at ";
+        if (IsFromSameFile(SourceMgr, DirLoc, DiagLoc)) {
+          OS << "line " << SourceMgr.getPresumedLineNumber(DiagLoc);
+        }
+        else {
+          OS << SourceMgr.getFilename(DiagLoc) << ":" << SourceMgr.getPresumedLineNumber(DiagLoc);
+        }
+        OS << ": " << DiagText;
+        Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
+        return 1;
+    }
+  }
+  return 0;
+}
+
 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,
                                                       FileID FID,
                                                       ParsedStatus PS) {
@@ -1200,6 +1300,9 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
 
     // Check that the expected diagnostics occurred.
     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
+    if (CheckOrderOfDirectives && NumErrors == 0) {
+      NumErrors += CheckResultsAreInOrder(Diags, *SrcManager, *Buffer, ED);
+    }
   } else {
     const DiagnosticLevelMask DiagMask =
         ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected();

>From a49d77d28d2dc5a3566735acedf375a274ccb7e5 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 16:49:53 +0300
Subject: [PATCH 03/30] Stop filling IncompleteMatches if full matches are not
 required

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 08f6713bed2e7..4fdf31115a575 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1050,10 +1050,10 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
 
         const std::string &RightText = II->second;
         DiagnosticMatchResult MatchResult = D.match(RightText);
-        if (MatchResult == DiagnosticMatchResult::Match)
-          break;
-        if (MatchResult == DiagnosticMatchResult::PartialMatch) {
-          IncompleteMatches.push_back(&D);
+        if (MatchResult != DiagnosticMatchResult::NoMatch) {
+          if (D.FullMatchRequired && MatchResult == DiagnosticMatchResult::PartialMatch) {
+            IncompleteMatches.push_back(&D);
+          }
           break;
         }
       }

>From 1e3437c9d597a0b6a61caffc21d91a00c08b3c5a Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 20:44:31 +0300
Subject: [PATCH 04/30] Implement check for one diagnostic per directive

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |  2 ++
 .../clang/Frontend/VerifyDiagnosticConsumer.h |  1 +
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 35 ++++++++++++++++---
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index b73e5da018987..dda0d2a3d361d 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -191,6 +191,8 @@ def err_verify_nonconst_addrspace : Error<
 def err_verify_message_partial_match
     : Error<"diagnostic messages not fully matched: %0">;
 def err_verify_directive_out_of_order : Error<"out of order directive found: %0\nSubsequent directives were skipped">;
+def err_verify_non_singular_match
+    : Error<"exactly one diagnostic can be matched">;
 
 def note_fixit_applied : Note<"FIX-IT applied suggested code changes">;
 def note_fixit_in_macro : Note<
diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index 0d709d878c7ef..a58624890c71f 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -138,6 +138,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
   ParsingState State;
   ExpectedData ED;
   bool CheckOrderOfDirectives;
+  bool OneDiagPerDirective;
 
   void CheckDiagnostics();
 
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 4fdf31115a575..fae536eaba563 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -432,7 +432,8 @@ static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
 static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
                            Preprocessor *PP, SourceLocation Pos,
                            VerifyDiagnosticConsumer::ParsingState &State,
-                           VerifyDiagnosticConsumer::MarkerTracker &Markers) {
+                           VerifyDiagnosticConsumer::MarkerTracker &Markers,
+                           bool OneDiagPerDirective) {
   DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
 
   // First, scan the comment looking for markers.
@@ -613,12 +614,22 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
 
     // Next optional token: positive integer or a '+'.
     if (PH.Next(D.Min)) {
+      if (OneDiagPerDirective && D.Min != 1) {
+        Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                     diag::err_verify_non_singular_match);
+        continue;
+      }
       PH.Advance();
       // A positive integer can be followed by a '+' meaning min
       // or more, or by a '-' meaning a range from min to max.
       if (PH.Next("+")) {
         D.Max = Directive::MaxCount;
         PH.Advance();
+        if (OneDiagPerDirective) {
+          Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                       diag::err_verify_non_singular_match);
+          continue;
+        }
       } else if (PH.Next("-")) {
         PH.Advance();
         if (!PH.Next(D.Max) || D.Max < D.Min) {
@@ -626,12 +637,22 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
                        diag::err_verify_invalid_range) << KindStr;
           continue;
         }
+        if (OneDiagPerDirective && D.Max != 1) {
+          Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                       diag::err_verify_non_singular_match);
+          continue;
+        }
         PH.Advance();
       } else {
         D.Max = D.Min;
       }
     } else if (PH.Next("+")) {
       // '+' on its own means "1 or more".
+      if (OneDiagPerDirective && D.Max != 1) {
+        Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                     diag::err_verify_non_singular_match);
+        continue;
+      }
       D.Max = Directive::MaxCount;
       PH.Advance();
     }
@@ -703,6 +724,7 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
   if (Diags.hasSourceManager())
     setSourceManager(Diags.getSourceManager());
   CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
+  OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
 }
 
 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
@@ -818,7 +840,8 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
   // Fold any "\<EOL>" sequences
   size_t loc = C.find('\\');
   if (loc == StringRef::npos) {
-    ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers);
+    ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers,
+                   OneDiagPerDirective);
     return false;
   }
 
@@ -848,7 +871,8 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
   }
 
   if (!C2.empty())
-    ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers);
+    ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers,
+                   OneDiagPerDirective);
   return false;
 }
 
@@ -887,7 +911,7 @@ static bool findDirectives(SourceManager &SM, FileID FID,
 
     // Find first directive.
     if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
-                       Markers))
+                       Markers, /*OneDiagPerDirective=*/false))
       return true;
   }
   return false;
@@ -1198,7 +1222,7 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags, SourceManager &
         else {
           OS << SourceMgr.getFilename(Directive->DiagnosticLoc) << ":" << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
         }
-           OS << "\n  does not match diagnostic at ";
+        OS << "\n  does not match diagnostic at ";
         if (IsFromSameFile(SourceMgr, DirLoc, DiagLoc)) {
           OS << "line " << SourceMgr.getPresumedLineNumber(DiagLoc);
         }
@@ -1301,6 +1325,7 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
     // Check that the expected diagnostics occurred.
     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
     if (CheckOrderOfDirectives && NumErrors == 0) {
+      assert(OneDiagPerDirective);
       NumErrors += CheckResultsAreInOrder(Diags, *SrcManager, *Buffer, ED);
     }
   } else {

>From 482bc526ef9561f62a901e536c1179de33e5035e Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 20:47:23 +0300
Subject: [PATCH 05/30] Run clang-format

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |   4 +-
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 142 ++++++++++--------
 2 files changed, 85 insertions(+), 61 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index dda0d2a3d361d..9430f986e05c4 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -190,7 +190,9 @@ def err_verify_nonconst_addrspace : Error<
   "qualifier 'const' is needed for variables in address space '%0'">;
 def err_verify_message_partial_match
     : Error<"diagnostic messages not fully matched: %0">;
-def err_verify_directive_out_of_order : Error<"out of order directive found: %0\nSubsequent directives were skipped">;
+def err_verify_directive_out_of_order
+    : Error<"out of order directive found: %0\nSubsequent directives were "
+            "skipped">;
 def err_verify_non_singular_match
     : Error<"exactly one diagnostic can be matched">;
 
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index fae536eaba563..65cecbb163425 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1075,7 +1075,8 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
         const std::string &RightText = II->second;
         DiagnosticMatchResult MatchResult = D.match(RightText);
         if (MatchResult != DiagnosticMatchResult::NoMatch) {
-          if (D.FullMatchRequired && MatchResult == DiagnosticMatchResult::PartialMatch) {
+          if (D.FullMatchRequired &&
+              MatchResult == DiagnosticMatchResult::PartialMatch) {
             IncompleteMatches.push_back(&D);
           }
           break;
@@ -1142,18 +1143,18 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
 // diagnostics. Assumes that CheckResults returned 0 problems, i.e. that
 // every diagnostic was matched by every directive without considering the
 // order.
-static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
+static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
+                                       SourceManager &SourceMgr,
                                        const TextDiagnosticBuffer &Buffer,
                                        const ExpectedData &ED) {
   // Building a set of all directives ordered by their location
-  auto directiveComparator = [] (const Directive *LHS, const Directive *RHS) {
-      return LHS->DirectiveLoc < RHS->DiagnosticLoc;
-    };
-  auto sortDirectives = [&] (const DirectiveList &Unordered) {
+  auto directiveComparator = [](const Directive *LHS, const Directive *RHS) {
+    return LHS->DirectiveLoc < RHS->DiagnosticLoc;
+  };
+  auto sortDirectives = [&](const DirectiveList &Unordered) {
     std::vector<const Directive *> Ordered(Unordered.size());
-    std::transform(Unordered.cbegin(), Unordered.cend(), Ordered.begin(), [] (const std::unique_ptr<Directive> &D) {
-      return &*D;
-    });
+    std::transform(Unordered.cbegin(), Unordered.cend(), Ordered.begin(),
+                   [](const std::unique_ptr<Directive> &D) { return &*D; });
     std::sort(Ordered.begin(), Ordered.end(), directiveComparator);
     return Ordered;
   };
@@ -1162,76 +1163,97 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags, SourceManager &
   std::vector<const Directive *> OrderedNotes = sortDirectives(ED.Notes);
   std::vector<const Directive *> OrderedRemarks = sortDirectives(ED.Remarks);
 
-  std::vector<const Directive *> OrderedDirectives = [&]{
-    std::vector<const Directive *> OrderedER(OrderedErrors.size() + OrderedRemarks.size());
-    std::merge(OrderedErrors.cbegin(), OrderedErrors.cend(), OrderedRemarks.cbegin(), OrderedRemarks.cend(), OrderedER.begin(), directiveComparator);
-    std::vector<const Directive *> OrderedWN(OrderedWarns.size() + OrderedNotes.size());
-    std::merge(OrderedWarns.cbegin(), OrderedWarns.cend(), OrderedNotes.cbegin(), OrderedNotes.cend(), OrderedWN.begin(), directiveComparator);
-    std::vector<const Directive *> OrderedDirectives(OrderedER.size() + OrderedWN.size());
-    std::merge(OrderedER.cbegin(), OrderedER.cend(), OrderedWN.cbegin(), OrderedWN.cend(), OrderedDirectives.begin(), directiveComparator);
+  std::vector<const Directive *> OrderedDirectives = [&] {
+    std::vector<const Directive *> OrderedER(OrderedErrors.size() +
+                                             OrderedRemarks.size());
+    std::merge(OrderedErrors.cbegin(), OrderedErrors.cend(),
+               OrderedRemarks.cbegin(), OrderedRemarks.cend(),
+               OrderedER.begin(), directiveComparator);
+    std::vector<const Directive *> OrderedWN(OrderedWarns.size() +
+                                             OrderedNotes.size());
+    std::merge(OrderedWarns.cbegin(), OrderedWarns.cend(),
+               OrderedNotes.cbegin(), OrderedNotes.cend(), OrderedWN.begin(),
+               directiveComparator);
+    std::vector<const Directive *> OrderedDirectives(OrderedER.size() +
+                                                     OrderedWN.size());
+    std::merge(OrderedER.cbegin(), OrderedER.cend(), OrderedWN.cbegin(),
+               OrderedWN.cend(), OrderedDirectives.begin(),
+               directiveComparator);
     return OrderedDirectives;
   }();
 
-  auto getLocDiagPair = [&] (DiagnosticsEngine::Level DiagLevel, long DiagIndex) {
-    TextDiagnosticBuffer::const_iterator It = [&]{
+  auto getLocDiagPair = [&](DiagnosticsEngine::Level DiagLevel,
+                            long DiagIndex) {
+    TextDiagnosticBuffer::const_iterator It = [&] {
       switch (DiagLevel) {
-        case DiagnosticsEngine::Level::Fatal:
-        case DiagnosticsEngine::Level::Error:
-          assert(DiagIndex < Buffer.err_end() - Buffer.err_begin() && "DiagIndex is out of bounds!");
-          return Buffer.err_begin();
-        case DiagnosticsEngine::Level::Warning:
-          assert(DiagIndex < Buffer.warn_end() - Buffer.warn_begin() && "DiagIndex is out of bounds!");
-          return Buffer.warn_begin();
-        case DiagnosticsEngine::Level::Note:
-          assert(DiagIndex < Buffer.note_end() - Buffer.note_begin()  && "DiagIndex is out of bounds!");
-          return Buffer.note_begin();
-        case DiagnosticsEngine::Level::Remark:
-          assert(DiagIndex < Buffer.remark_end() - Buffer.remark_begin() && "DiagIndex is out of bounds!");
-          return Buffer.remark_begin();
-        case DiagnosticsEngine::Level::Ignored:
-          llvm_unreachable("Unexpected diagnostic level!");
+      case DiagnosticsEngine::Level::Fatal:
+      case DiagnosticsEngine::Level::Error:
+        assert(DiagIndex < Buffer.err_end() - Buffer.err_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.err_begin();
+      case DiagnosticsEngine::Level::Warning:
+        assert(DiagIndex < Buffer.warn_end() - Buffer.warn_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.warn_begin();
+      case DiagnosticsEngine::Level::Note:
+        assert(DiagIndex < Buffer.note_end() - Buffer.note_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.note_begin();
+      case DiagnosticsEngine::Level::Remark:
+        assert(DiagIndex < Buffer.remark_end() - Buffer.remark_begin() &&
+               "DiagIndex is out of bounds!");
+        return Buffer.remark_begin();
+      case DiagnosticsEngine::Level::Ignored:
+        llvm_unreachable("Unexpected diagnostic level!");
       }
     }();
-    
+
     std::advance(It, DiagIndex);
     return *It;
   };
 
   using LevelDiagPairT = std::pair<DiagnosticsEngine::Level, size_t>;
-  static_assert(std::is_same_v<LevelDiagPairT, TextDiagnosticBuffer::AllDiagList::value_type>);
+  static_assert(std::is_same_v<LevelDiagPairT,
+                               TextDiagnosticBuffer::AllDiagList::value_type>);
 
   // CheckResults already ensured that there are as many directives as emitted
   // diagnostics
-  for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(OrderedDirectives, llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
+  for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
+           OrderedDirectives,
+           llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
     assert(!Directive->MatchAnyFileAndLine);
     assert(!Directive->MatchAnyLine);
     const auto [DiagLevel, DiagIndex] = LevelDiagPair;
     const auto [DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
     const SourceLocation DirLoc = Directive->DirectiveLoc;
-    bool LocsMatch = SourceMgr.getPresumedLineNumber(DiagLoc) == SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) && IsFromSameFile(SourceMgr, DiagLoc, Directive->DiagnosticLoc);
-    if (!LocsMatch || Directive->match(DiagText) != DiagnosticMatchResult::Match) {
-        SmallString<256> Fmt;
-        llvm::raw_svector_ostream OS(Fmt);
-        OS << "\n  directive at " << SourceMgr.getFilename(DirLoc)
-             << ":" << SourceMgr.getPresumedLineNumber(DirLoc)
-             << ": " << Directive->Text
-           << "\n  that expects diagnostic at ";
-        if (IsFromSameFile(SourceMgr, DirLoc, Directive->DiagnosticLoc)) {
-          OS << "line " << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
-        }
-        else {
-          OS << SourceMgr.getFilename(Directive->DiagnosticLoc) << ":" << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
-        }
-        OS << "\n  does not match diagnostic at ";
-        if (IsFromSameFile(SourceMgr, DirLoc, DiagLoc)) {
-          OS << "line " << SourceMgr.getPresumedLineNumber(DiagLoc);
-        }
-        else {
-          OS << SourceMgr.getFilename(DiagLoc) << ":" << SourceMgr.getPresumedLineNumber(DiagLoc);
-        }
-        OS << ": " << DiagText;
-        Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
-        return 1;
+    bool LocsMatch =
+        SourceMgr.getPresumedLineNumber(DiagLoc) ==
+            SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
+        IsFromSameFile(SourceMgr, DiagLoc, Directive->DiagnosticLoc);
+    if (!LocsMatch ||
+        Directive->match(DiagText) != DiagnosticMatchResult::Match) {
+      SmallString<256> Fmt;
+      llvm::raw_svector_ostream OS(Fmt);
+      OS << "\n  directive at " << SourceMgr.getFilename(DirLoc) << ":"
+         << SourceMgr.getPresumedLineNumber(DirLoc) << ": " << Directive->Text
+         << "\n  that expects diagnostic at ";
+      if (IsFromSameFile(SourceMgr, DirLoc, Directive->DiagnosticLoc)) {
+        OS << "line "
+           << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+      } else {
+        OS << SourceMgr.getFilename(Directive->DiagnosticLoc) << ":"
+           << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+      }
+      OS << "\n  does not match diagnostic at ";
+      if (IsFromSameFile(SourceMgr, DirLoc, DiagLoc)) {
+        OS << "line " << SourceMgr.getPresumedLineNumber(DiagLoc);
+      } else {
+        OS << SourceMgr.getFilename(DiagLoc) << ":"
+           << SourceMgr.getPresumedLineNumber(DiagLoc);
+      }
+      OS << ": " << DiagText;
+      Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
+      return 1;
     }
   }
   return 0;

>From 3bf79bd63345d1b81af233a29023f698acab8e01 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 20:49:18 +0300
Subject: [PATCH 06/30] Add message to an assertion

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 65cecbb163425..d2b3963984d55 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1347,7 +1347,8 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
     // Check that the expected diagnostics occurred.
     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
     if (CheckOrderOfDirectives && NumErrors == 0) {
-      assert(OneDiagPerDirective);
+      assert(OneDiagPerDirective && "Can't check order of directives unless "
+                                    "they match only one diagnostic!");
       NumErrors += CheckResultsAreInOrder(Diags, *SrcManager, *Buffer, ED);
     }
   } else {

>From c362e08da0607438f4730e178a31da2c3b61c50b Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 1 Nov 2025 21:13:20 +0300
Subject: [PATCH 07/30] Add check for wildcard in diagnostic location

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |  2 ++
 .../clang/Frontend/VerifyDiagnosticConsumer.h |  1 +
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 27 ++++++++++++++++---
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 9430f986e05c4..7f3ebc266a77a 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -195,6 +195,8 @@ def err_verify_directive_out_of_order
             "skipped">;
 def err_verify_non_singular_match
     : Error<"exactly one diagnostic can be matched">;
+def err_verify_wildcard_loc
+    : Error<"cannot use wildcard for diagnostic location">;
 
 def note_fixit_applied : Note<"FIX-IT applied suggested code changes">;
 def note_fixit_in_macro : Note<
diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index a58624890c71f..ef1839f25cafd 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -139,6 +139,7 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
   ExpectedData ED;
   bool CheckOrderOfDirectives;
   bool OneDiagPerDirective;
+  bool DisableWildcardInDiagLoc;
 
   void CheckDiagnostics();
 
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index d2b3963984d55..53dff3fe700fc 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -433,7 +433,8 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
                            Preprocessor *PP, SourceLocation Pos,
                            VerifyDiagnosticConsumer::ParsingState &State,
                            VerifyDiagnosticConsumer::MarkerTracker &Markers,
-                           bool OneDiagPerDirective) {
+                           bool OneDiagPerDirective,
+                           bool DisableWildcardInDiagLoc) {
   DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();
 
   // First, scan the comment looking for markers.
@@ -564,6 +565,11 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
         PH.Advance();
 
         if (Filename == "*") {
+          if (DisableWildcardInDiagLoc) {
+            Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                         diag::err_verify_wildcard_loc);
+            continue;
+          }
           MatchAnyFileAndLine = true;
           if (!PH.Next("*")) {
             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
@@ -592,11 +598,21 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
           if (PH.Next(Line) && Line > 0)
             ExpectedLoc = SM.translateLineCol(FID, Line, 1);
           else if (PH.Next("*")) {
+            if (DisableWildcardInDiagLoc) {
+              Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                           diag::err_verify_wildcard_loc);
+              continue;
+            }
             MatchAnyLine = true;
             ExpectedLoc = SM.translateLineCol(FID, 1, 1);
           }
         }
       } else if (PH.Next("*")) {
+        if (DisableWildcardInDiagLoc) {
+          Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
+                       diag::err_verify_wildcard_loc);
+          continue;
+        }
         MatchAnyLine = true;
         ExpectedLoc = SourceLocation();
       }
@@ -725,6 +741,8 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
     setSourceManager(Diags.getSourceManager());
   CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
   OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
+  DisableWildcardInDiagLoc =
+      Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
 }
 
 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
@@ -841,7 +859,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
   size_t loc = C.find('\\');
   if (loc == StringRef::npos) {
     ParseDirective(C, &ED, SM, &PP, CommentBegin, State, *Markers,
-                   OneDiagPerDirective);
+                   OneDiagPerDirective, DisableWildcardInDiagLoc);
     return false;
   }
 
@@ -872,7 +890,7 @@ bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP,
 
   if (!C2.empty())
     ParseDirective(C2, &ED, SM, &PP, CommentBegin, State, *Markers,
-                   OneDiagPerDirective);
+                   OneDiagPerDirective, DisableWildcardInDiagLoc);
   return false;
 }
 
@@ -911,7 +929,8 @@ static bool findDirectives(SourceManager &SM, FileID FID,
 
     // Find first directive.
     if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), State,
-                       Markers, /*OneDiagPerDirective=*/false))
+                       Markers, /*OneDiagPerDirective=*/false,
+                       /*DisableWildcardInDiagLoc=*/false))
       return true;
   }
   return false;

>From a8b076086afbf3940da0f78cbfabcd98f4184d13 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 2 Nov 2025 18:42:46 +0300
Subject: [PATCH 08/30] Print actual diagnostic message when printing partial
 match

---
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 76 ++++++++-----------
 1 file changed, 33 insertions(+), 43 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 53dff3fe700fc..270cf11feaaa8 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1005,47 +1005,6 @@ static unsigned PrintExpected(DiagnosticsEngine &Diags,
   return DL.size();
 }
 
-/// Takes a list of diagnostics that were partially matched,
-/// despite '-verify-strict' option being set.
-static unsigned PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
-                             llvm::SmallVector<Directive *> &DL) {
-  if (DL.empty())
-    return 0;
-
-  const bool IsSinglePrefix =
-      Diags.getDiagnosticOptions().VerifyPrefixes.size() == 1;
-
-  SmallString<256> Fmt;
-  llvm::raw_svector_ostream OS(Fmt);
-  for (const auto *D : DL) {
-    if (D->DiagnosticLoc.isInvalid() || D->MatchAnyFileAndLine)
-      OS << "\n  File *";
-    else
-      OS << "\n  File " << SourceMgr.getFilename(D->DiagnosticLoc);
-    if (D->MatchAnyLine)
-      OS << " Line *";
-    else
-      OS << " Line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
-    if (D->DirectiveLoc != D->DiagnosticLoc) {
-      if (SourceMgr.getFilename(D->DirectiveLoc) !=
-          SourceMgr.getFilename(D->DiagnosticLoc)) {
-        OS << " (directive at " << SourceMgr.getFilename(D->DirectiveLoc) << ':'
-           << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
-      } else {
-        OS << " (directive at line "
-           << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << ')';
-      }
-    }
-    if (!IsSinglePrefix)
-      OS << " \'" << D->Spelling << '\'';
-    OS << ": " << D->Text;
-  }
-
-  Diags.Report(diag::err_verify_message_partial_match).setForceEmit()
-      << OS.str();
-  return DL.size();
-}
-
 /// Determine whether two source locations come from the same file.
 static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
                            SourceLocation DiagnosticLoc) {
@@ -1062,6 +1021,37 @@ static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
   return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
 }
 
+/// Takes a list of diagnostics that were partially matched,
+/// despite '-verify-strict' option being set.
+static unsigned
+PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
+             llvm::SmallVector<std::pair<Directive *, std::string>> &DL) {
+  if (DL.empty())
+    return 0;
+
+  SmallString<256> Fmt;
+  llvm::raw_svector_ostream OS(Fmt);
+  for (const auto &[D, DiagText] : DL) {
+    assert(!D->MatchAnyLine && !D->MatchAnyFileAndLine &&
+           "Wildcards should not be allowed in strict verify mode");
+    OS << "\n  Directive at " << SourceMgr.getFilename(D->DirectiveLoc) << ":"
+       << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << " \'"
+       << D->Spelling << "\': " << D->Text
+       << "\n    does not fully match diagnostic at ";
+    if (IsFromSameFile(SourceMgr, D->DirectiveLoc, D->DiagnosticLoc)) {
+      OS << "line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
+    } else {
+      OS << SourceMgr.getFilename(D->DiagnosticLoc) << ":"
+         << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
+    }
+    OS << ": " << DiagText;
+  }
+
+  Diags.Report(diag::err_verify_message_partial_match).setForceEmit()
+      << OS.str();
+  return DL.size();
+}
+
 /// CheckLists - Compare expected to seen diagnostic lists and return the
 /// the difference between them.
 static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
@@ -1072,7 +1062,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
                            bool IgnoreUnexpected) {
   std::vector<Directive *> LeftOnly;
   DiagList Right(d2_begin, d2_end);
-  llvm::SmallVector<Directive *> IncompleteMatches;
+  llvm::SmallVector<std::pair<Directive *, std::string>> IncompleteMatches;
 
   for (auto &Owner : Left) {
     Directive &D = *Owner;
@@ -1096,7 +1086,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
         if (MatchResult != DiagnosticMatchResult::NoMatch) {
           if (D.FullMatchRequired &&
               MatchResult == DiagnosticMatchResult::PartialMatch) {
-            IncompleteMatches.push_back(&D);
+            IncompleteMatches.push_back({&D, II->second});
           }
           break;
         }

>From cdb615096a175fbaf874b6976766e26854ed3706 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 2 Nov 2025 18:59:06 +0300
Subject: [PATCH 09/30] Trim diagnostic text before checking for full match

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 270cf11feaaa8..db73000af1e83 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -106,8 +106,8 @@ class StandardDirective : public Directive {
     if (!FullMatchRequired) {
       return DiagnosticMatchResult::Match;
     }
-    return S == Text ? DiagnosticMatchResult::Match
-                     : DiagnosticMatchResult::PartialMatch;
+    return S.trim() == Text ? DiagnosticMatchResult::Match
+                            : DiagnosticMatchResult::PartialMatch;
   }
 };
 
@@ -133,7 +133,7 @@ class RegexDirective : public Directive {
     }
 
     llvm::SmallVector<StringRef, 4> Matches;
-    Regex.match(S, &Matches);
+    Regex.match(S.trim(), &Matches);
     if (Matches.empty()) {
       return DiagnosticMatchResult::NoMatch;
     }

>From 2ad4b5cab3cd073a47dc2e4b61a895764e83f66b Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Mon, 3 Nov 2025 21:15:22 +0300
Subject: [PATCH 10/30] Fix couple of bugs in directive order check

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index db73000af1e83..dcab85ab54708 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1158,7 +1158,7 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
                                        const ExpectedData &ED) {
   // Building a set of all directives ordered by their location
   auto directiveComparator = [](const Directive *LHS, const Directive *RHS) {
-    return LHS->DirectiveLoc < RHS->DiagnosticLoc;
+    return LHS->DirectiveLoc < RHS->DirectiveLoc;
   };
   auto sortDirectives = [&](const DirectiveList &Unordered) {
     std::vector<const Directive *> Ordered(Unordered.size());
@@ -1191,8 +1191,8 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
     return OrderedDirectives;
   }();
 
-  auto getLocDiagPair = [&](DiagnosticsEngine::Level DiagLevel,
-                            long DiagIndex) {
+  auto getLocDiagPair = [&](DiagnosticsEngine::Level DiagLevel, long DiagIndex)
+      -> const std::pair<clang::SourceLocation, std::basic_string<char>> & {
     TextDiagnosticBuffer::const_iterator It = [&] {
       switch (DiagLevel) {
       case DiagnosticsEngine::Level::Fatal:
@@ -1230,15 +1230,15 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
   for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
            OrderedDirectives,
            llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
-    assert(!Directive->MatchAnyFileAndLine);
-    assert(!Directive->MatchAnyLine);
+    assert(!Directive->MatchAnyFileAndLine && !Directive->MatchAnyLine &&
+           "Wildcards should not be allowed in strict verify mode!");
     const auto [DiagLevel, DiagIndex] = LevelDiagPair;
-    const auto [DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
+    const auto &[DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
     const SourceLocation DirLoc = Directive->DirectiveLoc;
     bool LocsMatch =
         SourceMgr.getPresumedLineNumber(DiagLoc) ==
             SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
-        IsFromSameFile(SourceMgr, DiagLoc, Directive->DiagnosticLoc);
+        IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
     if (!LocsMatch ||
         Directive->match(DiagText) != DiagnosticMatchResult::Match) {
       SmallString<256> Fmt;

>From d86d2c609c40184f697147a7bf100f2d37e8f353 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 5 Nov 2025 13:33:17 +0300
Subject: [PATCH 11/30] Significantly Improve diagnsotics for out of order case

---
 .../clang/Basic/DiagnosticFrontendKinds.td    |  4 +-
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 37 ++++++-------------
 2 files changed, 14 insertions(+), 27 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 7f3ebc266a77a..0d3255795c5dd 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -191,8 +191,8 @@ def err_verify_nonconst_addrspace : Error<
 def err_verify_message_partial_match
     : Error<"diagnostic messages not fully matched: %0">;
 def err_verify_directive_out_of_order
-    : Error<"out of order directive found: %0\nSubsequent directives were "
-            "skipped">;
+    : Error<"all diagnostics were successfully matched, but out of order "
+            "directives were found: %0">;
 def err_verify_non_singular_match
     : Error<"exactly one diagnostic can be matched">;
 def err_verify_wildcard_loc
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index dcab85ab54708..cf5600e4bd675 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1224,9 +1224,11 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
   using LevelDiagPairT = std::pair<DiagnosticsEngine::Level, size_t>;
   static_assert(std::is_same_v<LevelDiagPairT,
                                TextDiagnosticBuffer::AllDiagList::value_type>);
-
+  int NumProblems = 0;
+  SmallString<256> Fmt;
+  llvm::raw_svector_ostream OS(Fmt);
   // CheckResults already ensured that there are as many directives as emitted
-  // diagnostics
+  // diagnostics, and that all of them match.
   for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
            OrderedDirectives,
            llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
@@ -1241,31 +1243,16 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
         IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
     if (!LocsMatch ||
         Directive->match(DiagText) != DiagnosticMatchResult::Match) {
-      SmallString<256> Fmt;
-      llvm::raw_svector_ostream OS(Fmt);
-      OS << "\n  directive at " << SourceMgr.getFilename(DirLoc) << ":"
-         << SourceMgr.getPresumedLineNumber(DirLoc) << ": " << Directive->Text
-         << "\n  that expects diagnostic at ";
-      if (IsFromSameFile(SourceMgr, DirLoc, Directive->DiagnosticLoc)) {
-        OS << "line "
-           << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
-      } else {
-        OS << SourceMgr.getFilename(Directive->DiagnosticLoc) << ":"
-           << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
-      }
-      OS << "\n  does not match diagnostic at ";
-      if (IsFromSameFile(SourceMgr, DirLoc, DiagLoc)) {
-        OS << "line " << SourceMgr.getPresumedLineNumber(DiagLoc);
-      } else {
-        OS << SourceMgr.getFilename(DiagLoc) << ":"
-           << SourceMgr.getPresumedLineNumber(DiagLoc);
-      }
-      OS << ": " << DiagText;
-      Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
-      return 1;
+      OS << "\n  '" << Directive->Spelling << "' at line "
+         << SourceMgr.getPresumedLineNumber(DirLoc) << " in "
+         << SourceMgr.getFilename(DirLoc) << ": " << Directive->Text;
+      ++NumProblems;
     }
   }
-  return 0;
+  if (NumProblems > 0) {
+    Diags.Report(diag::err_verify_directive_out_of_order) << OS.str();
+  }
+  return NumProblems;
 }
 
 void VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM,

>From ff657ee7bfa8d25ed615dff3e50da8642338ff22 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Mon, 10 Nov 2025 15:44:56 +0300
Subject: [PATCH 12/30] Print severity in diagnostic about partial matches

---
 clang/include/clang/Basic/DiagnosticFrontendKinds.td | 2 +-
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp      | 7 ++++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 0d3255795c5dd..e31692621d2a5 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -189,7 +189,7 @@ def err_verify_no_directives : Error<
 def err_verify_nonconst_addrspace : Error<
   "qualifier 'const' is needed for variables in address space '%0'">;
 def err_verify_message_partial_match
-    : Error<"diagnostic messages not fully matched: %0">;
+    : Error<"diagnostic messages of '%0' severity not fully matched: %1">;
 def err_verify_directive_out_of_order
     : Error<"all diagnostics were successfully matched, but out of order "
             "directives were found: %0">;
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index cf5600e4bd675..729c88501253c 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1025,7 +1025,8 @@ static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
 /// despite '-verify-strict' option being set.
 static unsigned
 PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
-             llvm::SmallVector<std::pair<Directive *, std::string>> &DL) {
+             llvm::SmallVector<std::pair<Directive *, std::string>> &DL,
+             const char *Kind) {
   if (DL.empty())
     return 0;
 
@@ -1048,7 +1049,7 @@ PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   }
 
   Diags.Report(diag::err_verify_message_partial_match).setForceEmit()
-      << OS.str();
+      << Kind << OS.str();
   return DL.size();
 }
 
@@ -1105,7 +1106,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
   if (!IgnoreUnexpected)
     num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
-  num += PrintPartial(Diags, SourceMgr, IncompleteMatches);
+  num += PrintPartial(Diags, SourceMgr, IncompleteMatches, Label);
   return num;
 }
 

>From 6bbdf64fb62abc00af8ce991ab83dc22201f135c Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 21 Nov 2025 17:55:19 +0300
Subject: [PATCH 13/30] Add missing hyphens to diagnostic message

---
 clang/include/clang/Basic/DiagnosticFrontendKinds.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index e31692621d2a5..335b299155b26 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -191,7 +191,7 @@ def err_verify_nonconst_addrspace : Error<
 def err_verify_message_partial_match
     : Error<"diagnostic messages of '%0' severity not fully matched: %1">;
 def err_verify_directive_out_of_order
-    : Error<"all diagnostics were successfully matched, but out of order "
+    : Error<"all diagnostics were successfully matched, but out-of-order "
             "directives were found: %0">;
 def err_verify_non_singular_match
     : Error<"exactly one diagnostic can be matched">;

>From 2825edb3b6aa425057e85e1fd4f417e8ec327f9a Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 21 Nov 2025 17:56:34 +0300
Subject: [PATCH 14/30] Correctly trim strings before checking for full match

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 729c88501253c..85fb818d9e64f 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -133,12 +133,14 @@ class RegexDirective : public Directive {
     }
 
     llvm::SmallVector<StringRef, 4> Matches;
-    Regex.match(S.trim(), &Matches);
+    llvm::StringRef TrimmedText = S.trim();
+    Regex.match(TrimmedText, &Matches);
     if (Matches.empty()) {
       return DiagnosticMatchResult::NoMatch;
     }
-    return Matches[0].size() == S.size() ? DiagnosticMatchResult::Match
-                                         : DiagnosticMatchResult::PartialMatch;
+    return Matches[0].size() == TrimmedText.size()
+               ? DiagnosticMatchResult::Match
+               : DiagnosticMatchResult::PartialMatch;
   }
 
 private:

>From f5b93d6716f85bece3488f1e5eb74ef406577e17 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 21 Nov 2025 17:57:13 +0300
Subject: [PATCH 15/30] Improve diagnostic for out-of-order case

---
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 36 +++++++++++++++----
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 85fb818d9e64f..61f3fe27e062a 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1240,16 +1240,40 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
     const auto [DiagLevel, DiagIndex] = LevelDiagPair;
     const auto &[DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
     const SourceLocation DirLoc = Directive->DirectiveLoc;
+
     bool LocsMatch =
         SourceMgr.getPresumedLineNumber(DiagLoc) ==
             SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
         IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
-    if (!LocsMatch ||
-        Directive->match(DiagText) != DiagnosticMatchResult::Match) {
-      OS << "\n  '" << Directive->Spelling << "' at line "
-         << SourceMgr.getPresumedLineNumber(DirLoc) << " in "
-         << SourceMgr.getFilename(DirLoc) << ": " << Directive->Text;
-      ++NumProblems;
+    bool TextMatch = Directive->match(DiagText) == DiagnosticMatchResult::Match;
+    if (LocsMatch && TextMatch) {
+      continue;
+    }
+    ++NumProblems;
+
+    auto printFileNameIfDifferent = [&](SourceLocation DirLoc,
+                                        SourceLocation Loc) {
+      if (!IsFromSameFile(SourceMgr, DirLoc, Loc)) {
+        OS << " in " << SourceMgr.getFilename(Loc);
+      }
+    };
+
+    OS << "\n  '" << Directive->Spelling << "' at line "
+       << SourceMgr.getPresumedLineNumber(DirLoc) << " in "
+       << SourceMgr.getFilename(DirLoc) << ": " << Directive->Text
+       << "\n    matches diagnostic at line "
+       << SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc);
+    printFileNameIfDifferent(DirLoc, Directive->DiagnosticLoc);
+    if (TextMatch) {
+      OS << ", but diagnostic with the same message was first emitted at line "
+         << SourceMgr.getPresumedLineNumber(DiagLoc);
+      printFileNameIfDifferent(DirLoc, DiagLoc);
+    } else {
+      OS << ", but diagnostic at line "
+         << SourceMgr.getPresumedLineNumber(DiagLoc);
+      printFileNameIfDifferent(DirLoc, DiagLoc);
+      OS << " was emitted first:"
+         << "\n      " << DiagText;
     }
   }
   if (NumProblems > 0) {

>From 06ac16a2f9af44d82a556a8898c9383236ff098c Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 21 Nov 2025 17:57:34 +0300
Subject: [PATCH 16/30] Improve diagnostic for partial case

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 61f3fe27e062a..488abec664060 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -1037,15 +1037,13 @@ PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   for (const auto &[D, DiagText] : DL) {
     assert(!D->MatchAnyLine && !D->MatchAnyFileAndLine &&
            "Wildcards should not be allowed in strict verify mode");
-    OS << "\n  Directive at " << SourceMgr.getFilename(D->DirectiveLoc) << ":"
-       << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << " \'"
-       << D->Spelling << "\': " << D->Text
-       << "\n    does not fully match diagnostic at ";
-    if (IsFromSameFile(SourceMgr, D->DirectiveLoc, D->DiagnosticLoc)) {
-      OS << "line " << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
-    } else {
-      OS << SourceMgr.getFilename(D->DiagnosticLoc) << ":"
-         << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
+    OS << "\n  '" << D->Spelling << "' at line "
+       << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << " in "
+       << SourceMgr.getFilename(D->DiagnosticLoc) << ": " << D->Text
+       << "\n    does not fully match diagnostic at line "
+       << SourceMgr.getPresumedLineNumber(D->DiagnosticLoc);
+    if (!IsFromSameFile(SourceMgr, D->DirectiveLoc, D->DiagnosticLoc)) {
+      OS << " in " << SourceMgr.getFilename(D->DiagnosticLoc);
     }
     OS << ": " << DiagText;
   }

>From f3ddf13c949c42a1dc4f5d184064cb6e9dadaf6e Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 21 Nov 2025 17:58:37 +0300
Subject: [PATCH 17/30] Fix missing diagnostic for non-singular match

---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 488abec664060..d32fcac9f371e 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -666,7 +666,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
       }
     } else if (PH.Next("+")) {
       // '+' on its own means "1 or more".
-      if (OneDiagPerDirective && D.Max != 1) {
+      if (OneDiagPerDirective) {
         Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                      diag::err_verify_non_singular_match);
         continue;

>From ec12c830e929974d788848e45e9284a0e269c370 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Mon, 3 Nov 2025 07:50:25 -0500
Subject: [PATCH 18/30] [C2y] Support WG14 N3457, the __COUNTER__ macro
 (#162662)

This implements the parts of
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm which were
adopted at the recent meeting in Brno.

Clang already implemented `__COUNTER__`, but needed some changes for
conformance. Specifically, we now diagnose when the macro is expanded
more than 2147483647 times. Additionally, we now give the expected
extension and pre-compat warnings for the feature.

To support testing the limits, this also adds a -cc1-only option,
`-finitial-counter-value=`, which lets you specify the initial value the
`__COUNTER__` macro should expand to.
---
 clang/docs/LanguageExtensions.rst             |  5 ++-
 clang/docs/ReleaseNotes.rst                   |  5 +++
 .../include/clang/Basic/DiagnosticLexKinds.td |  8 ++++
 clang/include/clang/Driver/Options.td         |  4 ++
 clang/include/clang/Lex/Preprocessor.h        |  6 +--
 clang/include/clang/Lex/PreprocessorOptions.h |  4 ++
 clang/include/clang/Serialization/ASTReader.h |  8 ++--
 clang/lib/Frontend/ASTUnit.cpp                |  6 +--
 clang/lib/Frontend/InitPreprocessor.cpp       |  3 ++
 clang/lib/Lex/PPMacroExpansion.cpp            | 14 ++++++-
 clang/lib/Serialization/ASTReader.cpp         |  4 +-
 clang/test/C/C2y/n3457.c                      | 38 +++++++++++++++++++
 clang/test/C/C2y/n3457_1.c                    | 20 ++++++++++
 clang/test/C/C2y/n3457_2.c                    | 10 +++++
 clang/www/c_status.html                       |  2 +-
 .../benchmark/include/benchmark/benchmark.h   | 10 +++++
 16 files changed, 132 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/C/C2y/n3457.c
 create mode 100644 clang/test/C/C2y/n3457_1.c
 create mode 100644 clang/test/C/C2y/n3457_2.c

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 495f2ab3926ce..baa0bbb5ea631 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -385,7 +385,9 @@ Builtin Macros
 
 ``__COUNTER__``
   Defined to an integer value that starts at zero and is incremented each time
-  the ``__COUNTER__`` macro is expanded.
+  the ``__COUNTER__`` macro is expanded. This is a standard feature in C2y but
+  is an extension in earlier language modes and in C++. This macro can only be
+  expanded 2147483647 times at most.
 
 ``__INCLUDE_LEVEL__``
   Defined to an integral value that is the include depth of the file currently
@@ -1821,6 +1823,7 @@ Octal literals prefixed with ``0o`` or ``0O``                                  C
 ``_Countof`` (N3369, N3469)                                                    C2y           C89
 ``_Generic`` with a type operand (N3260)                                       C2y           C89, C++
 ``++``/``--`` on ``_Complex`` value (N3259)                                    C2y           C89, C++
+``__COUNTER__`` (N3457)                                                        C2y           C89, C++
 ============================================= ================================ ============= =============
 
 Builtin type aliases
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 73aaaad8b32e5..56fd508e46ff0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -196,6 +196,11 @@ C2y Feature Support
   function or variable within an extern inline function is no longer a
   constraint per `WG14 N3622 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3622.txt>`_.
 - Clang now supports `N3355 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3355.htm>`_ Named Loops.
+- Clang's implementation of ``__COUNTER__`` was updated to conform to
+  `WG14 N3457 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm>`_.
+  This includes adding pedantic warnings for the feature being an extension in
+  other language modes as well as an error when the counter is expanded more
+  than 2147483647 times.
 
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index c7fe6e1db6d1f..417187222e448 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -90,6 +90,14 @@ def err_unterminated___pragma : Error<"missing terminating ')' character">;
 
 def err_conflict_marker : Error<"version control conflict marker in file">;
 
+def err_counter_overflow : Error<
+  "'__COUNTER__' value cannot exceed 2'147'483'647">;
+def ext_counter : Extension<
+  "'__COUNTER__' is a C2y extension">, InGroup<C2y>;
+def warn_counter : Warning<
+  "'__COUNTER__' is incompatible with standards before C2y">,
+  InGroup<CPre2yCompat>, DefaultIgnore;
+
 def err_raw_delim_too_long : Error<
   "raw string delimiter longer than 16 characters"
   "; use PREFIX( )PREFIX to delimit raw string">;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 77cb21e3b7d8a..1ac6725cda7c3 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8451,6 +8451,10 @@ def aligned_alloc_unavailable : Flag<["-"], "faligned-alloc-unavailable">,
   MarshallingInfoFlag<LangOpts<"AlignedAllocationUnavailable">>,
   ShouldParseIf<faligned_allocation.KeyPath>;
 
+def finitial_counter_value_EQ : Joined<["-"], "finitial-counter-value=">,
+  HelpText<"Sets the initial value for __COUNTER__, defaults to 0.">,
+  MarshallingInfoInt<PreprocessorOpts<"InitialCounterValue">, "0">;
+
 } // let Visibility = [CC1Option]
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 39754847a93e4..412002259c054 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -226,7 +226,7 @@ class Preprocessor {
       LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine;
 
   // Next __COUNTER__ value, starts at 0.
-  unsigned CounterValue = 0;
+  uint32_t CounterValue = 0;
 
   enum {
     /// Maximum depth of \#includes.
@@ -2421,8 +2421,8 @@ class Preprocessor {
   bool SawDateOrTime() const {
     return DATELoc != SourceLocation() || TIMELoc != SourceLocation();
   }
-  unsigned getCounterValue() const { return CounterValue; }
-  void setCounterValue(unsigned V) { CounterValue = V; }
+  uint32_t getCounterValue() const { return CounterValue; }
+  void setCounterValue(uint32_t V) { CounterValue = V; }
 
   LangOptions::FPEvalMethodKind getCurrentFPEvalMethod() const {
     assert(CurrentFPEvalMethod != LangOptions::FEM_UnsetOnCommandLine &&
diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h
index d4c4e1ccbf2c4..1c2f6e72e1b93 100644
--- a/clang/include/clang/Lex/PreprocessorOptions.h
+++ b/clang/include/clang/Lex/PreprocessorOptions.h
@@ -198,6 +198,10 @@ class PreprocessorOptions {
   /// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH.
   std::optional<uint64_t> SourceDateEpoch;
 
+  /// The initial value for __COUNTER__; typically is zero but can be set via a
+  /// -cc1 flag for testing purposes.
+  uint32_t InitialCounterValue = 0;
+
 public:
   PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {}
 
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index af856a8097ab1..4ca45a16408a6 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -220,8 +220,8 @@ class ASTReaderListener {
   }
 
   /// Receives __COUNTER__ value.
-  virtual void ReadCounter(const serialization::ModuleFile &M,
-                           unsigned Value) {}
+  virtual void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) {
+  }
 
   /// This is called for each AST file loaded.
   virtual void visitModuleFile(StringRef Filename,
@@ -312,7 +312,7 @@ class ChainedASTReaderListener : public ASTReaderListener {
                                bool Complain,
                                std::string &SuggestedPredefines) override;
 
-  void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override;
+  void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override;
   bool needsInputFileVisitation() override;
   bool needsSystemInputFileVisitation() override;
   void visitModuleFile(StringRef Filename,
@@ -352,7 +352,7 @@ class PCHValidator : public ASTReaderListener {
                                StringRef ModuleFilename,
                                StringRef SpecificModuleCachePath,
                                bool Complain) override;
-  void ReadCounter(const serialization::ModuleFile &M, unsigned Value) override;
+  void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override;
 };
 
 /// ASTReaderListenter implementation to set SuggestedPredefines of
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index 6cc7094846155..1169acb389acf 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -518,14 +518,14 @@ class ASTInfoCollector : public ASTReaderListener {
   LangOptions &LangOpts;
   CodeGenOptions &CodeGenOpts;
   TargetOptions &TargetOpts;
-  unsigned &Counter;
+  uint32_t &Counter;
 
 public:
   ASTInfoCollector(HeaderSearchOptions &HSOpts,
                    std::string &SpecificModuleCachePath,
                    PreprocessorOptions &PPOpts, LangOptions &LangOpts,
                    CodeGenOptions &CodeGenOpts, TargetOptions &TargetOpts,
-                   unsigned &Counter)
+                   uint32_t &Counter)
       : HSOpts(HSOpts), SpecificModuleCachePath(SpecificModuleCachePath),
         PPOpts(PPOpts), LangOpts(LangOpts), CodeGenOpts(CodeGenOpts),
         TargetOpts(TargetOpts), Counter(Counter) {}
@@ -577,7 +577,7 @@ class ASTInfoCollector : public ASTReaderListener {
   }
 
   void ReadCounter(const serialization::ModuleFile &M,
-                   unsigned NewCounter) override {
+                   uint32_t NewCounter) override {
     Counter = NewCounter;
   }
 };
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 47f1d5a6b636c..9ddcc366dc1d9 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1542,6 +1542,9 @@ void clang::InitializePreprocessor(Preprocessor &PP,
   llvm::raw_string_ostream Predefines(PredefineBuffer);
   MacroBuilder Builder(Predefines);
 
+  // Ensure that the initial value of __COUNTER__ is hooked up.
+  PP.setCounterValue(InitOpts.InitialCounterValue);
+
   // Emit line markers for various builtin sections of the file. The 3 here
   // marks <built-in> as being a system header, which suppresses warnings when
   // the same macro is defined multiple times.
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index dd80ae586a1f6..5efa4b5b3f872 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1735,7 +1735,19 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
       Diag(getLastFPEvalPragmaLocation(), diag::note_pragma_entered_here);
     }
   } else if (II == Ident__COUNTER__) {
-    // __COUNTER__ expands to a simple numeric value.
+    Diag(Tok.getLocation(),
+         getLangOpts().C2y ? diag::warn_counter : diag::ext_counter);
+    // __COUNTER__ expands to a simple numeric value that must be less than
+    // 2147483647.
+    constexpr uint32_t MaxPosValue = std::numeric_limits<int32_t>::max();
+    if (CounterValue > MaxPosValue) {
+      Diag(Tok.getLocation(), diag::err_counter_overflow);
+      // Retain the maximal value so we don't issue conversion-related
+      // diagnostics by overflowing into a long long. While this does produce
+      // a duplicate value, there's no way to ignore this error so there's no
+      // translation anyway.
+      CounterValue = MaxPosValue;
+    }
     OS << CounterValue++;
     Tok.setKind(tok::numeric_constant);
   } else if (II == Ident__has_feature) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index e3106f8d8e13c..d5528219bb7d5 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -225,7 +225,7 @@ bool ChainedASTReaderListener::ReadPreprocessorOptions(
 }
 
 void ChainedASTReaderListener::ReadCounter(const serialization::ModuleFile &M,
-                                           unsigned Value) {
+                                           uint32_t Value) {
   First->ReadCounter(M, Value);
   Second->ReadCounter(M, Value);
 }
@@ -973,7 +973,7 @@ bool PCHValidator::ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
       PP.getPreprocessorOpts());
 }
 
-void PCHValidator::ReadCounter(const ModuleFile &M, unsigned Value) {
+void PCHValidator::ReadCounter(const ModuleFile &M, uint32_t Value) {
   PP.setCounterValue(Value);
 }
 
diff --git a/clang/test/C/C2y/n3457.c b/clang/test/C/C2y/n3457.c
new file mode 100644
index 0000000000000..d71a3f37e1343
--- /dev/null
+++ b/clang/test/C/C2y/n3457.c
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -verify=ext -std=c23 -pedantic %s
+// RUN: %clang_cc1 -verify=ext -pedantic -x c++ %s
+// RUN: %clang_cc1 -verify=pre -std=c2y -pedantic -Wpre-c2y-compat %s
+
+/* WG14 N3457: Clang 22
+ * The __COUNTER__ predefined macro
+ *
+ * This predefined macro was supported as an extension in earlier versions of
+ * Clang, but the required diagnostics for the limits were not added until 22.
+ */
+
+// Ensure that __COUNTER__ starts from 0.
+static_assert(__COUNTER__ == 0); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                  */
+
+// Ensure that the produced value can be used with token concatenation.
+#define CAT_IMPL(a, b) a ## b
+#define CAT(a, b) CAT_IMPL(a, b)
+#define NAME_WITH_COUNTER(a) CAT(a, __COUNTER__)
+void test() {
+  // Because this is the 2nd expansion, this defines test1.
+  int NAME_WITH_COUNTER(test); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                  pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                */
+  int other_test = test1;      // Ok
+}
+
+// Ensure that __COUNTER__ increments each time you mention it.
+static_assert(__COUNTER__ == 2); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
+static_assert(__COUNTER__ == 3); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
+static_assert(__COUNTER__ == 4); /* ext-warning {{'__COUNTER__' is a C2y extension}}
+                                    pre-warning {{'__COUNTER__' is incompatible with standards before C2y}}
+                                 */
diff --git a/clang/test/C/C2y/n3457_1.c b/clang/test/C/C2y/n3457_1.c
new file mode 100644
index 0000000000000..76c5a0b9a700f
--- /dev/null
+++ b/clang/test/C/C2y/n3457_1.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483646 %s
+
+// The value produced needs to be a type that's representable with a signed
+// long. However, the actual type it expands to does *not* need to be forced to
+// be signed long because that would generally mean suffixing the value with L,
+// which would be very surprising for folks using this to generate unique ids.
+// We'll test this by ensuring the largest value can be expanded properly and
+// an assertion that signed long is always at least four bytes wide (which is
+// what's required to represent that maximal value).
+//
+// So we set the initial counter value to 2147483646, we'll validate that,
+// increment it once to get to the maximal value and ensure there's no
+// diagnostic, then increment again to ensure we get the constraint violation.
+
+static_assert(__COUNTER__ == 2147483646); // Test and increment
+static_assert(__COUNTER__ == 2147483647); // Test and increment
+
+// This one should fail.
+signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2'147'483'647}}
+
diff --git a/clang/test/C/C2y/n3457_2.c b/clang/test/C/C2y/n3457_2.c
new file mode 100644
index 0000000000000..018c8f4390767
--- /dev/null
+++ b/clang/test/C/C2y/n3457_2.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -verify=good -std=c2y -finitial-counter-value=2147483648 %s
+// RUN: %clang_cc1 -verify -std=c2y -finitial-counter-value=2147483648 -DEXPAND_IT %s
+// good-no-diagnostics
+
+// This sets the intial __COUNTER__ value to something that's too big. Setting
+// the value too large is fine. Expanding to a too-large value is not.
+#ifdef EXPAND_IT
+  // This one should fail.
+  signed long i = __COUNTER__; // expected-error {{'__COUNTER__' value cannot exceed 2'147'483'647}}
+#endif
diff --git a/clang/www/c_status.html b/clang/www/c_status.html
index b8039622fe694..80a52f791dfcf 100644
--- a/clang/www/c_status.html
+++ b/clang/www/c_status.html
@@ -329,7 +329,7 @@ <h2 id="c2y">C2y implementation status</h2>
     <tr>
       <td>The __COUNTER__ predefined macro</td>
       <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm">N3457</a></td>
-      <td class="unknown" align="center">Unknown</td>
+      <td class="unreleased" align="center">Clang 22</td>
 	</tr>
     <tr>
       <td>Chasing Ghosts I: constant expressions v2</td>
diff --git a/third-party/benchmark/include/benchmark/benchmark.h b/third-party/benchmark/include/benchmark/benchmark.h
index 08cfe29da344e..c2debb216d64f 100644
--- a/third-party/benchmark/include/benchmark/benchmark.h
+++ b/third-party/benchmark/include/benchmark/benchmark.h
@@ -250,6 +250,10 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
   _Pragma("GCC diagnostic push")             \
   _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
 #define BENCHMARK_RESTORE_DEPRECATED_WARNING _Pragma("GCC diagnostic pop")
+#define BENCHMARK_DISABLE_PEDANTIC_WARNING \
+  _Pragma("GCC diagnostic push")             \
+  _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
+#define BENCHMARK_RESTORE_PEDANTIC_WARNING _Pragma("GCC diagnostic pop")
 #elif defined(__NVCOMPILER)
 #define BENCHMARK_BUILTIN_EXPECT(x, y) __builtin_expect(x, y)
 #define BENCHMARK_DEPRECATED_MSG(msg) __attribute__((deprecated(msg)))
@@ -257,6 +261,8 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
   _Pragma("diagnostic push") \
   _Pragma("diag_suppress deprecated_entity_with_custom_message")
 #define BENCHMARK_RESTORE_DEPRECATED_WARNING _Pragma("diagnostic pop")
+#define BENCHMARK_DISABLE_PEDANTIC_WARNING
+#define BENCHMARK_RESTORE_PEDANTIC_WARNING
 #else
 #define BENCHMARK_BUILTIN_EXPECT(x, y) x
 #define BENCHMARK_DEPRECATED_MSG(msg)
@@ -265,6 +271,8 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
       __LINE__) ") : warning note: " msg))
 #define BENCHMARK_DISABLE_DEPRECATED_WARNING
 #define BENCHMARK_RESTORE_DEPRECATED_WARNING
+#define BENCHMARK_DISABLE_PEDANTIC_WARNING
+#define BENCHMARK_RESTORE_PEDANTIC_WARNING
 #endif
 // clang-format on
 
@@ -1462,11 +1470,13 @@ class Fixture : public internal::Benchmark {
 // Check that __COUNTER__ is defined and that __COUNTER__ increases by 1
 // every time it is expanded. X + 1 == X + 0 is used in case X is defined to be
 // empty. If X is empty the expression becomes (+1 == +0).
+BENCHMARK_DISABLE_PEDANTIC_WARNING
 #if defined(__COUNTER__) && (__COUNTER__ + 1 == __COUNTER__ + 0)
 #define BENCHMARK_PRIVATE_UNIQUE_ID __COUNTER__
 #else
 #define BENCHMARK_PRIVATE_UNIQUE_ID __LINE__
 #endif
+BENCHMARK_RESTORE_PEDANTIC_WARNING
 
 // Helpers for generating unique variable names
 #ifdef BENCHMARK_HAS_CXX11

>From 4576991d7dc4bd7a18b2266e5c1d58d666dcc508 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 30 Jan 2026 13:56:47 +0300
Subject: [PATCH 19/30] Rename enumerators in DiagnosticMatchResult

---
 .../clang/Frontend/VerifyDiagnosticConsumer.h    |  4 ++--
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp  | 16 ++++++++--------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index ef1839f25cafd..9098ac685cbb7 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -32,8 +32,8 @@ class TextDiagnosticBuffer;
 
 enum class DiagnosticMatchResult {
   NoMatch,
-  Match,
-  PartialMatch,
+  Partial,
+  Full,
 };
 
 /// VerifyDiagnosticConsumer - Create a diagnostic client which will use
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 2ab54dce477c7..eed799890ce41 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -104,10 +104,10 @@ class StandardDirective : public Directive {
       return DiagnosticMatchResult::NoMatch;
     }
     if (!FullMatchRequired) {
-      return DiagnosticMatchResult::Match;
+      return DiagnosticMatchResult::Full;
     }
-    return S.trim() == Text ? DiagnosticMatchResult::Match
-                            : DiagnosticMatchResult::PartialMatch;
+    return S.trim() == Text ? DiagnosticMatchResult::Full
+                            : DiagnosticMatchResult::Partial;
   }
 };
 
@@ -128,7 +128,7 @@ class RegexDirective : public Directive {
 
   DiagnosticMatchResult match(StringRef S) const override {
     if (!FullMatchRequired) {
-      return Regex.match(S) ? DiagnosticMatchResult::Match
+      return Regex.match(S) ? DiagnosticMatchResult::Full
                             : DiagnosticMatchResult::NoMatch;
     }
 
@@ -139,8 +139,8 @@ class RegexDirective : public Directive {
       return DiagnosticMatchResult::NoMatch;
     }
     return Matches[0].size() == TrimmedText.size()
-               ? DiagnosticMatchResult::Match
-               : DiagnosticMatchResult::PartialMatch;
+               ? DiagnosticMatchResult::Full
+               : DiagnosticMatchResult::Partial;
   }
 
 private:
@@ -1086,7 +1086,7 @@ static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
         DiagnosticMatchResult MatchResult = D.match(RightText);
         if (MatchResult != DiagnosticMatchResult::NoMatch) {
           if (D.FullMatchRequired &&
-              MatchResult == DiagnosticMatchResult::PartialMatch) {
+              MatchResult == DiagnosticMatchResult::Partial) {
             IncompleteMatches.push_back({&D, II->second});
           }
           break;
@@ -1243,7 +1243,7 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
         SourceMgr.getPresumedLineNumber(DiagLoc) ==
             SourceMgr.getPresumedLineNumber(Directive->DiagnosticLoc) &&
         IsFromSameFile(SourceMgr, Directive->DiagnosticLoc, DiagLoc);
-    bool TextMatch = Directive->match(DiagText) == DiagnosticMatchResult::Match;
+    bool TextMatch = Directive->match(DiagText) == DiagnosticMatchResult::Full;
     if (LocsMatch && TextMatch) {
       continue;
     }

>From 56cbcbae7861b7f1b28b380e8ef2673ffcc0dfe9 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Fri, 30 Jan 2026 14:59:20 +0300
Subject: [PATCH 20/30] Add tests

---
 .../Frontend/verify-strict-full-match.cpp     | 10 ++++
 .../test/Frontend/verify-strict-one-diag.cpp  | 58 +++++++++++++++++++
 clang/test/Frontend/verify-strict-order.cpp   | 14 +++++
 .../test/Frontend/verify-strict-wildcard.cpp  | 22 +++++++
 4 files changed, 104 insertions(+)
 create mode 100644 clang/test/Frontend/verify-strict-full-match.cpp
 create mode 100644 clang/test/Frontend/verify-strict-one-diag.cpp
 create mode 100644 clang/test/Frontend/verify-strict-order.cpp
 create mode 100644 clang/test/Frontend/verify-strict-wildcard.cpp

diff --git a/clang/test/Frontend/verify-strict-full-match.cpp b/clang/test/Frontend/verify-strict-full-match.cpp
new file mode 100644
index 0000000000000..c84b1451a82af
--- /dev/null
+++ b/clang/test/Frontend/verify-strict-full-match.cpp
@@ -0,0 +1,10 @@
+// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+
+void f1(A);
+// expected-error at -1 {{unknown type}}
+
+// CHECK:      error: diagnostic messages of 'error' severity not fully matched:
+// CHECK-NEXT:   'expected-error' at line 4 in {{.*}}: unknown type
+// CHECK-NEXT:      does not fully match diagnostic at line 3: unknown type name 'A'
+
+// CHECK-NEXT: 1 error generated.
diff --git a/clang/test/Frontend/verify-strict-one-diag.cpp b/clang/test/Frontend/verify-strict-one-diag.cpp
new file mode 100644
index 0000000000000..1888ceb549f49
--- /dev/null
+++ b/clang/test/Frontend/verify-strict-one-diag.cpp
@@ -0,0 +1,58 @@
+// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+
+void f1(A, A);
+// expected-error at -1 {{unknown type name 'A'}}
+// expected-error at -2 1 {{unknown type name 'A'}}
+
+void f2(A, A);
+// expected-error at -1 2 {{unknown type name 'A'}}
+
+// CHECK:      error: 'expected-error' diagnostics seen but not expected:
+// CHECK-NEXT:   Line 7: unknown type name 'A'
+// CHECK-NEXT:   Line 7: unknown type name 'A'
+// CHECK-NEXT:   Line 8: exactly one diagnostic can be matched
+
+void f3(A, A);
+// expected-error at -1 0-1 {{unknown type name 'A'}}
+// expected-error at -2 0-1 {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 15: unknown type name 'A'
+// CHECK-NEXT:   Line 15: unknown type name 'A'
+// CHECK-NEXT:   Line 16: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 17: exactly one diagnostic can be matched
+
+void f4(A, A);
+// expected-error at -1 1-2 {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 24: unknown type name 'A'
+// CHECK-NEXT:   Line 24: unknown type name 'A'
+// CHECK-NEXT:   Line 25: exactly one diagnostic can be matched
+
+void f5(A);
+// expected-error at -1 0-2 {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 31: unknown type name 'A'
+// CHECK-NEXT:   Line 32: exactly one diagnostic can be matched
+
+void f6(A, A);
+// expected-error at -1 + {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 37: unknown type name 'A'
+// CHECK-NEXT:   Line 37: unknown type name 'A'
+// CHECK-NEXT:   Line 38: exactly one diagnostic can be matched
+
+void f7(A, A);
+// expected-error at -1 0+ {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 44: unknown type name 'A'
+// CHECK-NEXT:   Line 44: unknown type name 'A'
+// CHECK-NEXT:   Line 45: exactly one diagnostic can be matched
+
+void f8(A, A);
+// expected-error at -1 1+ {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 51: unknown type name 'A'
+// CHECK-NEXT:   Line 51: unknown type name 'A'
+// CHECK-NEXT:   Line 52: exactly one diagnostic can be matched
+
+// CHECK-NEXT: 21 errors generated.
diff --git a/clang/test/Frontend/verify-strict-order.cpp b/clang/test/Frontend/verify-strict-order.cpp
new file mode 100644
index 0000000000000..6e1dd877cb6b0
--- /dev/null
+++ b/clang/test/Frontend/verify-strict-order.cpp
@@ -0,0 +1,14 @@
+// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+
+void f1(A); // #f1
+void f2(B); // #f2
+// expected-error@#f2 {{unknown type name 'B'}}
+// expected-error@#f1 {{unknown type name 'A'}}
+
+// CHECK:      error: all diagnostics were successfully matched, but out-of-order directives were found:
+// CHECK-NEXT:   'expected-error' at line 5 in {{.*}}: unknown type name 'B'
+// CHECK-NEXT:     matches diagnostic at line 4, but diagnostic at line 3 was emitted first:
+// CHECK-NEXT:       unknown type name 'A'
+// CHECK-NEXT:   'expected-error' at line 6 in {{.*}}: unknown type name 'A'
+// CHECK-NEXT:     matches diagnostic at line 3, but diagnostic at line 4 was emitted first:
+// CHECK-NEXT:       unknown type name 'B'
diff --git a/clang/test/Frontend/verify-strict-wildcard.cpp b/clang/test/Frontend/verify-strict-wildcard.cpp
new file mode 100644
index 0000000000000..64069ebced5e9
--- /dev/null
+++ b/clang/test/Frontend/verify-strict-wildcard.cpp
@@ -0,0 +1,22 @@
+// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+
+void f1(A);
+// expected-error@* {{unknown type name 'A'}}
+
+// CHECK:      error: 'expected-error' diagnostics seen but not expected:
+// CHECK-NEXT:   Line 3: unknown type name 'A'
+// CHECK-NEXT:   Line 4: cannot use wildcard for diagnostic location
+
+void f2(A);
+// expected-error@*:6 {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 10: unknown type name 'A'
+// CHECK-NEXT:   Line 11: cannot use wildcard for diagnostic location
+
+void f3(A);
+// expected-error@*:* {{unknown type name 'A'}}
+
+// CHECK-NEXT:   Line 16: unknown type name 'A'
+// CHECK-NEXT:   Line 17: cannot use wildcard for diagnostic location
+
+// CHECK-NEXT: 6 errors generated.

>From 4a23cb849fed044e9abecc97abcafab46c16b5db Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 31 Jan 2026 02:48:19 +0300
Subject: [PATCH 21/30] Don't ignore directives which have wildcards or don't
 match exactly one diag

---
 .../clang/Frontend/VerifyDiagnosticConsumer.h |  2 +
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 41 +++++++++++--------
 .../test/Frontend/verify-strict-one-diag.cpp  | 31 +++++---------
 .../test/Frontend/verify-strict-wildcard.cpp  | 12 +-----
 4 files changed, 38 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
index 9098ac685cbb7..f30f29233f1f5 100644
--- a/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
+++ b/clang/include/clang/Frontend/VerifyDiagnosticConsumer.h
@@ -121,6 +121,8 @@ class VerifyDiagnosticConsumer: public DiagnosticConsumer,
   struct ParsingState {
     DirectiveStatus Status;
     std::string FirstNoDiagnosticsDirective;
+    bool AllDirectivesMatchExactlyOneDiag = true;
+    bool WildcardsAreErroneouslyPresent = false;
   };
 
   class MarkerTracker;
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index eed799890ce41..a54be5a12342d 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -570,7 +570,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
           if (DisableWildcardInDiagLoc) {
             Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                          diag::err_verify_wildcard_loc);
-            continue;
+            State.WildcardsAreErroneouslyPresent = true;
           }
           MatchAnyFileAndLine = true;
           if (!PH.Next("*")) {
@@ -603,7 +603,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
             if (DisableWildcardInDiagLoc) {
               Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                            diag::err_verify_wildcard_loc);
-              continue;
+              State.WildcardsAreErroneouslyPresent = true;
             }
             MatchAnyLine = true;
             ExpectedLoc = SM.translateLineCol(FID, 1, 1);
@@ -613,7 +613,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
         if (DisableWildcardInDiagLoc) {
           Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                        diag::err_verify_wildcard_loc);
-          continue;
+          State.WildcardsAreErroneouslyPresent = true;
         }
         MatchAnyLine = true;
         ExpectedLoc = SourceLocation();
@@ -632,10 +632,10 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
 
     // Next optional token: positive integer or a '+'.
     if (PH.Next(D.Min)) {
-      if (OneDiagPerDirective && D.Min != 1) {
+      if (D.Min != 1 && OneDiagPerDirective) {
         Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                      diag::err_verify_non_singular_match);
-        continue;
+        State.AllDirectivesMatchExactlyOneDiag = false;
       }
       PH.Advance();
       // A positive integer can be followed by a '+' meaning min
@@ -646,7 +646,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
         if (OneDiagPerDirective) {
           Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                        diag::err_verify_non_singular_match);
-          continue;
+          State.AllDirectivesMatchExactlyOneDiag = false;
         }
       } else if (PH.Next("-")) {
         PH.Advance();
@@ -658,7 +658,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
         if (OneDiagPerDirective && D.Max != 1) {
           Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                        diag::err_verify_non_singular_match);
-          continue;
+          State.AllDirectivesMatchExactlyOneDiag = false;
         }
         PH.Advance();
       } else {
@@ -669,7 +669,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
       if (OneDiagPerDirective) {
         Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
                      diag::err_verify_non_singular_match);
-        continue;
+        State.AllDirectivesMatchExactlyOneDiag = false;
       }
       D.Max = Directive::MaxCount;
       PH.Advance();
@@ -1150,9 +1150,11 @@ static unsigned CheckResults(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
 }
 
 // Checks that directives are lexically in the same order as the emitted
-// diagnostics. Assumes that CheckResults returned 0 problems, i.e. that
-// every diagnostic was matched by every directive without considering the
-// order.
+// diagnostics. Assumes that:
+//   - every directive matches exactly one diagnostic,
+//   - there are no wildcards, and
+//   - CheckResults returned 0 problems, i.e. every diagnostic
+//     was matched by every directive without considering the order.
 static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
                                        SourceManager &SourceMgr,
                                        const TextDiagnosticBuffer &Buffer,
@@ -1228,13 +1230,13 @@ static unsigned CheckResultsAreInOrder(DiagnosticsEngine &Diags,
   int NumProblems = 0;
   SmallString<256> Fmt;
   llvm::raw_svector_ostream OS(Fmt);
-  // CheckResults already ensured that there are as many directives as emitted
-  // diagnostics, and that all of them match.
+  // zip_equal asserts that there're as many directives as emitted diagnostics.
+  // CheckResults has already ensured that all diagnostics were matched.
   for (const auto [Directive, LevelDiagPair] : llvm::zip_equal(
            OrderedDirectives,
            llvm::iterator_range{Buffer.all_begin(), Buffer.all_end()})) {
     assert(!Directive->MatchAnyFileAndLine && !Directive->MatchAnyLine &&
-           "Wildcards should not be allowed in strict verify mode!");
+           "Cannot compare source locations when wildcards are present");
     const auto [DiagLevel, DiagIndex] = LevelDiagPair;
     const auto &[DiagLoc, DiagText] = getLocDiagPair(DiagLevel, DiagIndex);
     const SourceLocation DirLoc = Directive->DirectiveLoc;
@@ -1367,9 +1369,14 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
 
     // Check that the expected diagnostics occurred.
     NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED);
-    if (CheckOrderOfDirectives && NumErrors == 0) {
-      assert(OneDiagPerDirective && "Can't check order of directives unless "
-                                    "they match only one diagnostic!");
+
+    // If either wildcards are present or there are directives that
+    // do not match exactly one diagnostic, we already issued
+    // errors about that. In such case we cannot check that directives
+    // are in order.
+    if (CheckOrderOfDirectives && NumErrors == 0 &&
+        State.AllDirectivesMatchExactlyOneDiag &&
+        !State.WildcardsAreErroneouslyPresent) {
       NumErrors += CheckResultsAreInOrder(Diags, *SrcManager, *Buffer, ED);
     }
   } else {
diff --git a/clang/test/Frontend/verify-strict-one-diag.cpp b/clang/test/Frontend/verify-strict-one-diag.cpp
index 1888ceb549f49..700c1100507d6 100644
--- a/clang/test/Frontend/verify-strict-one-diag.cpp
+++ b/clang/test/Frontend/verify-strict-one-diag.cpp
@@ -8,51 +8,40 @@ void f2(A, A);
 // expected-error at -1 2 {{unknown type name 'A'}}
 
 // CHECK:      error: 'expected-error' diagnostics seen but not expected:
-// CHECK-NEXT:   Line 7: unknown type name 'A'
-// CHECK-NEXT:   Line 7: unknown type name 'A'
 // CHECK-NEXT:   Line 8: exactly one diagnostic can be matched
 
 void f3(A, A);
 // expected-error at -1 0-1 {{unknown type name 'A'}}
 // expected-error at -2 0-1 {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 15: unknown type name 'A'
-// CHECK-NEXT:   Line 15: unknown type name 'A'
-// CHECK-NEXT:   Line 16: exactly one diagnostic can be matched
-// CHECK-NEXT:   Line 17: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 14: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 15: exactly one diagnostic can be matched
 
 void f4(A, A);
 // expected-error at -1 1-2 {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 24: unknown type name 'A'
-// CHECK-NEXT:   Line 24: unknown type name 'A'
-// CHECK-NEXT:   Line 25: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 21: exactly one diagnostic can be matched
 
 void f5(A);
 // expected-error at -1 0-2 {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 31: unknown type name 'A'
-// CHECK-NEXT:   Line 32: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 26: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 26: exactly one diagnostic can be matched
 
 void f6(A, A);
 // expected-error at -1 + {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 37: unknown type name 'A'
-// CHECK-NEXT:   Line 37: unknown type name 'A'
-// CHECK-NEXT:   Line 38: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 32: exactly one diagnostic can be matched
 
 void f7(A, A);
 // expected-error at -1 0+ {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 44: unknown type name 'A'
-// CHECK-NEXT:   Line 44: unknown type name 'A'
-// CHECK-NEXT:   Line 45: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 37: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 37: exactly one diagnostic can be matched
 
 void f8(A, A);
 // expected-error at -1 1+ {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 51: unknown type name 'A'
-// CHECK-NEXT:   Line 51: unknown type name 'A'
-// CHECK-NEXT:   Line 52: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 43: exactly one diagnostic can be matched
 
-// CHECK-NEXT: 21 errors generated.
+// CHECK-NEXT: 10 errors generated.
diff --git a/clang/test/Frontend/verify-strict-wildcard.cpp b/clang/test/Frontend/verify-strict-wildcard.cpp
index 64069ebced5e9..0865a23cf8b2d 100644
--- a/clang/test/Frontend/verify-strict-wildcard.cpp
+++ b/clang/test/Frontend/verify-strict-wildcard.cpp
@@ -4,19 +4,11 @@ void f1(A);
 // expected-error@* {{unknown type name 'A'}}
 
 // CHECK:      error: 'expected-error' diagnostics seen but not expected:
-// CHECK-NEXT:   Line 3: unknown type name 'A'
 // CHECK-NEXT:   Line 4: cannot use wildcard for diagnostic location
 
 void f2(A);
-// expected-error@*:6 {{unknown type name 'A'}}
-
-// CHECK-NEXT:   Line 10: unknown type name 'A'
-// CHECK-NEXT:   Line 11: cannot use wildcard for diagnostic location
-
-void f3(A);
 // expected-error@*:* {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 16: unknown type name 'A'
-// CHECK-NEXT:   Line 17: cannot use wildcard for diagnostic location
+// CHECK-NEXT:   Line 10: cannot use wildcard for diagnostic location
 
-// CHECK-NEXT: 6 errors generated.
+// CHECK-NEXT: 2 errors generated.

>From 1458933bbb7f7f4309f216ce1efd41fc9d81eed0 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 31 Jan 2026 14:14:49 +0300
Subject: [PATCH 22/30] Don't issue diagnostic about non-singular match more
 than once for a given directive

---
 .../lib/Frontend/VerifyDiagnosticConsumer.cpp | 26 +++++++++----------
 .../test/Frontend/verify-strict-one-diag.cpp  | 10 +++----
 2 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index a54be5a12342d..de7cb0a56570e 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -630,12 +630,12 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
     // Skip optional whitespace.
     PH.SkipWhitespace();
 
+    std::optional<std::ptrdiff_t> NonSingularMatchDiagOffset;
+
     // Next optional token: positive integer or a '+'.
     if (PH.Next(D.Min)) {
-      if (D.Min != 1 && OneDiagPerDirective) {
-        Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
-                     diag::err_verify_non_singular_match);
-        State.AllDirectivesMatchExactlyOneDiag = false;
+      if (OneDiagPerDirective && D.Min != 1) {
+        NonSingularMatchDiagOffset = PH.C - PH.Begin;
       }
       PH.Advance();
       // A positive integer can be followed by a '+' meaning min
@@ -644,9 +644,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
         D.Max = Directive::MaxCount;
         PH.Advance();
         if (OneDiagPerDirective) {
-          Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
-                       diag::err_verify_non_singular_match);
-          State.AllDirectivesMatchExactlyOneDiag = false;
+          NonSingularMatchDiagOffset = PH.C - PH.Begin;
         }
       } else if (PH.Next("-")) {
         PH.Advance();
@@ -656,9 +654,7 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
           continue;
         }
         if (OneDiagPerDirective && D.Max != 1) {
-          Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
-                       diag::err_verify_non_singular_match);
-          State.AllDirectivesMatchExactlyOneDiag = false;
+          NonSingularMatchDiagOffset = PH.C - PH.Begin;
         }
         PH.Advance();
       } else {
@@ -667,14 +663,18 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
     } else if (PH.Next("+")) {
       // '+' on its own means "1 or more".
       if (OneDiagPerDirective) {
-        Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin),
-                     diag::err_verify_non_singular_match);
-        State.AllDirectivesMatchExactlyOneDiag = false;
+        NonSingularMatchDiagOffset = PH.C - PH.Begin;
       }
       D.Max = Directive::MaxCount;
       PH.Advance();
     }
 
+    if (NonSingularMatchDiagOffset) {
+      Diags.Report(Pos.getLocWithOffset(*NonSingularMatchDiagOffset),
+                   diag::err_verify_non_singular_match);
+      State.AllDirectivesMatchExactlyOneDiag = false;
+    }
+
     // Skip optional whitespace.
     PH.SkipWhitespace();
 
diff --git a/clang/test/Frontend/verify-strict-one-diag.cpp b/clang/test/Frontend/verify-strict-one-diag.cpp
index 700c1100507d6..3f2152306462b 100644
--- a/clang/test/Frontend/verify-strict-one-diag.cpp
+++ b/clang/test/Frontend/verify-strict-one-diag.cpp
@@ -25,23 +25,21 @@ void f4(A, A);
 void f5(A);
 // expected-error at -1 0-2 {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 26: exactly one diagnostic can be matched
 // CHECK-NEXT:   Line 26: exactly one diagnostic can be matched
 
 void f6(A, A);
 // expected-error at -1 + {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 32: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 31: exactly one diagnostic can be matched
 
 void f7(A, A);
 // expected-error at -1 0+ {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 37: exactly one diagnostic can be matched
-// CHECK-NEXT:   Line 37: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 36: exactly one diagnostic can be matched
 
 void f8(A, A);
 // expected-error at -1 1+ {{unknown type name 'A'}}
 
-// CHECK-NEXT:   Line 43: exactly one diagnostic can be matched
+// CHECK-NEXT:   Line 41: exactly one diagnostic can be matched
 
-// CHECK-NEXT: 10 errors generated.
+// CHECK-NEXT: 8 errors generated.

>From 593492afe984c675a51ae619c59a5ab92fea1e70 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 31 Jan 2026 14:39:06 +0300
Subject: [PATCH 23/30] Rename '-verify-strict' to `-verify-directives`

---
 clang/include/clang/Basic/DiagnosticOptions.def     |  4 ++--
 clang/include/clang/Options/Options.td              |  6 +++---
 clang/lib/Frontend/CompilerInvocation.cpp           |  6 +++---
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp     | 13 +++++--------
 ...l-match.cpp => verify-directives-full-match.cpp} |  2 +-
 ...-one-diag.cpp => verify-directives-one-diag.cpp} |  2 +-
 ...strict-order.cpp => verify-directives-order.cpp} |  2 +-
 ...-wildcard.cpp => verify-directives-wildcard.cpp} |  2 +-
 8 files changed, 17 insertions(+), 20 deletions(-)
 rename clang/test/Frontend/{verify-strict-full-match.cpp => verify-directives-full-match.cpp} (81%)
 rename clang/test/Frontend/{verify-strict-one-diag.cpp => verify-directives-one-diag.cpp} (94%)
 rename clang/test/Frontend/{verify-strict-order.cpp => verify-directives-order.cpp} (90%)
 rename clang/test/Frontend/{verify-strict-wildcard.cpp => verify-directives-wildcard.cpp} (83%)

diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def
index 960d23b31dfb7..17d518c2b7fdd 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.def
+++ b/clang/include/clang/Basic/DiagnosticOptions.def
@@ -76,8 +76,8 @@ ENUM_DIAGOPT(VerifyIgnoreUnexpected, DiagnosticLevelMask, 4,
              DiagnosticLevelMask::None) /// Ignore unexpected diagnostics of
                                         /// the specified levels when using
                                         /// -verify.
-DIAGOPT(VerifyDiagnosticsStrict, 1, 0) /// Enable additional checks of
-                                       /// directives to improve readability
+DIAGOPT(VerifyDirectives, 1, 0) /// Enable checks of 'expected' directives
+                                /// themselves.
 DIAGOPT(ElideType, 1, 0)         /// Elide identical types in template diffing
 DIAGOPT(ShowTemplateTree, 1, 0)  /// Print a template tree when diffing
 
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index c9f688c7c092f..301de5fcbed2f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -8322,10 +8322,10 @@ def verify_ignore_unexpected : Flag<["-"], "verify-ignore-unexpected">,
   HelpText<"Ignore unexpected diagnostic messages">;
 def verify_ignore_unexpected_EQ : CommaJoined<["-"], "verify-ignore-unexpected=">,
   HelpText<"Ignore unexpected diagnostic messages">;
-def verify_strict
-    : Flag<["-"], "verify-strict">,
+def verify_directives
+    : Flag<["-"], "verify-directives">,
       HelpText<
-          "Enable additional checks on directives that improve readability">;
+          "Enable additional checks on 'expected' directives themselves">;
 def Wno_rewrite_macros : Flag<["-"], "Wno-rewrite-macros">,
   HelpText<"Silence ObjC rewriting warnings">,
   MarshallingInfoFlag<DiagnosticOpts<"NoRewriteMacros">>;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 99f9fb2e3a5d0..73cbeeeb243f1 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2540,8 +2540,8 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
     if (Prefix != "expected")
       GenerateArg(Consumer, OPT_verify_EQ, Prefix);
 
-  if (Opts.VerifyDiagnosticsStrict) {
-    GenerateArg(Consumer, OPT_verify_strict);
+  if (Opts.VerifyDirectives) {
+    GenerateArg(Consumer, OPT_verify_directives);
   }
 
   DiagnosticLevelMask VIU = Opts.getVerifyIgnoreUnexpected();
@@ -2642,7 +2642,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
   Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor);
 
   Opts.VerifyDiagnostics = Args.hasArg(OPT_verify) || Args.hasArg(OPT_verify_EQ);
-  Opts.VerifyDiagnosticsStrict = Args.hasArg(OPT_verify_strict);
+  Opts.VerifyDirectives = Args.hasArg(OPT_verify_directives);
   Opts.VerifyPrefixes = Args.getAllArgValues(OPT_verify_EQ);
   if (Args.hasArg(OPT_verify))
     Opts.VerifyPrefixes.push_back("expected");
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index de7cb0a56570e..4d99f0620d58c 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -325,7 +325,7 @@ void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
   std::unique_ptr<Directive> D = Directive::create(
       UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
       MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max,
-      Diags.getDiagnosticOptions().VerifyDiagnosticsStrict);
+      Diags.getDiagnosticOptions().VerifyDirectives);
 
   std::string Error;
   if (!D->isValid(Error)) {
@@ -741,10 +741,10 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
       State{HasNoDirectives, {}} {
   if (Diags.hasSourceManager())
     setSourceManager(Diags.getSourceManager());
-  CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
-  OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
+  CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDirectives;
+  OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDirectives;
   DisableWildcardInDiagLoc =
-      Diags.getDiagnosticOptions().VerifyDiagnosticsStrict;
+      Diags.getDiagnosticOptions().VerifyDirectives;
 }
 
 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {
@@ -1023,8 +1023,7 @@ static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc,
   return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc)));
 }
 
-/// Takes a list of diagnostics that were partially matched,
-/// despite '-verify-strict' option being set.
+/// Takes a list of diagnostics that were partially matched and prints them.
 static unsigned
 PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
              llvm::SmallVector<std::pair<Directive *, std::string>> &DL,
@@ -1035,8 +1034,6 @@ PrintPartial(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
   SmallString<256> Fmt;
   llvm::raw_svector_ostream OS(Fmt);
   for (const auto &[D, DiagText] : DL) {
-    assert(!D->MatchAnyLine && !D->MatchAnyFileAndLine &&
-           "Wildcards should not be allowed in strict verify mode");
     OS << "\n  '" << D->Spelling << "' at line "
        << SourceMgr.getPresumedLineNumber(D->DirectiveLoc) << " in "
        << SourceMgr.getFilename(D->DiagnosticLoc) << ": " << D->Text
diff --git a/clang/test/Frontend/verify-strict-full-match.cpp b/clang/test/Frontend/verify-directives-full-match.cpp
similarity index 81%
rename from clang/test/Frontend/verify-strict-full-match.cpp
rename to clang/test/Frontend/verify-directives-full-match.cpp
index c84b1451a82af..ea769d0c256bd 100644
--- a/clang/test/Frontend/verify-strict-full-match.cpp
+++ b/clang/test/Frontend/verify-directives-full-match.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -verify -verify-directives %s 2>&1 | FileCheck %s
 
 void f1(A);
 // expected-error at -1 {{unknown type}}
diff --git a/clang/test/Frontend/verify-strict-one-diag.cpp b/clang/test/Frontend/verify-directives-one-diag.cpp
similarity index 94%
rename from clang/test/Frontend/verify-strict-one-diag.cpp
rename to clang/test/Frontend/verify-directives-one-diag.cpp
index 3f2152306462b..328c8cde23cbc 100644
--- a/clang/test/Frontend/verify-strict-one-diag.cpp
+++ b/clang/test/Frontend/verify-directives-one-diag.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -verify -verify-directives %s 2>&1 | FileCheck %s
 
 void f1(A, A);
 // expected-error at -1 {{unknown type name 'A'}}
diff --git a/clang/test/Frontend/verify-strict-order.cpp b/clang/test/Frontend/verify-directives-order.cpp
similarity index 90%
rename from clang/test/Frontend/verify-strict-order.cpp
rename to clang/test/Frontend/verify-directives-order.cpp
index 6e1dd877cb6b0..d1cc6b8d6291b 100644
--- a/clang/test/Frontend/verify-strict-order.cpp
+++ b/clang/test/Frontend/verify-directives-order.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -verify -verify-directives %s 2>&1 | FileCheck %s
 
 void f1(A); // #f1
 void f2(B); // #f2
diff --git a/clang/test/Frontend/verify-strict-wildcard.cpp b/clang/test/Frontend/verify-directives-wildcard.cpp
similarity index 83%
rename from clang/test/Frontend/verify-strict-wildcard.cpp
rename to clang/test/Frontend/verify-directives-wildcard.cpp
index 0865a23cf8b2d..a45a382356d97 100644
--- a/clang/test/Frontend/verify-strict-wildcard.cpp
+++ b/clang/test/Frontend/verify-directives-wildcard.cpp
@@ -1,4 +1,4 @@
-// RUN: not %clang_cc1 -verify -verify-strict %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -verify -verify-directives %s 2>&1 | FileCheck %s
 
 void f1(A);
 // expected-error@* {{unknown type name 'A'}}

>From d2a96f81d16227c4ebfe2e4fa7bcb263359dceab Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 4 Feb 2026 01:56:19 +0300
Subject: [PATCH 24/30] Add documentation

---
 clang/docs/InternalsManual.rst | 228 +++++++++++++++++++++++++++++++--
 1 file changed, 214 insertions(+), 14 deletions(-)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index 42004bcac56b2..d2bf4fdf4c365 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3451,22 +3451,40 @@ are similar.
 Testing
 -------
 All functional changes to Clang should come with test coverage demonstrating
-the change in behavior.
+the change in behavior. There are four kinds of such tests.
+
+The first kind is unit tests. Such tests are placed in ``clang/test/unittest``.
+
+The second kind of tests ensures that only specific diagnostics are emitted at
+specific source lines. Those tests are using ``-verify`` mode of ``-cc1``,
+which is described below in
+:ref:`"Verifying Diagnostics" <verifying-diagnostics>`. Additional
+provisions for tests for C++ defect reports are described in ... section. 
+
+The third kind of tests checks AST dump. Such tests pass AST dump to
+`FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
+which check presence of certain patterns (or lack of thereof).
+
+The fourth kind of tests checks LLVM IR output of Clang in cases when checking
+diagnostics is not sufficient (e.g. when testing exception handling or object
+lifetime). Such tests pass LLVM IR output to
+`FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
+which check presence of certain IR patterns (or lack of thereof).
 
 .. _verifying-diagnostics:
 
 Verifying Diagnostics
 ^^^^^^^^^^^^^^^^^^^^^
 Clang ``-cc1`` supports the ``-verify`` command line option as a way to
-validate diagnostic behavior. This option will use special comments within the
-test file to verify that expected diagnostics appear in the correct source
-locations. If all of the expected diagnostics match the actual output of Clang,
-then the invocation will return normally. If there are discrepancies between
-the expected and actual output, Clang will emit detailed information about
-which expected diagnostics were not seen or which unexpected diagnostics were
-seen, etc. A complete example is:
+validate diagnostic behavior. This option will use special comments, called
+directives, within the test file to verify that expected diagnostics appear in
+the correct source locations. If all of the expected diagnostics match the
+actual output of Clang, then the invocation will return normally. If there are
+discrepancies between the expected and actual output, Clang will emit detailed
+information about which expected diagnostics were not seen or which unexpected
+diagnostics were seen, etc. A complete example is:
 
-.. code-block: c++
+.. code-block:: c++
 
   // RUN: %clang_cc1 -verify %s
   int A = B; // expected-error {{use of undeclared identifier 'B'}}
@@ -3476,6 +3494,42 @@ diagnostic verifier will pass. However, if the expected error does not appear
 or appears in a different location than expected, or if additional diagnostics
 appear, the diagnostic verifier will fail and emit information as to why.
 
+Directive Syntax
+~~~~~~~~~~~~~~~~
+EBNF syntax description of the directives is the following:
+
+.. productionlist:: verify-grammar
+  directive: prefix , "-" , diagnostic-kind , [ regex-match ] , [ diagnotic-loc ] , [ " " , quantifier ] , "{" , [ delimiter-open ], "{", [ diagnostic-text ] , "}" , [ delimiter-close ], "}" ;
+  diagnostic-kind : "error" | "warning" | "note" | "remark" ;
+  regex-match : "-re" ;
+  diagnostic-loc : "@" , ( "+" | "-" ) , number
+                 : | "@" , line
+                 : | "@" , file-path , ":" , line
+                 : | "@" , "*" , [ ":" , "*" ]
+                 : | "@" , "#" , marker-name ;
+  line : number | "*" ;
+  number : digit, { digit } ;
+  quantifier : "+"
+             : | number , [ "+" ]
+             : | number , "-" , number ;
+  delimiter-open : { "{" } ;
+  delimiter-close : { "}" } ;
+
+Where:
+
+- ``prefix`` is "expected" or a custom string
+  (:ref:`Custom Prefixes <custom-prefixes>`).
+- ``delimiter-open`` and ``delimiter-close`` have to have the same length
+  (:ref:`Diagnostic Text <diagnostic-text>`).
+- ``file-path`` is relative or absolte path to a file
+  (:ref:`Diagnostic Location <diagnostic-location>`).
+- ``marker-name`` is name of the marker somewhere in the source
+  (:ref:`Diagnostic Location <diagnostic-location>`).
+- ``diagnostic-text`` is text of the expected diagnostic
+  (:ref:`Diagnostic Text <diagnostic-text>`).
+
+Custom Prefixes
+~~~~~~~~~~~~~~~
 The ``-verify`` command optionally accepts a comma-delimited list of one or
 more verification prefixes that can be used to craft those special comments.
 Each prefix must start with a letter and contain only alphanumeric characters,
@@ -3502,8 +3556,8 @@ Multiple occurrences accumulate prefixes.  For example,
 ``-verify -verify=foo,bar -verify=baz`` is equivalent to
 ``-verify=expected,foo,bar,baz``.
 
-Specifying Diagnostics
-^^^^^^^^^^^^^^^^^^^^^^
+Dianogstic Location
+^^^^^^^^^^^^^^^^^^^
 Indicating that a line expects an error or a warning is easy. Put a comment
 on the line that has the diagnostic, use
 ``expected-{error,warning,remark,note}`` to tag if it's an expected error,
@@ -3565,6 +3619,8 @@ appending the marker to the diagnostic with ``@#<marker>``, as with:
 
 The name of a marker used in a directive must be unique within the compilation.
 
+Quantifiers
+~~~~~~~~~~~
 The simple syntax above allows each specification to match exactly one
 diagnostic. You can use the extended syntax to customize this. The extended
 syntax is ``expected-<type> <n> {{diag text}}``, where ``<type>`` is one of
@@ -3603,9 +3659,8 @@ In this example, the diagnostic may appear only once, if at all.
 
 .. _DiagnosticMatching:
 
-Matching Modes
-~~~~~~~~~~~~~~
-
+Diagnostic Text
+~~~~~~~~~~~~~~~
 The default matching mode is simple string, which looks for the expected text
 that appears between the first `{{` and `}}` pair of the comment. The string is
 interpreted just as-is, with one exception: the sequence `\n` is converted to a
@@ -3647,6 +3702,151 @@ Examples matching error: "variable has incomplete type 'struct s'"
   // expected-error-re {{variable has type 'struct {{(.*)}}'}}
   // expected-error-re {{variable has type 'struct{{[[:space:]](.*)}}'}}
 
+Verifying the Directives
+~~~~~~~~~~~~~~~~~~~~~~~~
+Clang ``-cc1`` also has ``-verify-directives`` mode, which can be enabled
+alongside ``-verify`` to place additional restrictions on directives,
+and strives to improve readability of the expected compiler output
+for reviewers and future readers, but makes it a bit harder to write the test:
+
+- Diagnostic text specified in a directive has to be a full match.
+- Lexical order of directives has to match order in which diagnostics are
+  emitted.
+- Each directive can match exactly one diagnostic.
+- Wildcards (``*``) are not allowed in diagnostic location.
+
+It is recommended to write new tests with this mode enabled, but for some tests
+it's not possible. Typical problems and their solutions are listed below.
+
+- **Diagnostic is issued only sometimes.**
+  The cause of variance needs to be identified and captured. Typically it is
+  either a compiler option like language mode, in which case additional custom
+  prefix should be sufficient, or platform, in which case triple has to be
+  explicitly passed in command-line arguments.
+- **Template instantiations at the end of translation unit.**
+  Instantiations at the end of the TU cause associated diagnostics to appear
+  too late. Instantiations need to happen before the next directive, and when
+  it is possible, it still takes some creativity to achieve, especially in
+  C++98 mode. Typical solutions include explicit template instantiations,
+  manifestly constant-evaluated expressions, ``enable_if``, and removing
+  template parameters altogether.
+- **Analysis-based warnings.** Some warnings, like ``-Wunused``, are based
+  on CFG analysis and are emitted later than e.g. parser would emit them,
+  because some lookahead is required to collect all the necessary information
+  for such analysis. Not much can be done in this case.
+- **Lambdas.** When lambda is mentioned in diagnostic message, it is identified
+  by its source location, which is typically not salient for the test,
+  and would make it overspecified. In this case regex match can be used to skip
+  over source location.
+
+C++ Defect Report Tests
+^^^^^^^^^^^^^^^^^^^^^^^
+C++ Defect Report tests are placed in ``clang/test/CXX/drs`` directory, which
+consists of two types of files:
+- files where tests are grouped by 100 by issue number, e.g. ``cwg15xx.cpp``
+  ("big files");
+- files with individual tests or smaller groups of tests, e.g. ``cwg177x.cpp``
+  or ``cwg787.cpp``.
+
+C++ defect report tests are run in all language modes available via ``-std=``,
+except for modes with GNU extensions and ``c++03``, as it is synonymous to
+``c++98``. Exceptions, pedantic errors, and ``-verify-directives`` are enabled.
+
+Big files are where most of the tests end up being placed to save on process
+startup cost during test runs. Big files also serve as an index of tests:
+even if test is placed in its individual file, it's still mentioned in the
+big file where this test would be placed otherwise. Big files need all tests
+to work with the same set of compiler options specified in RUN lines, but
+sometimes tests need special provisions, in which case they should be placed
+in their own file. Typical reasons for that include:
+
+- **Test is checking AST dump or LLVM IR.** We don't yet know how
+  to concatenate FileCheck directives from multiple tests into a single file
+  without avoiding test interference. 
+- **Test is not compatible with ``-verify-directives``.** For instance, some
+  tests can't be rewritten to prevent instantiations at the end of TU.
+- **Test needs C++20 modules.** Such tests require special RUN lines to compile
+  modules in the right order.
+- **Test prevents compiler from checking subsequent tests.** In some cases
+  that involve templates Clang refuses to recover from expected errors and will
+  skip over expected errors in subsequent tests.
+
+C++ defect report tests make heavy use of markers. Marker names need to
+be prefixed with ``cwgNNN-``, where NNN is number of the core issue being
+tested. This is especially needed in big files, because all markers share
+the same namespace.
+
+Some diagnostics are expected only in certain language modes. This in handled
+in two parts. First, C++ defect report tests share a common pool of custom
+prefixes:
+- ``expected`` for diagnostics issued in all language modes;
+- ``cxxNN`` (e.g. ``cxx98``) for diagnostics issued only in one language mode;
+- ``since-cxxNN`` (e.g. ``since-cxx11``) for diagnostics that are issued
+  in a certain language mode and all newer language modes;
+- ``cxxNN-MM`` (e.g. ``cxx98-14``) for diagnostics that appear in all language
+  modes within a certain range.
+
+Second, parts of tests that test features not univerally available in all
+language modes are guarded with ``#if __cplusplus``. Prefixes of directives
+in such parts need to reflect the latest ``#if __cplusplus`` guard to make
+tests require less context to understand, even though this is not strictly
+necessary to make the test work:
+
+.. code-block:: c++
+
+  #if __cplusplus >= 201103L
+  enum : int { a };
+  enum class { b };
+  // since-cxx11-error at -1 {{scoped enumeration requires a name}}
+  #endif
+
+On top of `-verify-directives`, C++ defect report tests use the following
+conventions to make it easier to reconstruct compiler output while looking at
+directives:
+
+- Errors and warnings are placed on the next line after the line they are
+  expecting a diagnostic at, and at the same indentation.
+
+  .. code-block:: c++
+
+    namespace X::Y {}
+    // cxx98-14-error at -1 {{nested namespace definition is a C++17 extension; define each namespace separately}}
+    namespace X {
+      namespace X::Y {}
+      // cxx98-14-error at -1 {{nested namespace definition is a C++17 extension; define each namespace separately}}
+    }
+
+- Directives that are placed right after the line they are expecting
+  a diagnostic at use relative offsets (``@-1``, ``@-2``, and so on),
+  as long as it's obvious that all relative offsets point to the same line.
+  If there is a feeling readers would start counting lines to make sure
+  they know where the diagnostic is expected, markers should be used instead.
+
+  .. code-block:: c++
+
+    bool b = (void(*)(S, S))operator- < (void(*)(S, S))operator-;
+    // cxx98-17-warning at -1 {{ordered comparison of function pointers ('void (*)(S, S)' and 'void (*)(S, S)')}}
+    // cxx20-23-error at -2 {{expected '>'}}
+    //   cxx20-23-note at -3 {{to match this '<'}}
+
+    int *q = new int[T()]; // #cwg299-q
+    // cxx98-11-error@#cwg299-q {{ambiguous conversion of array size expression of type 'T' to an integral or enumeration type}}
+    //   cxx98-11-note@#cwg299-int {{conversion to integral type 'int' declared here}}
+    //   cxx98-11-note@#cwg299-ushort {{conversion to integral type 'unsigned short' declared here}}
+    // since-cxx14-error-re@#cwg299-q {{conversion from 'T' to '__size_t' (aka 'unsigned {{long long|long|int}}') is ambiguous}}
+    //   since-cxx14-note@#cwg299-int {{candidate function}}
+    //   since-cxx14-note@#cwg299-ushort {{candidate function}}
+
+- Notes and remarks are indented by two spaces relative to the error or warning
+  they are attached to.
+
+  .. code-block:: c++
+
+    void (*p)() throw(int) = &f; // #cwg92-p
+    // since-cxx17-error@#cwg92-p {{ISO C++17 does not allow dynamic exception specifications}}
+    //   since-cxx17-note@#cwg92-p {{use 'noexcept(false)' instead}}
+    // cxx98-14-error@#cwg92-p {{target exception specification is not superset of source}}
+
 Feature Test Macros
 ===================
 Clang implements several ways to test whether a feature is supported or not.

>From 27a88b2437ac4c6ff92116fdd0c0190546f291c3 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 5 Feb 2026 02:34:52 +0300
Subject: [PATCH 25/30] Run clang-format

---
 clang/include/clang/Options/Options.td          |  3 +--
 clang/lib/Frontend/VerifyDiagnosticConsumer.cpp | 11 +++++------
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 301de5fcbed2f..8238d220a5d60 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -8324,8 +8324,7 @@ def verify_ignore_unexpected_EQ : CommaJoined<["-"], "verify-ignore-unexpected="
   HelpText<"Ignore unexpected diagnostic messages">;
 def verify_directives
     : Flag<["-"], "verify-directives">,
-      HelpText<
-          "Enable additional checks on 'expected' directives themselves">;
+      HelpText<"Enable additional checks on 'expected' directives themselves">;
 def Wno_rewrite_macros : Flag<["-"], "Wno-rewrite-macros">,
   HelpText<"Silence ObjC rewriting warnings">,
   MarshallingInfoFlag<DiagnosticOpts<"NoRewriteMacros">>;
diff --git a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
index 4d99f0620d58c..2fc24dc1c9680 100644
--- a/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
+++ b/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
@@ -322,10 +322,10 @@ void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD,
                      bool MatchAnyFileAndLine = false,
                      bool MatchAnyLine = false) {
   // Construct new directive.
-  std::unique_ptr<Directive> D = Directive::create(
-      UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
-      MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min, UD.Max,
-      Diags.getDiagnosticOptions().VerifyDirectives);
+  std::unique_ptr<Directive> D =
+      Directive::create(UD.RegexKind, UD.DirectivePos, ExpectedLoc, UD.Spelling,
+                        MatchAnyFileAndLine, MatchAnyLine, UD.Text, UD.Min,
+                        UD.Max, Diags.getDiagnosticOptions().VerifyDirectives);
 
   std::string Error;
   if (!D->isValid(Error)) {
@@ -743,8 +743,7 @@ VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_)
     setSourceManager(Diags.getSourceManager());
   CheckOrderOfDirectives = Diags.getDiagnosticOptions().VerifyDirectives;
   OneDiagPerDirective = Diags.getDiagnosticOptions().VerifyDirectives;
-  DisableWildcardInDiagLoc =
-      Diags.getDiagnosticOptions().VerifyDirectives;
+  DisableWildcardInDiagLoc = Diags.getDiagnosticOptions().VerifyDirectives;
 }
 
 VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() {

>From bfdf4fa5efb22a25b274c4d7d6a6c76764185d7b Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 5 Feb 2026 05:03:13 +0300
Subject: [PATCH 26/30] Add missing newlines in docs

---
 clang/docs/InternalsManual.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index d2bf4fdf4c365..fc75b3ae424d7 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3743,6 +3743,7 @@ C++ Defect Report Tests
 ^^^^^^^^^^^^^^^^^^^^^^^
 C++ Defect Report tests are placed in ``clang/test/CXX/drs`` directory, which
 consists of two types of files:
+
 - files where tests are grouped by 100 by issue number, e.g. ``cwg15xx.cpp``
   ("big files");
 - files with individual tests or smaller groups of tests, e.g. ``cwg177x.cpp``
@@ -3779,6 +3780,7 @@ the same namespace.
 Some diagnostics are expected only in certain language modes. This in handled
 in two parts. First, C++ defect report tests share a common pool of custom
 prefixes:
+
 - ``expected`` for diagnostics issued in all language modes;
 - ``cxxNN`` (e.g. ``cxx98``) for diagnostics issued only in one language mode;
 - ``since-cxxNN`` (e.g. ``since-cxx11``) for diagnostics that are issued

>From d949da6dce3ce5a0515097ebdc014bdd5a752cd2 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 5 Feb 2026 19:43:44 +0400
Subject: [PATCH 27/30] Update clang/docs/InternalsManual.rst

Co-authored-by: Erich Keane <ekeane at nvidia.com>
---
 clang/docs/InternalsManual.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index fc75b3ae424d7..32afc62819742 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3788,7 +3788,7 @@ prefixes:
 - ``cxxNN-MM`` (e.g. ``cxx98-14``) for diagnostics that appear in all language
   modes within a certain range.
 
-Second, parts of tests that test features not univerally available in all
+Second, parts of tests that test features not universally available in all
 language modes are guarded with ``#if __cplusplus``. Prefixes of directives
 in such parts need to reflect the latest ``#if __cplusplus`` guard to make
 tests require less context to understand, even though this is not strictly

>From 1aa4217d3438be881789a7baf0846de21c11f3b3 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 7 Feb 2026 17:09:49 +0300
Subject: [PATCH 28/30] Fix internal references in the docs

---
 clang/docs/InternalsManual.rst | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index 32afc62819742..2a368bf98b28f 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3528,6 +3528,8 @@ Where:
 - ``diagnostic-text`` is text of the expected diagnostic
   (:ref:`Diagnostic Text <diagnostic-text>`).
 
+.. _custom-prefixes:
+
 Custom Prefixes
 ~~~~~~~~~~~~~~~
 The ``-verify`` command optionally accepts a comma-delimited list of one or
@@ -3556,6 +3558,8 @@ Multiple occurrences accumulate prefixes.  For example,
 ``-verify -verify=foo,bar -verify=baz`` is equivalent to
 ``-verify=expected,foo,bar,baz``.
 
+.. _diagnostic-location:
+
 Dianogstic Location
 ^^^^^^^^^^^^^^^^^^^
 Indicating that a line expects an error or a warning is easy. Put a comment
@@ -3568,7 +3572,7 @@ should be included in test cases unless there is a compelling reason to use
 truncated text instead.)
 
 For a full description of the matching behavior, including more complex
-matching scenarios, see :ref:`matching <DiagnosticMatching>` below.
+matching scenarios, see :ref:`"Diagnostic text" <diagnostic-text>` below.
 
 Here's an example of the most commonly used way to specify expected
 diagnostics:
@@ -3657,7 +3661,7 @@ A range can also be specified by ``<n>-<m>``. For example:
 
 In this example, the diagnostic may appear only once, if at all.
 
-.. _DiagnosticMatching:
+.. _diagnostic-text:
 
 Diagnostic Text
 ~~~~~~~~~~~~~~~

>From c292eb7a7cf51b4d05d9b97ec61b4f7eb7ab391a Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 7 Feb 2026 18:20:09 +0300
Subject: [PATCH 29/30] Address feedback on documentation

---
 clang/docs/InternalsManual.rst | 45 ++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index 2a368bf98b28f..591e1613e2a8e 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3451,25 +3451,23 @@ are similar.
 Testing
 -------
 All functional changes to Clang should come with test coverage demonstrating
-the change in behavior. There are four kinds of such tests.
-
-The first kind is unit tests. Such tests are placed in ``clang/test/unittest``.
-
-The second kind of tests ensures that only specific diagnostics are emitted at
-specific source lines. Those tests are using ``-verify`` mode of ``-cc1``,
-which is described below in
-:ref:`"Verifying Diagnostics" <verifying-diagnostics>`. Additional
-provisions for tests for C++ defect reports are described in ... section. 
-
-The third kind of tests checks AST dump. Such tests pass AST dump to
-`FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
-which check presence of certain patterns (or lack of thereof).
-
-The fourth kind of tests checks LLVM IR output of Clang in cases when checking
-diagnostics is not sufficient (e.g. when testing exception handling or object
-lifetime). Such tests pass LLVM IR output to
-`FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
-which check presence of certain IR patterns (or lack of thereof).
+the change in behavior. There are four kinds of tests:
+
+* Unit tests: such tests are placed in ``clang/test/unittest``.
+* Diagnostic tests: such tests ensures that only specific diagnostics are
+  emitted at specific source lines. Those tests are using ``-verify`` mode of
+  ``-cc1``, which is described below in
+  :ref:`"Verifying Diagnostics" <verifying-diagnostics>`. Additional
+  provisions for tests for C++ defect reports are described in ... section. 
+* AST dump tests: such tests pass AST dump to
+  `FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
+  which check presence of certain patterns (or lack of thereof).
+* LLVM IR tests: in such tests LLVM IR output of Clang is checked, which is
+  needed in cases when checking diagnostics is not sufficient (e.g. when
+  testing exception handling or object lifetime). Such tests pass LLVM IR
+  output to
+  `FileCheck <https://llvm.org/docs/CommandGuide/FileCheck.html>`_ utility,
+  which check presence of certain IR patterns (or lack of thereof).
 
 .. _verifying-diagnostics:
 
@@ -3528,6 +3526,15 @@ Where:
 - ``diagnostic-text`` is text of the expected diagnostic
   (:ref:`Diagnostic Text <diagnostic-text>`).
 
+Examples:
+
+- ``// expected-note {{declared here}}``
+- ``// expected-error at +1 0-1 {{{expected identifier or '{'}}}``
+- ``// cxx98-17-warning@#func-decl + {{target exception specification is not superset of source}}``
+- ``// expected-note@* 1 {{file entered}}``
+- ``// expected-note at decls.h:2 {{previous declaration is here}}``
+- ``// cxx11-17-error-re at -1 {{no matching constructor for initialization of 'A' (aka '(lambda at {{.+}})')}}`
+
 .. _custom-prefixes:
 
 Custom Prefixes

>From feb9aaaee8321ce990d5bb3b1c7c88adc2c8aef8 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sat, 7 Feb 2026 18:36:20 +0300
Subject: [PATCH 30/30] Fix documentation build error

---
 clang/docs/InternalsManual.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst
index 591e1613e2a8e..2d87827d8ea08 100644
--- a/clang/docs/InternalsManual.rst
+++ b/clang/docs/InternalsManual.rst
@@ -3533,7 +3533,7 @@ Examples:
 - ``// cxx98-17-warning@#func-decl + {{target exception specification is not superset of source}}``
 - ``// expected-note@* 1 {{file entered}}``
 - ``// expected-note at decls.h:2 {{previous declaration is here}}``
-- ``// cxx11-17-error-re at -1 {{no matching constructor for initialization of 'A' (aka '(lambda at {{.+}})')}}`
+- ``// cxx11-17-error-re at -1 {{no matching constructor for initialization of 'A' (aka '(lambda at {{.+}})')}}``
 
 .. _custom-prefixes:
 



More information about the cfe-commits mailing list