[clangd] Copy existing includes in ReplayPreamble
authorKadir Cetinkaya <kadircet@google.com>
Tue, 2 Jun 2020 09:52:55 +0000 (11:52 +0200)
committerKadir Cetinkaya <kadircet@google.com>
Tue, 2 Jun 2020 11:34:40 +0000 (13:34 +0200)
ReplayPreamble was just grabbing the reference of IncludeStructure
passed to it and then replayed any includes seen so while exiting
built-in file.

This implies any include seen in built-in files being replayed as part
of preamble, even though they are not. This wasn't an issue until we've
started patching preambles, as includes from built-in files were not
mapped back to main-file.

This patch copies over existing includes at the time of
ReplayPreamble::attach and only replies those to prevent any includes
from the preamble patch getting mixed-in.

clang-tools-extra/clangd/ParsedAST.cpp
clang-tools-extra/clangd/unittests/ParsedASTTests.cpp

index 082c7ca..9eed9bc 100644 (file)
@@ -136,7 +136,8 @@ private:
   ReplayPreamble(const IncludeStructure &Includes, PPCallbacks *Delegate,
                  const SourceManager &SM, Preprocessor &PP,
                  const LangOptions &LangOpts, const PreambleBounds &PB)
-      : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP) {
+      : IncludesToReplay(Includes.MainFileIncludes), 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 +168,7 @@ private:
   }
 
   void replay() {
-    for (const auto &Inc : Includes.MainFileIncludes) {
+    for (const auto &Inc : IncludesToReplay) {
       const FileEntry *File = nullptr;
       if (Inc.Resolved != "")
         if (auto FE = SM.getFileManager().getFile(Inc.Resolved))
@@ -227,7 +228,7 @@ private:
     }
   }
 
-  const IncludeStructure &Includes;
+  std::vector<Inclusion> IncludesToReplay;
   PPCallbacks *Delegate;
   const SourceManager &SM;
   Preprocessor &PP;
index d86f741..0ad4a9f 100644 (file)
@@ -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 <a.h>";
+  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) {