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