Skip to content

Commit ee6961d

Browse files
[clangd] Do not collect macros when clang-tidy checks call into the preprocessor (#106329)
Fixes #99617
1 parent 121fb2c commit ee6961d

File tree

4 files changed

+33
-1
lines changed

4 files changed

+33
-1
lines changed

clang-tools-extra/clangd/CollectMacros.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ void CollectMainFileMacros::add(const Token &MacroNameTok, const MacroInfo *MI,
3232
if (Loc.isInvalid() || Loc.isMacroID())
3333
return;
3434

35+
assert(isInsideMainFile(Loc, SM));
3536
auto Name = MacroNameTok.getIdentifierInfo()->getName();
3637
Out.Names.insert(Name);
3738
size_t Start = SM.getFileOffset(Loc);

clang-tools-extra/clangd/CollectMacros.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ class CollectMainFileMacros : public PPCallbacks {
8282

8383
void SourceRangeSkipped(SourceRange R, SourceLocation EndifLoc) override;
8484

85+
// Called when the AST build is done to disable further recording
86+
// of macros by this class. This is needed because some clang-tidy
87+
// checks can trigger PP callbacks by calling directly into the
88+
// preprocessor. Such calls are not interleaved with FileChanged()
89+
// in the expected way, leading this class to erroneously process
90+
// macros that are not in the main file.
91+
void doneParse() { InMainFile = false; }
92+
8593
private:
8694
void add(const Token &MacroNameTok, const MacroInfo *MI,
8795
bool IsDefinition = false, bool InConditionalDirective = false);

clang-tools-extra/clangd/ParsedAST.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,9 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
688688
Marks = Patch->marks();
689689
}
690690
auto &PP = Clang->getPreprocessor();
691-
PP.addPPCallbacks(std::make_unique<CollectMainFileMacros>(PP, Macros));
691+
auto MacroCollector = std::make_unique<CollectMainFileMacros>(PP, Macros);
692+
auto *MacroCollectorPtr = MacroCollector.get(); // so we can call doneParse()
693+
PP.addPPCallbacks(std::move(MacroCollector));
692694

693695
PP.addPPCallbacks(
694696
collectPragmaMarksCallback(Clang->getSourceManager(), Marks));
@@ -709,6 +711,10 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
709711
log("Execute() failed when building AST for {0}: {1}", MainInput.getFile(),
710712
toString(std::move(Err)));
711713

714+
// Disable the macro collector for the remainder of this function, e.g.
715+
// clang-tidy checkers.
716+
MacroCollectorPtr->doneParse();
717+
712718
// We have to consume the tokens before running clang-tidy to avoid collecting
713719
// tokens from running the preprocessor inside the checks (only
714720
// modernize-use-trailing-return-type does that today).

clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,23 @@ TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) {
940940
withFix(equalToFix(ExpectedFix2))))));
941941
}
942942

943+
TEST(DiagnosticsTest, ClangTidyCallingIntoPreprocessor) {
944+
std::string Main = R"cpp(
945+
extern "C" {
946+
#include "b.h"
947+
}
948+
)cpp";
949+
std::string Header = R"cpp(
950+
#define EXTERN extern
951+
EXTERN int waldo();
952+
)cpp";
953+
auto TU = TestTU::withCode(Main);
954+
TU.AdditionalFiles["b.h"] = Header;
955+
TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");
956+
// Check that no assertion failures occur during the build
957+
TU.build();
958+
}
959+
943960
TEST(DiagnosticsTest, Preprocessor) {
944961
// This looks like a preamble, but there's an #else in the middle!
945962
// Check that:

0 commit comments

Comments
 (0)