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