diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index 082c7cae00219..f3568c34f99a6 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -116,7 +116,7 @@ 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(const IncludeStructure &Includes, CompilerInstance &Clang, + static void attach(std::vector Includes, CompilerInstance &Clang, const PreambleBounds &PB) { auto &PP = Clang.getPreprocessor(); auto *ExistingCallbacks = PP.getPPCallbacks(); @@ -124,7 +124,7 @@ class ReplayPreamble : private PPCallbacks { if (!ExistingCallbacks) return; PP.addPPCallbacks(std::unique_ptr(new ReplayPreamble( - Includes, ExistingCallbacks, Clang.getSourceManager(), PP, + std::move(Includes), 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. @@ -133,10 +133,10 @@ class ReplayPreamble : private PPCallbacks { } private: - ReplayPreamble(const IncludeStructure &Includes, PPCallbacks *Delegate, + ReplayPreamble(std::vector Includes, PPCallbacks *Delegate, const SourceManager &SM, Preprocessor &PP, const LangOptions &LangOpts, const PreambleBounds &PB) - : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP) { + : Includes(std::move(Includes)), 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( @@ -167,7 +167,7 @@ class ReplayPreamble : private PPCallbacks { } void replay() { - for (const auto &Inc : Includes.MainFileIncludes) { + for (const auto &Inc : Includes) { const FileEntry *File = nullptr; if (Inc.Resolved != "") if (auto FE = SM.getFileManager().getFile(Inc.Resolved)) @@ -227,7 +227,7 @@ class ReplayPreamble : private PPCallbacks { } } - const IncludeStructure &Includes; + const std::vector Includes; PPCallbacks *Delegate; const SourceManager &SM; Preprocessor &PP; @@ -382,7 +382,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(Includes, *Clang, Preamble->Preamble.getBounds()); + ReplayPreamble::attach(Patch->preambleIncludes(), *Clang, + Preamble->Preamble.getBounds()); } // Important: collectIncludeStructure is registered *after* ReplayPreamble! // Otherwise we would collect the replayed includes again... diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp index d86f741d9e72e..0ad4a9f20ccb3 100644 --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -328,6 +328,8 @@ TEST(ParsedASTTest, CollectsMainFileMacroExpansions) { testing::UnorderedElementsAreArray(TestCase.points())); } +MATCHER_P(WithFileName, Inc, "") { return arg.FileName == Inc; } + TEST(ParsedASTTest, ReplayPreambleForTidyCheckers) { struct Inclusion { Inclusion(const SourceManager &SM, SourceLocation HashLoc, @@ -439,6 +441,24 @@ TEST(ParsedASTTest, ReplayPreambleForTidyCheckers) { Code.substr(FileRange.Begin - 1, FileRange.End - FileRange.Begin + 2)); EXPECT_EQ(SkippedFiles[I].kind(), tok::header_name); } + + // Make sure replay logic works with patched preambles. + TU.Code = ""; + StoreDiags Diags; + auto Inputs = TU.inputs(); + auto CI = buildCompilerInvocation(Inputs, Diags); + auto EmptyPreamble = + buildPreamble(testPath(TU.Filename), *CI, Inputs, true, nullptr); + ASSERT_TRUE(EmptyPreamble); + TU.Code = "#include "; + Includes.clear(); + auto PatchedAST = ParsedAST::build(testPath(TU.Filename), TU.inputs(), + std::move(CI), {}, EmptyPreamble); + ASSERT_TRUE(PatchedAST); + // Make sure includes were seen only once. + EXPECT_THAT(Includes, + ElementsAre(WithFileName(testPath("__preamble_patch__.h")), + WithFileName("a.h"))); } TEST(ParsedASTTest, PatchesAdditionalIncludes) {