clang-tools 19.0.0git
ParsedASTTests.cpp
Go to the documentation of this file.
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 "CompileCommands.h"
19#include "Compiler.h"
20#include "Config.h"
21#include "Diagnostics.h"
22#include "Headers.h"
23#include "ParsedAST.h"
24#include "Preamble.h"
25#include "SourceCode.h"
26#include "TestFS.h"
27#include "TestTU.h"
28#include "TidyProvider.h"
29#include "support/Context.h"
30#include "clang/AST/DeclTemplate.h"
31#include "clang/Basic/FileEntry.h"
32#include "clang/Basic/SourceLocation.h"
33#include "clang/Basic/SourceManager.h"
34#include "clang/Basic/TokenKinds.h"
35#include "clang/Lex/PPCallbacks.h"
36#include "clang/Tooling/Syntax/Tokens.h"
37#include "llvm/ADT/StringRef.h"
38#include "llvm/Testing/Annotations/Annotations.h"
39#include "llvm/Testing/Support/Error.h"
40#include "gmock/gmock-matchers.h"
41#include "gmock/gmock.h"
42#include "gtest/gtest.h"
43#include <memory>
44#include <utility>
45#include <vector>
46
47namespace clang {
48namespace clangd {
49namespace {
50
51using ::testing::AllOf;
52using ::testing::Contains;
53using ::testing::ElementsAre;
54using ::testing::ElementsAreArray;
55using ::testing::IsEmpty;
56
57MATCHER_P(declNamed, Name, "") {
58 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
59 if (ND->getName() == Name)
60 return true;
61 if (auto *Stream = result_listener->stream()) {
62 llvm::raw_os_ostream OS(*Stream);
63 arg->dump(OS);
64 }
65 return false;
66}
67
68MATCHER_P(declKind, Kind, "") {
69 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
70 if (ND->getDeclKindName() == llvm::StringRef(Kind))
71 return true;
72 if (auto *Stream = result_listener->stream()) {
73 llvm::raw_os_ostream OS(*Stream);
74 arg->dump(OS);
75 }
76 return false;
77}
78
79// Matches if the Decl has template args equal to ArgName. If the decl is a
80// NamedDecl and ArgName is an empty string it also matches.
81MATCHER_P(withTemplateArgs, ArgName, "") {
82 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
83 if (const auto *Args = FD->getTemplateSpecializationArgs()) {
84 std::string SpecializationArgs;
85 // Without the PrintingPolicy "bool" will be printed as "_Bool".
86 LangOptions LO;
87 PrintingPolicy Policy(LO);
88 Policy.adjustForCPlusPlus();
89 for (const auto &Arg : Args->asArray()) {
90 if (SpecializationArgs.size() > 0)
91 SpecializationArgs += ",";
92 SpecializationArgs += Arg.getAsType().getAsString(Policy);
93 }
94 if (Args->size() == 0)
95 return ArgName == SpecializationArgs;
96 return ArgName == "<" + SpecializationArgs + ">";
97 }
98 }
99 if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
100 return printTemplateSpecializationArgs(*ND) == ArgName;
101 return false;
102}
103
104MATCHER_P(pragmaTrivia, P, "") { return arg.Trivia == P; }
105
106MATCHER(eqInc, "") {
107 Inclusion Actual = testing::get<0>(arg);
108 Inclusion Expected = testing::get<1>(arg);
109 return std::tie(Actual.HashLine, Actual.Written) ==
110 std::tie(Expected.HashLine, Expected.Written);
111}
112
113TEST(ParsedASTTest, TopLevelDecls) {
114 TestTU TU;
115 TU.HeaderCode = R"(
116 int header1();
117 int header2;
118 )";
119 TU.Code = R"cpp(
120 int main();
121 template <typename> bool X = true;
122 )cpp";
123 auto AST = TU.build();
124 EXPECT_THAT(AST.getLocalTopLevelDecls(),
125 testing::UnorderedElementsAreArray(
126 {AllOf(declNamed("main"), declKind("Function")),
127 AllOf(declNamed("X"), declKind("VarTemplate"))}));
128}
129
130TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) {
131 TestTU TU;
132 TU.HeaderCode = R"cpp(
133 #define LL void foo(){}
134 template<class T>
135 struct H {
136 H() {}
137 LL
138 };
139 )cpp";
140 TU.Code = R"cpp(
141 int main() {
142 H<int> h;
143 h.foo();
144 }
145 )cpp";
146 auto AST = TU.build();
147 EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(declNamed("main")));
148}
149
150TEST(ParsedASTTest, DoesNotGetImplicitTemplateTopDecls) {
151 TestTU TU;
152 TU.Code = R"cpp(
153 template<typename T>
154 void f(T) {}
155 void s() {
156 f(10UL);
157 }
158 )cpp";
159
160 auto AST = TU.build();
161 EXPECT_THAT(AST.getLocalTopLevelDecls(),
162 ElementsAre(declNamed("f"), declNamed("s")));
163}
164
165TEST(ParsedASTTest,
166 GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
167 TestTU TU;
168 TU.Code = R"cpp(
169 template <typename T>
170 void f(T) {}
171 template<>
172 void f(bool);
173 template void f(double);
174
175 template <class T>
176 struct V {};
177 template<class T>
178 struct V<T*> {};
179 template <>
180 struct V<bool> {};
181
182 template<class T>
183 T foo = T(10);
184 int i = foo<int>;
185 double d = foo<double>;
186
187 template <class T>
188 int foo<T*> = 0;
189 template <>
190 int foo<bool> = 0;
191 )cpp";
192
193 auto AST = TU.build();
194 EXPECT_THAT(
195 AST.getLocalTopLevelDecls(),
196 ElementsAreArray({AllOf(declNamed("f"), withTemplateArgs("")),
197 AllOf(declNamed("f"), withTemplateArgs("<bool>")),
198 AllOf(declNamed("f"), withTemplateArgs("<double>")),
199 AllOf(declNamed("V"), withTemplateArgs("")),
200 AllOf(declNamed("V"), withTemplateArgs("<T *>")),
201 AllOf(declNamed("V"), withTemplateArgs("<bool>")),
202 AllOf(declNamed("foo"), withTemplateArgs("")),
203 AllOf(declNamed("i"), withTemplateArgs("")),
204 AllOf(declNamed("d"), withTemplateArgs("")),
205 AllOf(declNamed("foo"), withTemplateArgs("<T *>")),
206 AllOf(declNamed("foo"), withTemplateArgs("<bool>"))}));
207}
208
209TEST(ParsedASTTest, IgnoresDelayedTemplateParsing) {
210 auto TU = TestTU::withCode(R"cpp(
211 template <typename T> void xxx() {
212 int yyy = 0;
213 }
214 )cpp");
215 TU.ExtraArgs.push_back("-fdelayed-template-parsing");
216 auto AST = TU.build();
217 EXPECT_EQ(Decl::Var, findUnqualifiedDecl(AST, "yyy").getKind());
218}
219
220TEST(ParsedASTTest, TokensAfterPreamble) {
221 TestTU TU;
222 TU.AdditionalFiles["foo.h"] = R"(
223 int foo();
224 )";
225 TU.Code = R"cpp(
226 #include "foo.h"
227 first_token;
228 void test() {
229 // error-ok: invalid syntax, just examining token stream
230 }
231 last_token
232)cpp";
233 auto AST = TU.build();
234 const syntax::TokenBuffer &T = AST.getTokens();
235 const auto &SM = AST.getSourceManager();
236
237 ASSERT_GT(T.expandedTokens().size(), 2u);
238 // Check first token after the preamble.
239 EXPECT_EQ(T.expandedTokens().front().text(SM), "first_token");
240 // Last token is always 'eof'.
241 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
242 // Check the token before 'eof'.
243 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "last_token");
244
245 // The spelled tokens for the main file should have everything.
246 auto Spelled = T.spelledTokens(SM.getMainFileID());
247 ASSERT_FALSE(Spelled.empty());
248 EXPECT_EQ(Spelled.front().kind(), tok::hash);
249 EXPECT_EQ(Spelled.back().text(SM), "last_token");
250}
251
252TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
253 TestTU TU;
254 // this check runs the preprocessor, we need to make sure it does not break
255 // our recording logic.
256 TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");
257 TU.Code = "inline int foo() {}";
258
259 auto AST = TU.build();
260 const syntax::TokenBuffer &T = AST.getTokens();
261 const auto &SM = AST.getSourceManager();
262
263 ASSERT_GT(T.expandedTokens().size(), 7u);
264 // Check first token after the preamble.
265 EXPECT_EQ(T.expandedTokens().front().text(SM), "inline");
266 // Last token is always 'eof'.
267 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
268 // Check the token before 'eof'.
269 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}");
270}
271
272TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
273 MockFS FS;
274 FS.Files = {{testPath("foo.cpp"), "void test() {}"}};
275 // Unknown flags should not prevent a build of compiler invocation.
276 ParseInputs Inputs;
277 Inputs.TFS = &FS;
278 Inputs.CompileCommand.CommandLine = {"clang", "-fsome-unknown-flag",
279 testPath("foo.cpp")};
280 IgnoreDiagnostics IgnoreDiags;
281 EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
282
283 // Unknown forwarded to -cc1 should not a failure either.
284 Inputs.CompileCommand.CommandLine = {
285 "clang", "-Xclang", "-fsome-unknown-flag", testPath("foo.cpp")};
286 EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
287}
288
289TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {
290 llvm::Annotations TestCase(R"cpp(
291 #define ^MACRO_ARGS(X, Y) X Y
292 // - preamble ends
293 ^ID(int A);
294 // Macro arguments included.
295 ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), E), ^ID(= 2));
296
297 // Macro names inside other macros not included.
298 #define ^MACRO_ARGS2(X, Y) X Y
299 #define ^FOO BAR
300 #define ^BAR 1
301 int F = ^FOO;
302
303 // Macros from token concatenations not included.
304 #define ^CONCAT(X) X##A()
305 #define ^PREPEND(X) MACRO##X()
306 #define ^MACROA() 123
307 int G = ^CONCAT(MACRO);
308 int H = ^PREPEND(A);
309
310 // Macros included not from preamble not included.
311 #include "foo.inc"
312
313 int printf(const char*, ...);
314 void exit(int);
315 #define ^assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); }
316
317 void test() {
318 // Includes macro expansions in arguments that are expressions
319 ^assert(0 <= ^BAR);
320 }
321
322 #ifdef ^UNDEFINED
323 #endif
324
325 #define ^MULTIPLE_DEFINITION 1
326 #undef ^MULTIPLE_DEFINITION
327
328 #define ^MULTIPLE_DEFINITION 2
329 #undef ^MULTIPLE_DEFINITION
330 )cpp");
331 auto TU = TestTU::withCode(TestCase.code());
332 TU.HeaderCode = R"cpp(
333 #define ID(X) X
334 #define MACRO_EXP(X) ID(X)
335 MACRO_EXP(int B);
336 )cpp";
337 TU.AdditionalFiles["foo.inc"] = R"cpp(
338 int C = ID(1);
339 #define DEF 1
340 int D = DEF;
341 )cpp";
342 ParsedAST AST = TU.build();
343 std::vector<size_t> MacroExpansionPositions;
344 for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
345 for (const auto &R : SIDToRefs.second)
346 MacroExpansionPositions.push_back(R.StartOffset);
347 }
348 for (const auto &R : AST.getMacros().UnknownMacros)
349 MacroExpansionPositions.push_back(R.StartOffset);
350 EXPECT_THAT(
351 MacroExpansionPositions,
352 testing::UnorderedElementsAreArray(TestCase.points()));
353}
354
355MATCHER_P(withFileName, Inc, "") { return arg.FileName == Inc; }
356
357TEST(ParsedASTTest, PatchesAdditionalIncludes) {
358 llvm::StringLiteral ModifiedContents = R"cpp(
359 #include "baz.h"
360 #include "foo.h"
361 #include "sub/aux.h"
362 void bar() {
363 foo();
364 baz();
365 aux();
366 })cpp";
367 // Build expected ast with symbols coming from headers.
368 TestTU TU;
369 TU.Filename = "foo.cpp";
370 TU.AdditionalFiles["foo.h"] = "void foo();";
371 TU.AdditionalFiles["sub/baz.h"] = "void baz();";
372 TU.AdditionalFiles["sub/aux.h"] = "void aux();";
373 TU.ExtraArgs = {"-I" + testPath("sub")};
374 TU.Code = ModifiedContents.str();
375 auto ExpectedAST = TU.build();
376
377 // Build preamble with no includes.
378 TU.Code = "";
379 StoreDiags Diags;
380 MockFS FS;
381 auto Inputs = TU.inputs(FS);
382 auto CI = buildCompilerInvocation(Inputs, Diags);
383 auto EmptyPreamble =
384 buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
385 ASSERT_TRUE(EmptyPreamble);
386 EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, IsEmpty());
387
388 // Now build an AST using empty preamble and ensure patched includes worked.
389 TU.Code = ModifiedContents.str();
390 Inputs = TU.inputs(FS);
391 auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
392 {}, EmptyPreamble);
393 ASSERT_TRUE(PatchedAST);
394
395 // Ensure source location information is correct, including resolved paths.
396 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
397 testing::Pointwise(
398 eqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
399 // Ensure file proximity signals are correct.
400 auto &SM = PatchedAST->getSourceManager();
401 auto &FM = SM.getFileManager();
402 // Copy so that we can use operator[] to get the children.
403 IncludeStructure Includes = PatchedAST->getIncludeStructure();
404 auto MainFE = FM.getFile(testPath("foo.cpp"));
405 ASSERT_TRUE(MainFE);
406 auto MainID = Includes.getID(*MainFE);
407 auto AuxFE = FM.getFile(testPath("sub/aux.h"));
408 ASSERT_TRUE(AuxFE);
409 auto AuxID = Includes.getID(*AuxFE);
410 EXPECT_THAT(Includes.IncludeChildren[*MainID], Contains(*AuxID));
411}
412
413TEST(ParsedASTTest, PatchesDeletedIncludes) {
414 TestTU TU;
415 TU.Filename = "foo.cpp";
416 TU.Code = "";
417 auto ExpectedAST = TU.build();
418
419 // Build preamble with no includes.
420 TU.Code = R"cpp(#include <foo.h>)cpp";
421 StoreDiags Diags;
422 MockFS FS;
423 auto Inputs = TU.inputs(FS);
424 auto CI = buildCompilerInvocation(Inputs, Diags);
425 auto BaselinePreamble =
426 buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
427 ASSERT_TRUE(BaselinePreamble);
428 EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes,
429 ElementsAre(testing::Field(&Inclusion::Written, "<foo.h>")));
430
431 // Now build an AST using additional includes and check that locations are
432 // correctly parsed.
433 TU.Code = "";
434 Inputs = TU.inputs(FS);
435 auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
436 {}, BaselinePreamble);
437 ASSERT_TRUE(PatchedAST);
438
439 // Ensure source location information is correct.
440 EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
441 testing::Pointwise(
442 eqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes));
443 // Ensure file proximity signals are correct.
444 auto &SM = ExpectedAST.getSourceManager();
445 auto &FM = SM.getFileManager();
446 // Copy so that we can getOrCreateID().
447 IncludeStructure Includes = ExpectedAST.getIncludeStructure();
448 auto MainFE = FM.getFileRef(testPath("foo.cpp"));
449 ASSERT_THAT_EXPECTED(MainFE, llvm::Succeeded());
450 auto MainID = Includes.getOrCreateID(*MainFE);
451 auto &PatchedFM = PatchedAST->getSourceManager().getFileManager();
452 IncludeStructure PatchedIncludes = PatchedAST->getIncludeStructure();
453 auto PatchedMainFE = PatchedFM.getFileRef(testPath("foo.cpp"));
454 ASSERT_THAT_EXPECTED(PatchedMainFE, llvm::Succeeded());
455 auto PatchedMainID = PatchedIncludes.getOrCreateID(*PatchedMainFE);
456 EXPECT_EQ(Includes.includeDepth(MainID)[MainID],
457 PatchedIncludes.includeDepth(PatchedMainID)[PatchedMainID]);
458}
459
460// Returns Code guarded by #ifndef guards
461std::string guard(llvm::StringRef Code) {
462 static int GuardID = 0;
463 std::string GuardName = ("GUARD_" + llvm::Twine(++GuardID)).str();
464 return llvm::formatv("#ifndef {0}\n#define {0}\n{1}\n#endif\n", GuardName,
465 Code);
466}
467
468std::string once(llvm::StringRef Code) {
469 return llvm::formatv("#pragma once\n{0}\n", Code);
470}
471
472bool mainIsGuarded(const ParsedAST &AST) {
473 const auto &SM = AST.getSourceManager();
474 OptionalFileEntryRef MainFE = SM.getFileEntryRefForID(SM.getMainFileID());
475 return AST.getPreprocessor()
476 .getHeaderSearchInfo()
477 .isFileMultipleIncludeGuarded(*MainFE);
478}
479
480MATCHER_P(diag, Desc, "") {
481 return llvm::StringRef(arg.Message).contains(Desc);
482}
483
484// Check our understanding of whether the main file is header guarded or not.
485TEST(ParsedASTTest, HeaderGuards) {
486 TestTU TU;
487 TU.ImplicitHeaderGuard = false;
488
489 TU.Code = ";";
490 EXPECT_FALSE(mainIsGuarded(TU.build()));
491
492 TU.Code = guard(";");
493 EXPECT_TRUE(mainIsGuarded(TU.build()));
494
495 TU.Code = once(";");
496 EXPECT_TRUE(mainIsGuarded(TU.build()));
497
498 TU.Code = R"cpp(
499 ;
500 #pragma once
501 )cpp";
502 EXPECT_FALSE(mainIsGuarded(TU.build())); // FIXME: true
503
504 TU.Code = R"cpp(
505 ;
506 #ifndef GUARD
507 #define GUARD
508 ;
509 #endif
510 )cpp";
511 EXPECT_FALSE(mainIsGuarded(TU.build()));
512}
513
514// Check our handling of files that include themselves.
515// Ideally we allow this if the file has header guards.
516//
517// Note: the semicolons (empty statements) are significant!
518// - they force the preamble to end and the body to begin. Directives can have
519// different effects in the preamble vs main file (which we try to hide).
520// - if the preamble would otherwise cover the whole file, a trailing semicolon
521// forces their sizes to be different. This is significant because the file
522// size is part of the lookup key for HeaderFileInfo, and we don't want to
523// rely on the preamble's HFI being looked up when parsing the main file.
524TEST(ParsedASTTest, HeaderGuardsSelfInclude) {
525 // Disable include cleaner diagnostics to prevent them from interfering with
526 // other diagnostics.
527 Config Cfg;
530 WithContextValue Ctx(Config::Key, std::move(Cfg));
531
532 TestTU TU;
533 TU.ImplicitHeaderGuard = false;
534 TU.Filename = "self.h";
535
536 TU.Code = R"cpp(
537 #include "self.h" // error-ok
538 ;
539 )cpp";
540 auto AST = TU.build();
541 EXPECT_THAT(AST.getDiagnostics(),
542 ElementsAre(diag("recursively when building a preamble")));
543 EXPECT_FALSE(mainIsGuarded(AST));
544
545 TU.Code = R"cpp(
546 ;
547 #include "self.h" // error-ok
548 )cpp";
549 AST = TU.build();
550 EXPECT_THAT(AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
551 EXPECT_FALSE(mainIsGuarded(AST));
552
553 TU.Code = R"cpp(
554 #pragma once
555 #include "self.h"
556 ;
557 )cpp";
558 AST = TU.build();
559 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
560 EXPECT_TRUE(mainIsGuarded(AST));
561
562 TU.Code = R"cpp(
563 #pragma once
564 ;
565 #include "self.h"
566 )cpp";
567 AST = TU.build();
568 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
569 EXPECT_TRUE(mainIsGuarded(AST));
570
571 TU.Code = R"cpp(
572 ;
573 #pragma once
574 #include "self.h"
575 )cpp";
576 AST = TU.build();
577 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
578 EXPECT_TRUE(mainIsGuarded(AST));
579
580 TU.Code = R"cpp(
581 #ifndef GUARD
582 #define GUARD
583 #include "self.h" // error-ok: FIXME, this would be nice to support
584 #endif
585 ;
586 )cpp";
587 AST = TU.build();
588 EXPECT_THAT(AST.getDiagnostics(),
589 ElementsAre(diag("recursively when building a preamble")));
590 EXPECT_TRUE(mainIsGuarded(AST));
591
592 TU.Code = R"cpp(
593 #ifndef GUARD
594 #define GUARD
595 ;
596 #include "self.h"
597 #endif
598 )cpp";
599 AST = TU.build();
600 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
601 EXPECT_TRUE(mainIsGuarded(AST));
602
603 // Guarded too late...
604 TU.Code = R"cpp(
605 #include "self.h" // error-ok
606 #ifndef GUARD
607 #define GUARD
608 ;
609 #endif
610 )cpp";
611 AST = TU.build();
612 EXPECT_THAT(AST.getDiagnostics(),
613 ElementsAre(diag("recursively when building a preamble")));
614 EXPECT_FALSE(mainIsGuarded(AST));
615
616 TU.Code = R"cpp(
617 #include "self.h" // error-ok
618 ;
619 #ifndef GUARD
620 #define GUARD
621 #endif
622 )cpp";
623 AST = TU.build();
624 EXPECT_THAT(AST.getDiagnostics(),
625 ElementsAre(diag("recursively when building a preamble")));
626 EXPECT_FALSE(mainIsGuarded(AST));
627
628 TU.Code = R"cpp(
629 ;
630 #ifndef GUARD
631 #define GUARD
632 #include "self.h"
633 #endif
634 )cpp";
635 AST = TU.build();
636 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
637 EXPECT_FALSE(mainIsGuarded(AST));
638
639 TU.Code = R"cpp(
640 #include "self.h" // error-ok
641 #pragma once
642 ;
643 )cpp";
644 AST = TU.build();
645 EXPECT_THAT(AST.getDiagnostics(),
646 ElementsAre(diag("recursively when building a preamble")));
647 EXPECT_TRUE(mainIsGuarded(AST));
648
649 TU.Code = R"cpp(
650 #include "self.h" // error-ok
651 ;
652 #pragma once
653 )cpp";
654 AST = TU.build();
655 EXPECT_THAT(AST.getDiagnostics(),
656 ElementsAre(diag("recursively when building a preamble")));
657 EXPECT_TRUE(mainIsGuarded(AST));
658}
659
660// Tests how we handle common idioms for splitting a header-only library
661// into interface and implementation files (e.g. *.h vs *.inl).
662// These files mutually include each other, and need careful handling of include
663// guards (which interact with preambles).
664TEST(ParsedASTTest, HeaderGuardsImplIface) {
665 std::string Interface = R"cpp(
666 // error-ok: we assert on diagnostics explicitly
667 template <class T> struct Traits {
668 unsigned size();
669 };
670 #include "impl.h"
671 )cpp";
672 std::string Implementation = R"cpp(
673 // error-ok: we assert on diagnostics explicitly
674 #include "iface.h"
675 template <class T> unsigned Traits<T>::size() {
676 return sizeof(T);
677 }
678 )cpp";
679
680 TestTU TU;
681 TU.ImplicitHeaderGuard = false; // We're testing include guard handling!
682 TU.ExtraArgs.push_back("-xc++-header");
683
684 // Editing the interface file, which is include guarded (easy case).
685 // We mostly get this right via PP if we don't recognize the include guard.
686 TU.Filename = "iface.h";
687 TU.Code = guard(Interface);
688 TU.AdditionalFiles = {{"impl.h", Implementation}};
689 auto AST = TU.build();
690 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
691 EXPECT_TRUE(mainIsGuarded(AST));
692 // Slightly harder: the `#pragma once` is part of the preamble, and we
693 // need to transfer it to the main file's HeaderFileInfo.
694 TU.Code = once(Interface);
695 AST = TU.build();
696 EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
697 EXPECT_TRUE(mainIsGuarded(AST));
698
699 // Editing the implementation file, which is not include guarded.
700 TU.Filename = "impl.h";
701 TU.Code = Implementation;
702 TU.AdditionalFiles = {{"iface.h", guard(Interface)}};
703 AST = TU.build();
704 // The diagnostic is unfortunate in this case, but correct per our model.
705 // Ultimately the include is skipped and the code is parsed correctly though.
706 EXPECT_THAT(AST.getDiagnostics(),
707 ElementsAre(diag("in included file: main file cannot be included "
708 "recursively when building a preamble")));
709 EXPECT_FALSE(mainIsGuarded(AST));
710 // Interface is pragma once guarded, same thing.
711 TU.AdditionalFiles = {{"iface.h", once(Interface)}};
712 AST = TU.build();
713 EXPECT_THAT(AST.getDiagnostics(),
714 ElementsAre(diag("in included file: main file cannot be included "
715 "recursively when building a preamble")));
716 EXPECT_FALSE(mainIsGuarded(AST));
717}
718
719TEST(ParsedASTTest, DiscoversPragmaMarks) {
720 TestTU TU;
721 TU.AdditionalFiles["Header.h"] = R"(
722 #pragma mark - Something API
723 int something();
724 #pragma mark Something else
725 )";
726 TU.Code = R"cpp(
727 #include "Header.h"
728 #pragma mark In Preamble
729 #pragma mark - Something Impl
730 int something() { return 1; }
731 #pragma mark End
732 )cpp";
733 auto AST = TU.build();
734
735 EXPECT_THAT(AST.getMarks(), ElementsAre(pragmaTrivia(" In Preamble"),
736 pragmaTrivia(" - Something Impl"),
737 pragmaTrivia(" End")));
738}
739
740TEST(ParsedASTTest, GracefulFailureOnAssemblyFile) {
741 std::string Filename = "TestTU.S";
742 std::string Code = R"S(
743main:
744 # test comment
745 bx lr
746 )S";
747
748 // The rest is a simplified version of TestTU::build().
749 // Don't call TestTU::build() itself because it would assert on
750 // failure to build an AST.
751 MockFS FS;
752 std::string FullFilename = testPath(Filename);
753 FS.Files[FullFilename] = Code;
754 ParseInputs Inputs;
755 auto &Argv = Inputs.CompileCommand.CommandLine;
756 Argv = {"clang"};
757 Argv.push_back(FullFilename);
758 Inputs.CompileCommand.Filename = FullFilename;
759 Inputs.CompileCommand.Directory = testRoot();
760 Inputs.Contents = Code;
761 Inputs.TFS = &FS;
762 StoreDiags Diags;
763 auto CI = buildCompilerInvocation(Inputs, Diags);
764 assert(CI && "Failed to build compilation invocation.");
765 auto AST = ParsedAST::build(FullFilename, Inputs, std::move(CI), {}, nullptr);
766
767 EXPECT_FALSE(AST.has_value())
768 << "Should not try to build AST for assembly source file";
769}
770
771} // namespace
772} // namespace clangd
773} // namespace clang
BindArgumentKind Kind
llvm::SmallString< 256U > Name
std::function< std::unique_ptr< Command >()> Implementation
Definition: Dexp.cpp:363
std::string Code
IgnoringDiagConsumer IgnoreDiags
std::string Filename
Filename as a string.
std::vector< const char * > Expected
std::unique_ptr< CompilerInvocation > CI
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
llvm::json::Object Args
Definition: Trace.cpp:138
llvm::DenseMap< HeaderID, unsigned > includeDepth(HeaderID Root=MainFileID) const
Definition: Headers.cpp:228
HeaderID getOrCreateID(FileEntryRef Entry)
Definition: Headers.cpp:208
std::optional< HeaderID > getID(const FileEntry *Entry) const
Definition: Headers.cpp:197
llvm::DenseMap< HeaderID, SmallVector< HeaderID > > IncludeChildren
Definition: Headers.h:169
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:402
std::string printTemplateSpecializationArgs(const NamedDecl &ND)
Prints template arguments of a decl as written in the source code, including enclosing '<' and '>',...
Definition: AST.cpp:265
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:95
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation CI, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback, PreambleBuildStats *Stats)
Build a preamble for the new inputs unless an old one can be reused.
Definition: Preamble.cpp:591
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
TEST(BackgroundQueueTest, Priority)
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:260
MATCHER(declared, "")
const char * testRoot()
Definition: TestFS.cpp:85
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static clangd::Key< Config > Key
Context key which can be used to set the current Config.
Definition: Config.h:48
IncludesPolicy UnusedIncludes
Definition: Config.h:110
IncludesPolicy MissingIncludes
Definition: Config.h:111
struct clang::clangd::Config::@4 Diagnostics
Controls warnings and errors when parsing code.
std::string Written
Definition: Headers.h:70
tooling::CompileCommand CompileCommand
Definition: Compiler.h:49
const ThreadsafeFS * TFS
Definition: Compiler.h:50
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36