• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- ParsedASTTests.cpp ------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // These tests cover clangd's logic to build a TU, which generally uses the APIs
10 // in ParsedAST and Preamble, via the TestTU helper.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "../../clang-tidy/ClangTidyCheck.h"
15 #include "../../clang-tidy/ClangTidyModule.h"
16 #include "../../clang-tidy/ClangTidyModuleRegistry.h"
17 #include "AST.h"
18 #include "Annotations.h"
19 #include "Compiler.h"
20 #include "Diagnostics.h"
21 #include "Headers.h"
22 #include "ParsedAST.h"
23 #include "Preamble.h"
24 #include "SourceCode.h"
25 #include "TestFS.h"
26 #include "TestTU.h"
27 #include "TidyProvider.h"
28 #include "clang/AST/DeclTemplate.h"
29 #include "clang/Basic/SourceLocation.h"
30 #include "clang/Basic/SourceManager.h"
31 #include "clang/Basic/TokenKinds.h"
32 #include "clang/Lex/PPCallbacks.h"
33 #include "clang/Lex/Token.h"
34 #include "clang/Tooling/Syntax/Tokens.h"
35 #include "llvm/ADT/STLExtras.h"
36 #include "llvm/ADT/StringRef.h"
37 #include "llvm/Support/ScopedPrinter.h"
38 #include "gmock/gmock-matchers.h"
39 #include "gmock/gmock.h"
40 #include "gtest/gtest.h"
41 
42 namespace clang {
43 namespace clangd {
44 namespace {
45 
46 using ::testing::AllOf;
47 using ::testing::ElementsAre;
48 using ::testing::ElementsAreArray;
49 
50 MATCHER_P(DeclNamed, Name, "") {
51   if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
52     if (ND->getName() == Name)
53       return true;
54   if (auto *Stream = result_listener->stream()) {
55     llvm::raw_os_ostream OS(*Stream);
56     arg->dump(OS);
57   }
58   return false;
59 }
60 
61 MATCHER_P(DeclKind, Kind, "") {
62   if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
63     if (ND->getDeclKindName() == llvm::StringRef(Kind))
64       return true;
65   if (auto *Stream = result_listener->stream()) {
66     llvm::raw_os_ostream OS(*Stream);
67     arg->dump(OS);
68   }
69   return false;
70 }
71 
72 // Matches if the Decl has template args equal to ArgName. If the decl is a
73 // NamedDecl and ArgName is an empty string it also matches.
74 MATCHER_P(WithTemplateArgs, ArgName, "") {
75   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
76     if (const auto *Args = FD->getTemplateSpecializationArgs()) {
77       std::string SpecializationArgs;
78       // Without the PrintingPolicy "bool" will be printed as "_Bool".
79       LangOptions LO;
80       PrintingPolicy Policy(LO);
81       Policy.adjustForCPlusPlus();
82       for (const auto &Arg : Args->asArray()) {
83         if (SpecializationArgs.size() > 0)
84           SpecializationArgs += ",";
85         SpecializationArgs += Arg.getAsType().getAsString(Policy);
86       }
87       if (Args->size() == 0)
88         return ArgName == SpecializationArgs;
89       return ArgName == "<" + SpecializationArgs + ">";
90     }
91   }
92   if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
93     return printTemplateSpecializationArgs(*ND) == ArgName;
94   return false;
95 }
96 
97 MATCHER_P(RangeIs, R, "") {
98   return arg.beginOffset() == R.Begin && arg.endOffset() == R.End;
99 }
100 
101 MATCHER(EqInc, "") {
102   Inclusion Actual = testing::get<0>(arg);
103   Inclusion Expected = testing::get<1>(arg);
104   return std::tie(Actual.HashLine, Actual.Written) ==
105          std::tie(Expected.HashLine, Expected.Written);
106 }
107 
TEST(ParsedASTTest,TopLevelDecls)108 TEST(ParsedASTTest, TopLevelDecls) {
109   TestTU TU;
110   TU.HeaderCode = R"(
111     int header1();
112     int header2;
113   )";
114   TU.Code = R"cpp(
115     int main();
116     template <typename> bool X = true;
117   )cpp";
118   auto AST = TU.build();
119   EXPECT_THAT(AST.getLocalTopLevelDecls(),
120               testing::UnorderedElementsAreArray(
121                   {AllOf(DeclNamed("main"), DeclKind("Function")),
122                    AllOf(DeclNamed("X"), DeclKind("VarTemplate"))}));
123 }
124 
TEST(ParsedASTTest,DoesNotGetIncludedTopDecls)125 TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) {
126   TestTU TU;
127   TU.HeaderCode = R"cpp(
128     #define LL void foo(){}
129     template<class T>
130     struct H {
131       H() {}
132       LL
133     };
134   )cpp";
135   TU.Code = R"cpp(
136     int main() {
137       H<int> h;
138       h.foo();
139     }
140   )cpp";
141   auto AST = TU.build();
142   EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
143 }
144 
TEST(ParsedASTTest,DoesNotGetImplicitTemplateTopDecls)145 TEST(ParsedASTTest, DoesNotGetImplicitTemplateTopDecls) {
146   TestTU TU;
147   TU.Code = R"cpp(
148     template<typename T>
149     void f(T) {}
150     void s() {
151       f(10UL);
152     }
153   )cpp";
154 
155   auto AST = TU.build();
156   EXPECT_THAT(AST.getLocalTopLevelDecls(),
157               ElementsAre(DeclNamed("f"), DeclNamed("s")));
158 }
159 
TEST(ParsedASTTest,GetsExplicitInstantiationAndSpecializationTemplateTopDecls)160 TEST(ParsedASTTest,
161      GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
162   TestTU TU;
163   TU.Code = R"cpp(
164     template <typename T>
165     void f(T) {}
166     template<>
167     void f(bool);
168     template void f(double);
169 
170     template <class T>
171     struct V {};
172     template<class T>
173     struct V<T*> {};
174     template <>
175     struct V<bool> {};
176 
177     template<class T>
178     T foo = T(10);
179     int i = foo<int>;
180     double d = foo<double>;
181 
182     template <class T>
183     int foo<T*> = 0;
184     template <>
185     int foo<bool> = 0;
186   )cpp";
187   // FIXME: Auto-completion in a template requires disabling delayed template
188   // parsing.
189   TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
190 
191   auto AST = TU.build();
192   EXPECT_THAT(
193       AST.getLocalTopLevelDecls(),
194       ElementsAreArray({AllOf(DeclNamed("f"), WithTemplateArgs("")),
195                         AllOf(DeclNamed("f"), WithTemplateArgs("<bool>")),
196                         AllOf(DeclNamed("f"), WithTemplateArgs("<double>")),
197                         AllOf(DeclNamed("V"), WithTemplateArgs("")),
198                         AllOf(DeclNamed("V"), WithTemplateArgs("<T *>")),
199                         AllOf(DeclNamed("V"), WithTemplateArgs("<bool>")),
200                         AllOf(DeclNamed("foo"), WithTemplateArgs("")),
201                         AllOf(DeclNamed("i"), WithTemplateArgs("")),
202                         AllOf(DeclNamed("d"), WithTemplateArgs("")),
203                         AllOf(DeclNamed("foo"), WithTemplateArgs("<T *>")),
204                         AllOf(DeclNamed("foo"), WithTemplateArgs("<bool>"))}));
205 }
206 
TEST(ParsedASTTest,IgnoresDelayedTemplateParsing)207 TEST(ParsedASTTest, IgnoresDelayedTemplateParsing) {
208   auto TU = TestTU::withCode(R"cpp(
209     template <typename T> void xxx() {
210       int yyy = 0;
211     }
212   )cpp");
213   TU.ExtraArgs.push_back("-fdelayed-template-parsing");
214   auto AST = TU.build();
215   EXPECT_EQ(Decl::Var, findUnqualifiedDecl(AST, "yyy").getKind());
216 }
217 
TEST(ParsedASTTest,TokensAfterPreamble)218 TEST(ParsedASTTest, TokensAfterPreamble) {
219   TestTU TU;
220   TU.AdditionalFiles["foo.h"] = R"(
221     int foo();
222   )";
223   TU.Code = R"cpp(
224       #include "foo.h"
225       first_token;
226       void test() {
227         // error-ok: invalid syntax, just examining token stream
228       }
229       last_token
230 )cpp";
231   auto AST = TU.build();
232   const syntax::TokenBuffer &T = AST.getTokens();
233   const auto &SM = AST.getSourceManager();
234 
235   ASSERT_GT(T.expandedTokens().size(), 2u);
236   // Check first token after the preamble.
237   EXPECT_EQ(T.expandedTokens().front().text(SM), "first_token");
238   // Last token is always 'eof'.
239   EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
240   // Check the token before 'eof'.
241   EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "last_token");
242 
243   // The spelled tokens for the main file should have everything.
244   auto Spelled = T.spelledTokens(SM.getMainFileID());
245   ASSERT_FALSE(Spelled.empty());
246   EXPECT_EQ(Spelled.front().kind(), tok::hash);
247   EXPECT_EQ(Spelled.back().text(SM), "last_token");
248 }
249 
TEST(ParsedASTTest,NoCrashOnTokensWithTidyCheck)250 TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
251   TestTU TU;
252   // this check runs the preprocessor, we need to make sure it does not break
253   // our recording logic.
254   TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");
255   TU.Code = "inline int foo() {}";
256 
257   auto AST = TU.build();
258   const syntax::TokenBuffer &T = AST.getTokens();
259   const auto &SM = AST.getSourceManager();
260 
261   ASSERT_GT(T.expandedTokens().size(), 7u);
262   // Check first token after the preamble.
263   EXPECT_EQ(T.expandedTokens().front().text(SM), "inline");
264   // Last token is always 'eof'.
265   EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
266   // Check the token before 'eof'.
267   EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}");
268 }
269 
TEST(ParsedASTTest,CanBuildInvocationWithUnknownArgs)270 TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
271   MockFS FS;
272   FS.Files = {{testPath("foo.cpp"), "void test() {}"}};
273   // Unknown flags should not prevent a build of compiler invocation.
274   ParseInputs Inputs;
275   Inputs.TFS = &FS;
276   Inputs.CompileCommand.CommandLine = {"clang", "-fsome-unknown-flag",
277                                        testPath("foo.cpp")};
278   IgnoreDiagnostics IgnoreDiags;
279   EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
280 
281   // Unknown forwarded to -cc1 should not a failure either.
282   Inputs.CompileCommand.CommandLine = {
283       "clang", "-Xclang", "-fsome-unknown-flag", testPath("foo.cpp")};
284   EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
285 }
286 
TEST(ParsedASTTest,CollectsMainFileMacroExpansions)287 TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {
288   Annotations TestCase(R"cpp(
289     #define ^MACRO_ARGS(X, Y) X Y
290     // - preamble ends
291     ^ID(int A);
292     // Macro arguments included.
293     ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), E), ^ID(= 2));
294 
295     // Macro names inside other macros not included.
296     #define ^MACRO_ARGS2(X, Y) X Y
297     #define ^FOO BAR
298     #define ^BAR 1
299     int F = ^FOO;
300 
301     // Macros from token concatenations not included.
302     #define ^CONCAT(X) X##A()
303     #define ^PREPEND(X) MACRO##X()
304     #define ^MACROA() 123
305     int G = ^CONCAT(MACRO);
306     int H = ^PREPEND(A);
307 
308     // Macros included not from preamble not included.
309     #include "foo.inc"
310 
311     int printf(const char*, ...);
312     void exit(int);
313     #define ^assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); }
314 
315     void test() {
316       // Includes macro expansions in arguments that are expressions
317       ^assert(0 <= ^BAR);
318     }
319 
320     #ifdef ^UNDEFINED
321     #endif
322 
323     #define ^MULTIPLE_DEFINITION 1
324     #undef ^MULTIPLE_DEFINITION
325 
326     #define ^MULTIPLE_DEFINITION 2
327     #undef ^MULTIPLE_DEFINITION
328   )cpp");
329   auto TU = TestTU::withCode(TestCase.code());
330   TU.HeaderCode = R"cpp(
331     #define ID(X) X
332     #define MACRO_EXP(X) ID(X)
333     MACRO_EXP(int B);
334   )cpp";
335   TU.AdditionalFiles["foo.inc"] = R"cpp(
336     int C = ID(1);
337     #define DEF 1
338     int D = DEF;
339   )cpp";
340   ParsedAST AST = TU.build();
341   std::vector<Position> MacroExpansionPositions;
342   for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
343     for (const auto &R : SIDToRefs.second)
344       MacroExpansionPositions.push_back(R.start);
345   }
346   for (const auto &R : AST.getMacros().UnknownMacros)
347     MacroExpansionPositions.push_back(R.start);
348   EXPECT_THAT(MacroExpansionPositions,
349               testing::UnorderedElementsAreArray(TestCase.points()));
350 }
351 
352 MATCHER_P(WithFileName, Inc, "") { return arg.FileName == Inc; }
353 
TEST(ParsedASTTest,ReplayPreambleForTidyCheckers)354 TEST(ParsedASTTest, ReplayPreambleForTidyCheckers) {
355   struct Inclusion {
356     Inclusion(const SourceManager &SM, SourceLocation HashLoc,
357               const Token &IncludeTok, llvm::StringRef FileName, bool IsAngled,
358               CharSourceRange FilenameRange)
359         : HashOffset(SM.getDecomposedLoc(HashLoc).second), IncTok(IncludeTok),
360           IncDirective(IncludeTok.getIdentifierInfo()->getName()),
361           FileNameOffset(SM.getDecomposedLoc(FilenameRange.getBegin()).second),
362           FileName(FileName), IsAngled(IsAngled) {}
363     size_t HashOffset;
364     syntax::Token IncTok;
365     llvm::StringRef IncDirective;
366     size_t FileNameOffset;
367     llvm::StringRef FileName;
368     bool IsAngled;
369   };
370   static std::vector<Inclusion> Includes;
371   static std::vector<syntax::Token> SkippedFiles;
372   struct ReplayPreamblePPCallback : public PPCallbacks {
373     const SourceManager &SM;
374     explicit ReplayPreamblePPCallback(const SourceManager &SM) : SM(SM) {}
375 
376     void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
377                             StringRef FileName, bool IsAngled,
378                             CharSourceRange FilenameRange, const FileEntry *,
379                             StringRef, StringRef, const Module *,
380                             SrcMgr::CharacteristicKind) override {
381       Includes.emplace_back(SM, HashLoc, IncludeTok, FileName, IsAngled,
382                             FilenameRange);
383     }
384 
385     void FileSkipped(const FileEntryRef &, const Token &FilenameTok,
386                      SrcMgr::CharacteristicKind) override {
387       SkippedFiles.emplace_back(FilenameTok);
388     }
389   };
390   struct ReplayPreambleCheck : public tidy::ClangTidyCheck {
391     ReplayPreambleCheck(StringRef Name, tidy::ClangTidyContext *Context)
392         : ClangTidyCheck(Name, Context) {}
393     void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
394                              Preprocessor *ModuleExpanderPP) override {
395       PP->addPPCallbacks(::std::make_unique<ReplayPreamblePPCallback>(SM));
396     }
397   };
398   struct ReplayPreambleModule : public tidy::ClangTidyModule {
399     void
400     addCheckFactories(tidy::ClangTidyCheckFactories &CheckFactories) override {
401       CheckFactories.registerCheck<ReplayPreambleCheck>(
402           "replay-preamble-check");
403     }
404   };
405 
406   static tidy::ClangTidyModuleRegistry::Add<ReplayPreambleModule> X(
407       "replay-preamble-module", "");
408   TestTU TU;
409   // This check records inclusion directives replayed by clangd.
410   TU.ClangTidyProvider = addTidyChecks("replay-preamble-check");
411   llvm::Annotations Test(R"cpp(
412     $hash^#$include[[import]] $filebegin^"$filerange[[bar.h]]"
413     $hash^#$include[[include_next]] $filebegin^"$filerange[[baz.h]]"
414     $hash^#$include[[include]] $filebegin^<$filerange[[a.h]]>)cpp");
415   llvm::StringRef Code = Test.code();
416   TU.Code = Code.str();
417   TU.AdditionalFiles["bar.h"] = "";
418   TU.AdditionalFiles["baz.h"] = "";
419   TU.AdditionalFiles["a.h"] = "";
420   // Since we are also testing #import directives, and they don't make much
421   // sense in c++ (also they actually break on windows), just set language to
422   // obj-c.
423   TU.ExtraArgs = {"-isystem.", "-xobjective-c"};
424 
425   const auto &AST = TU.build();
426   const auto &SM = AST.getSourceManager();
427 
428   auto HashLocs = Test.points("hash");
429   ASSERT_EQ(HashLocs.size(), Includes.size());
430   auto IncludeRanges = Test.ranges("include");
431   ASSERT_EQ(IncludeRanges.size(), Includes.size());
432   auto FileBeginLocs = Test.points("filebegin");
433   ASSERT_EQ(FileBeginLocs.size(), Includes.size());
434   auto FileRanges = Test.ranges("filerange");
435   ASSERT_EQ(FileRanges.size(), Includes.size());
436 
437   ASSERT_EQ(SkippedFiles.size(), Includes.size());
438   for (size_t I = 0; I < Includes.size(); ++I) {
439     const auto &Inc = Includes[I];
440 
441     EXPECT_EQ(Inc.HashOffset, HashLocs[I]);
442 
443     auto IncRange = IncludeRanges[I];
444     EXPECT_THAT(Inc.IncTok.range(SM), RangeIs(IncRange));
445     EXPECT_EQ(Inc.IncTok.kind(), tok::identifier);
446     EXPECT_EQ(Inc.IncDirective,
447               Code.substr(IncRange.Begin, IncRange.End - IncRange.Begin));
448 
449     EXPECT_EQ(Inc.FileNameOffset, FileBeginLocs[I]);
450     EXPECT_EQ(Inc.IsAngled, Code[FileBeginLocs[I]] == '<');
451 
452     auto FileRange = FileRanges[I];
453     EXPECT_EQ(Inc.FileName,
454               Code.substr(FileRange.Begin, FileRange.End - FileRange.Begin));
455 
456     EXPECT_EQ(SM.getDecomposedLoc(SkippedFiles[I].location()).second,
457               Inc.FileNameOffset);
458     // This also contains quotes/angles so increment the range by one from both
459     // sides.
460     EXPECT_EQ(
461         SkippedFiles[I].text(SM),
462         Code.substr(FileRange.Begin - 1, FileRange.End - FileRange.Begin + 2));
463     EXPECT_EQ(SkippedFiles[I].kind(), tok::header_name);
464   }
465 
466   TU.AdditionalFiles["a.h"] = "";
467   TU.AdditionalFiles["b.h"] = "";
468   TU.AdditionalFiles["c.h"] = "";
469   // Make sure replay logic works with patched preambles.
470   llvm::StringLiteral Baseline = R"cpp(
471     #include "a.h"
472     #include "c.h")cpp";
473   MockFS FS;
474   TU.Code = Baseline.str();
475   auto Inputs = TU.inputs(FS);
476   auto BaselinePreamble = TU.preamble();
477   ASSERT_TRUE(BaselinePreamble);
478 
479   // First make sure we don't crash on various modifications to the preamble.
480   llvm::StringLiteral Cases[] = {
481       // clang-format off
482       // New include in middle.
483       R"cpp(
484         #include "a.h"
485         #include "b.h"
486         #include "c.h")cpp",
487       // New include at top.
488       R"cpp(
489         #include "b.h"
490         #include "a.h"
491         #include "c.h")cpp",
492       // New include at bottom.
493       R"cpp(
494         #include "a.h"
495         #include "c.h"
496         #include "b.h")cpp",
497       // Same size with a missing include.
498       R"cpp(
499         #include "a.h"
500         #include "b.h")cpp",
501       // Smaller with no new includes.
502       R"cpp(
503         #include "a.h")cpp",
504       // Smaller with a new includes.
505       R"cpp(
506         #include "b.h")cpp",
507       // clang-format on
508   };
509   for (llvm::StringLiteral Case : Cases) {
510     TU.Code = Case.str();
511 
512     IgnoreDiagnostics Diags;
513     auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
514     auto PatchedAST = ParsedAST::build(testPath(TU.Filename), TU.inputs(FS),
515                                        std::move(CI), {}, BaselinePreamble);
516     ASSERT_TRUE(PatchedAST);
517     EXPECT_TRUE(PatchedAST->getDiagnostics().empty());
518   }
519 
520   // Then ensure correctness by making sure includes were seen only once.
521   // Note that we first see the includes from the patch, as preamble includes
522   // are replayed after exiting the built-in file.
523   Includes.clear();
524   TU.Code = R"cpp(
525     #include "a.h"
526     #include "b.h")cpp";
527   IgnoreDiagnostics Diags;
528   auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
529   auto PatchedAST = ParsedAST::build(testPath(TU.Filename), TU.inputs(FS),
530                                      std::move(CI), {}, BaselinePreamble);
531   ASSERT_TRUE(PatchedAST);
532   EXPECT_TRUE(PatchedAST->getDiagnostics().empty());
533   EXPECT_THAT(Includes,
534               ElementsAre(WithFileName(testPath("__preamble_patch__.h")),
535                           WithFileName("b.h"), WithFileName("a.h")));
536 }
537 
TEST(ParsedASTTest,PatchesAdditionalIncludes)538 TEST(ParsedASTTest, PatchesAdditionalIncludes) {
539   llvm::StringLiteral ModifiedContents = R"cpp(
540     #include "baz.h"
541     #include "foo.h"
542     #include "sub/aux.h"
543     void bar() {
544       foo();
545       baz();
546       aux();
547     })cpp";
548   // Build expected ast with symbols coming from headers.
549   TestTU TU;
550   TU.Filename = "foo.cpp";
551   TU.AdditionalFiles["foo.h"] = "void foo();";
552   TU.AdditionalFiles["sub/baz.h"] = "void baz();";
553   TU.AdditionalFiles["sub/aux.h"] = "void aux();";
554   TU.ExtraArgs = {"-I" + testPath("sub")};
555   TU.Code = ModifiedContents.str();
556   auto ExpectedAST = TU.build();
557 
558   // Build preamble with no includes.
559   TU.Code = "";
560   StoreDiags Diags;
561   MockFS FS;
562   auto Inputs = TU.inputs(FS);
563   auto CI = buildCompilerInvocation(Inputs, Diags);
564   auto EmptyPreamble =
565       buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
566   ASSERT_TRUE(EmptyPreamble);
567   EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, testing::IsEmpty());
568 
569   // Now build an AST using empty preamble and ensure patched includes worked.
570   TU.Code = ModifiedContents.str();
571   Inputs = TU.inputs(FS);
572   auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
573                                      {}, EmptyPreamble);
574   ASSERT_TRUE(PatchedAST);
575   ASSERT_TRUE(PatchedAST->getDiagnostics().empty());
576 
577   // Ensure source location information is correct, including resolved paths.
578   EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
579               testing::Pointwise(
580                   EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
581   auto StringMapToVector = [](const llvm::StringMap<unsigned> SM) {
582     std::vector<std::pair<std::string, unsigned>> Res;
583     for (const auto &E : SM)
584       Res.push_back({E.first().str(), E.second});
585     llvm::sort(Res);
586     return Res;
587   };
588   // Ensure file proximity signals are correct.
589   EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth(
590                 testPath("foo.cpp"))),
591             StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth(
592                 testPath("foo.cpp"))));
593 }
594 
TEST(ParsedASTTest,PatchesDeletedIncludes)595 TEST(ParsedASTTest, PatchesDeletedIncludes) {
596   TestTU TU;
597   TU.Filename = "foo.cpp";
598   TU.Code = "";
599   auto ExpectedAST = TU.build();
600 
601   // Build preamble with no includes.
602   TU.Code = R"cpp(#include <foo.h>)cpp";
603   StoreDiags Diags;
604   MockFS FS;
605   auto Inputs = TU.inputs(FS);
606   auto CI = buildCompilerInvocation(Inputs, Diags);
607   auto BaselinePreamble =
608       buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
609   ASSERT_TRUE(BaselinePreamble);
610   EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes,
611               ElementsAre(testing::Field(&Inclusion::Written, "<foo.h>")));
612 
613   // Now build an AST using additional includes and check that locations are
614   // correctly parsed.
615   TU.Code = "";
616   Inputs = TU.inputs(FS);
617   auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
618                                      {}, BaselinePreamble);
619   ASSERT_TRUE(PatchedAST);
620 
621   // Ensure source location information is correct.
622   EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
623               testing::Pointwise(
624                   EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
625   auto StringMapToVector = [](const llvm::StringMap<unsigned> SM) {
626     std::vector<std::pair<std::string, unsigned>> Res;
627     for (const auto &E : SM)
628       Res.push_back({E.first().str(), E.second});
629     llvm::sort(Res);
630     return Res;
631   };
632   // Ensure file proximity signals are correct.
633   EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth(
634                 testPath("foo.cpp"))),
635             StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth(
636                 testPath("foo.cpp"))));
637 }
638 
639 } // namespace
640 } // namespace clangd
641 } // namespace clang
642