[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