[clang-tools-extra] [clangd] Replay macro definitions from preamble for clang-tidy checks (PR #202495)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 8 20:53:21 PDT 2026
https://github.com/QixiQAQ updated https://github.com/llvm/llvm-project/pull/202495
>From 89d9e739344012387ec4259c62681468ddd038a0 Mon Sep 17 00:00:00 2001
From: tongjinxuan <tongjinxuan at longcheer.com>
Date: Tue, 9 Jun 2026 11:32:45 +0800
Subject: [PATCH] [clangd] Replay macro definitions from preamble for
clang-tidy checks
Clang-tidy checkers observe preprocessor events via PPCallbacks.
When using a preamble, macro definitions in the preamble region of
the main file are not replayed during the main-file build, causing
checkers like bugprone-reserved-identifier to miss them.
This patch extends ReplayPreamble::replay() to also replay MacroDefined
events for macros defined directly in the preamble region of the open
file, similar to how InclusionDirective events are already replayed.
Fixes: https://github.com/clangd/clangd/issues/2501
Assisted-by: Claude (Anthropic)
---
clang-tools-extra/clangd/ParsedAST.cpp | 43 ++++++++++++++++++++------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index e2a49f384a3e9..728b223013f6d 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -142,16 +142,18 @@ class ReplayPreamble : private PPCallbacks {
// Attach preprocessor hooks such that preamble events will be injected at
// the appropriate time.
// Events will be delivered to the *currently registered* PP callbacks.
- static void attach(std::vector<Inclusion> Includes, CompilerInstance &Clang,
- const PreambleBounds &PB) {
+ static void attach(std::vector<Inclusion> Includes,
+ const MainFileMacros &Macros,
+ CompilerInstance &Clang,
+ const PreambleBounds &PB) {
auto &PP = Clang.getPreprocessor();
auto *ExistingCallbacks = PP.getPPCallbacks();
// No need to replay events if nobody is listening.
if (!ExistingCallbacks)
return;
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(new ReplayPreamble(
- std::move(Includes), ExistingCallbacks, Clang.getSourceManager(), PP,
- Clang.getLangOpts(), PB)));
+ std::move(Includes), Macros, ExistingCallbacks,
+ Clang.getSourceManager(), PP, Clang.getLangOpts(), PB)));
// We're relying on the fact that addPPCallbacks keeps the old PPCallbacks
// around, creating a chaining wrapper. Guard against other implementations.
assert(PP.getPPCallbacks() != ExistingCallbacks &&
@@ -159,10 +161,12 @@ class ReplayPreamble : private PPCallbacks {
}
private:
- ReplayPreamble(std::vector<Inclusion> Includes, PPCallbacks *Delegate,
- const SourceManager &SM, Preprocessor &PP,
- const LangOptions &LangOpts, const PreambleBounds &PB)
- : Includes(std::move(Includes)), Delegate(Delegate), SM(SM), PP(PP) {
+ReplayPreamble(std::vector<Inclusion> Includes, const MainFileMacros &Macros,
+ PPCallbacks *Delegate, const SourceManager &SM,
+ Preprocessor &PP, const LangOptions &LangOpts,
+ const PreambleBounds &PB)
+ : Includes(std::move(Includes)), Macros(Macros), Delegate(Delegate),
+ SM(SM), PP(PP) {
// Only tokenize the preamble section of the main file, as we are not
// interested in the rest of the tokens.
MainFileTokens = syntax::tokenize(
@@ -193,6 +197,24 @@ class ReplayPreamble : private PPCallbacks {
}
void replay() {
+ // Replay macro definitions from the preamble region of the main file,
+ // so that clang-tidy checks can observe them.
+ for (const auto &[SID, Refs] : Macros.MacroRefs) {
+ for (const auto &Ref : Refs) {
+ if (!Ref.IsDefinition)
+ continue;
+ auto Loc = SM.getComposedLoc(SM.getMainFileID(), Ref.StartOffset);
+ Token Tok;
+ if (Lexer::getRawToken(Loc, Tok, SM, PP.getLangOpts(), false))
+ continue;
+ if (auto *II = PP.getIdentifierInfo(Tok.getRawIdentifier())) {
+ Tok.setIdentifierInfo(II);
+ Tok.setKind(tok::identifier);
+ if (auto *MD = PP.getLocalMacroDirective(II))
+ Delegate->MacroDefined(Tok, MD);
+ }
+ }
+ }
for (const auto &Inc : Includes) {
OptionalFileEntryRef File;
if (Inc.Resolved != "")
@@ -250,6 +272,7 @@ class ReplayPreamble : private PPCallbacks {
}
const std::vector<Inclusion> Includes;
+ const MainFileMacros &Macros;
PPCallbacks *Delegate;
const SourceManager &SM;
Preprocessor &PP;
@@ -667,8 +690,8 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
Includes = Preamble->Includes;
Includes.MainFileIncludes = Patch->preambleIncludes();
// Replay the preamble includes so that clang-tidy checks can see them.
- ReplayPreamble::attach(Patch->preambleIncludes(), *Clang,
- Patch->modifiedBounds());
+ ReplayPreamble::attach(Patch->preambleIncludes(), Patch->mainFileMacros(),
+ *Clang, Patch->modifiedBounds());
PI = *Preamble->Pragmas;
}
// Important: collectIncludeStructure is registered *after* ReplayPreamble!
More information about the cfe-commits
mailing list