1 //===-- ParsedASTTests.cpp ------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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.
12 //===----------------------------------------------------------------------===//
14 #include "../../clang-tidy/ClangTidyCheck.h"
18 #include "Diagnostics.h"
20 #include "ParsedAST.h"
22 #include "SourceCode.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"
40 #include <string_view>
48 using ::testing::AllOf
;
49 using ::testing::Contains
;
50 using ::testing::ElementsAre
;
51 using ::testing::ElementsAreArray
;
52 using ::testing::IsEmpty
;
54 MATCHER_P(declNamed
, Name
, "") {
55 if (NamedDecl
*ND
= dyn_cast
<NamedDecl
>(arg
))
56 if (ND
->getName() == Name
)
58 if (auto *Stream
= result_listener
->stream()) {
59 llvm::raw_os_ostream
OS(*Stream
);
65 MATCHER_P(declKind
, Kind
, "") {
66 if (NamedDecl
*ND
= dyn_cast
<NamedDecl
>(arg
))
67 if (ND
->getDeclKindName() == llvm::StringRef(Kind
))
69 if (auto *Stream
= result_listener
->stream()) {
70 llvm::raw_os_ostream
OS(*Stream
);
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".
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
);
91 if (Args
->size() == 0)
92 return ArgName
== SpecializationArgs
;
93 return ArgName
== "<" + SpecializationArgs
+ ">";
96 if (const NamedDecl
*ND
= dyn_cast
<NamedDecl
>(arg
))
97 return printTemplateSpecializationArgs(*ND
) == ArgName
;
101 MATCHER_P(pragmaTrivia
, P
, "") { return arg
.Trivia
== P
; }
104 Inclusion Actual
= testing::get
<0>(arg
);
105 Inclusion Expected
= testing::get
<1>(arg
);
106 return std::tie(Actual
.HashLine
, Actual
.Written
) ==
107 std::tie(Expected
.HashLine
, Expected
.Written
);
110 TEST(ParsedASTTest
, TopLevelDecls
) {
118 template <typename> bool X = true;
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"))}));
127 TEST(ParsedASTTest
, DoesNotGetIncludedTopDecls
) {
129 TU
.HeaderCode
= R
"cpp(
130 #define LL void foo(){}
143 auto AST
= TU
.build();
144 EXPECT_THAT(AST
.getLocalTopLevelDecls(), ElementsAre(declNamed("main")));
147 TEST(ParsedASTTest
, DoesNotGetImplicitTemplateTopDecls
) {
157 auto AST
= TU
.build();
158 EXPECT_THAT(AST
.getLocalTopLevelDecls(),
159 ElementsAre(declNamed("f"), declNamed("s")));
163 GetsExplicitInstantiationAndSpecializationTemplateTopDecls
) {
166 template <typename T>
170 template void f(double);
182 double d = foo<double>;
190 auto AST
= TU
.build();
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>"))}));
206 TEST(ParsedASTTest
, IgnoresDelayedTemplateParsing
) {
207 auto TU
= TestTU::withCode(R
"cpp(
208 template <typename T> void xxx() {
212 TU
.ExtraArgs
.push_back("-fdelayed-template-parsing");
213 auto AST
= TU
.build();
214 EXPECT_EQ(Decl::Var
, findUnqualifiedDecl(AST
, "yyy").getKind());
217 TEST(ParsedASTTest
, TokensAfterPreamble
) {
219 TU
.AdditionalFiles
["foo.h"] = R
"(
226 // error-ok: invalid syntax, just examining token stream
230 auto AST
= TU
.build();
231 const syntax::TokenBuffer
&T
= AST
.getTokens();
232 const auto &SM
= AST
.getSourceManager();
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");
242 // The spelled tokens for the main file should have everything.
243 auto Spelled
= T
.spelledTokens(SM
.getMainFileID());
244 ASSERT_FALSE(Spelled
.empty());
245 EXPECT_EQ(Spelled
.front().kind(), tok::hash
);
246 EXPECT_EQ(Spelled
.back().text(SM
), "last_token");
249 TEST(ParsedASTTest
, NoCrashOnTokensWithTidyCheck
) {
251 // this check runs the preprocessor, we need to make sure it does not break
252 // our recording logic.
253 TU
.ClangTidyProvider
= addTidyChecks("modernize-use-trailing-return-type");
254 TU
.Code
= "inline int foo() {}";
256 auto AST
= TU
.build();
257 const syntax::TokenBuffer
&T
= AST
.getTokens();
258 const auto &SM
= AST
.getSourceManager();
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
), "}");
269 TEST(ParsedASTTest
, CanBuildInvocationWithUnknownArgs
) {
271 FS
.Files
= {{testPath("foo.cpp"), "void test() {}"}};
272 // Unknown flags should not prevent a build of compiler invocation.
275 Inputs
.CompileCommand
.CommandLine
= {"clang", "-fsome-unknown-flag",
276 testPath("foo.cpp")};
277 IgnoreDiagnostics IgnoreDiags
;
278 EXPECT_NE(buildCompilerInvocation(Inputs
, IgnoreDiags
), nullptr);
280 // Unknown forwarded to -cc1 should not a failure either.
281 Inputs
.CompileCommand
.CommandLine
= {
282 "clang", "-Xclang", "-fsome-unknown-flag", testPath("foo.cpp")};
283 EXPECT_NE(buildCompilerInvocation(Inputs
, IgnoreDiags
), nullptr);
286 TEST(ParsedASTTest
, CollectsMainFileMacroExpansions
) {
287 llvm::Annotations
TestCase(R
"cpp(
288 #define ^MACRO_ARGS(X, Y) X Y
291 // Macro arguments included.
292 ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), E), ^ID(= 2));
294 // Macro names inside other macros not included.
295 #define ^MACRO_ARGS2(X, Y) X Y
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);
307 // Macros included not from preamble not included.
310 int printf(const char*, ...);
312 #define ^assert(COND) if (!(COND)) { printf("%s
", #COND); exit(0); }
315 // Includes macro expansions in arguments that are expressions
322 #define ^MULTIPLE_DEFINITION 1
323 #undef ^MULTIPLE_DEFINITION
325 #define ^MULTIPLE_DEFINITION 2
326 #undef ^MULTIPLE_DEFINITION
328 auto TU
= TestTU::withCode(TestCase
.code());
329 TU
.HeaderCode
= R
"cpp(
331 #define MACRO_EXP(X) ID(X)
334 TU
.AdditionalFiles
["foo.inc"] = R
"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(R
.StartOffset
);
345 for (const auto &R
: AST
.getMacros().UnknownMacros
)
346 MacroExpansionPositions
.push_back(R
.StartOffset
);
347 EXPECT_THAT(MacroExpansionPositions
,
348 testing::UnorderedElementsAreArray(TestCase
.points()));
351 MATCHER_P(withFileName
, Inc
, "") { return arg
.FileName
== Inc
; }
353 TEST(ParsedASTTest
, PatchesAdditionalIncludes
) {
354 llvm::StringLiteral ModifiedContents
= R
"cpp(
363 // Build expected ast with symbols coming from headers.
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("sub")};
370 TU
.Code
= ModifiedContents
.str();
371 auto ExpectedAST
= TU
.build();
373 // Build preamble with no includes.
377 auto Inputs
= TU
.inputs(FS
);
378 auto CI
= buildCompilerInvocation(Inputs
, Diags
);
380 buildPreamble(testPath("foo.cpp"), *CI
, Inputs
, true, nullptr);
381 ASSERT_TRUE(EmptyPreamble
);
382 EXPECT_THAT(EmptyPreamble
->Includes
.MainFileIncludes
, IsEmpty());
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(testPath("foo.cpp"), Inputs
, std::move(CI
),
389 ASSERT_TRUE(PatchedAST
);
391 // Ensure source location information is correct, including resolved paths.
392 EXPECT_THAT(PatchedAST
->getIncludeStructure().MainFileIncludes
,
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
.getOptionalFileRef(testPath("foo.cpp"));
402 auto MainID
= Includes
.getID(*MainFE
);
403 auto AuxFE
= FM
.getOptionalFileRef(testPath("sub/aux.h"));
405 auto AuxID
= Includes
.getID(*AuxFE
);
406 EXPECT_THAT(Includes
.IncludeChildren
[*MainID
], Contains(*AuxID
));
409 TEST(ParsedASTTest
, PatchesDeletedIncludes
) {
411 TU
.Filename
= "foo.cpp";
413 auto ExpectedAST
= TU
.build();
415 // Build preamble with no includes.
416 TU
.Code
= R
"cpp(#include <foo.h>)cpp";
419 auto Inputs
= TU
.inputs(FS
);
420 auto CI
= buildCompilerInvocation(Inputs
, Diags
);
421 auto BaselinePreamble
=
422 buildPreamble(testPath("foo.cpp"), *CI
, Inputs
, true, nullptr);
423 ASSERT_TRUE(BaselinePreamble
);
424 EXPECT_THAT(BaselinePreamble
->Includes
.MainFileIncludes
,
425 ElementsAre(testing::Field(&Inclusion::Written
, "<foo.h>")));
427 // Now build an AST using additional includes and check that locations are
430 Inputs
= TU
.inputs(FS
);
431 auto PatchedAST
= ParsedAST::build(testPath("foo.cpp"), Inputs
, std::move(CI
),
432 {}, BaselinePreamble
);
433 ASSERT_TRUE(PatchedAST
);
435 // Ensure source location information is correct.
436 EXPECT_THAT(PatchedAST
->getIncludeStructure().MainFileIncludes
,
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(testPath("foo.cpp"));
445 ASSERT_THAT_EXPECTED(MainFE
, llvm::Succeeded());
446 auto MainID
= Includes
.getOrCreateID(*MainFE
);
447 auto &PatchedFM
= PatchedAST
->getSourceManager().getFileManager();
448 IncludeStructure PatchedIncludes
= PatchedAST
->getIncludeStructure();
449 auto PatchedMainFE
= PatchedFM
.getFileRef(testPath("foo.cpp"));
450 ASSERT_THAT_EXPECTED(PatchedMainFE
, llvm::Succeeded());
451 auto PatchedMainID
= PatchedIncludes
.getOrCreateID(*PatchedMainFE
);
452 EXPECT_EQ(Includes
.includeDepth(MainID
)[MainID
],
453 PatchedIncludes
.includeDepth(PatchedMainID
)[PatchedMainID
]);
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("#ifndef {0}\n#define {0}\n{1}\n#endif\n", GuardName
,
464 std::string
once(llvm::StringRef Code
) {
465 return llvm::formatv("#pragma once\n{0}\n", Code
);
468 bool mainIsGuarded(const ParsedAST
&AST
) {
469 const auto &SM
= AST
.getSourceManager();
470 OptionalFileEntryRef MainFE
= SM
.getFileEntryRefForID(SM
.getMainFileID());
471 return AST
.getPreprocessor()
472 .getHeaderSearchInfo()
473 .isFileMultipleIncludeGuarded(*MainFE
);
476 MATCHER_P(diag
, Desc
, "") {
477 return llvm::StringRef(arg
.Message
).contains(Desc
);
480 // Check our understanding of whether the main file is header guarded or not.
481 TEST(ParsedASTTest
, HeaderGuards
) {
483 TU
.ImplicitHeaderGuard
= false;
486 EXPECT_FALSE(mainIsGuarded(TU
.build()));
488 TU
.Code
= guard(";");
489 EXPECT_TRUE(mainIsGuarded(TU
.build()));
492 EXPECT_TRUE(mainIsGuarded(TU
.build()));
498 EXPECT_FALSE(mainIsGuarded(TU
.build())); // FIXME: true
507 EXPECT_FALSE(mainIsGuarded(TU
.build()));
510 // Check our handling of files that include themselves.
511 // Ideally we allow this if the file has header guards.
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.
524 Cfg
.Diagnostics
.MissingIncludes
= Config::IncludesPolicy::None
;
525 Cfg
.Diagnostics
.UnusedIncludes
= Config::IncludesPolicy::None
;
526 WithContextValue
Ctx(Config::Key
, std::move(Cfg
));
529 TU
.ImplicitHeaderGuard
= false;
530 TU
.Filename
= "self.h";
533 #include "self
.h
" // error-ok
536 auto AST
= TU
.build();
537 EXPECT_THAT(AST
.getDiagnostics(),
538 ElementsAre(diag("recursively when building a preamble")));
539 EXPECT_FALSE(mainIsGuarded(AST
));
543 #include "self
.h
" // error-ok
546 EXPECT_THAT(AST
.getDiagnostics(), ElementsAre(diag("nested too deeply")));
547 EXPECT_FALSE(mainIsGuarded(AST
));
555 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
556 EXPECT_TRUE(mainIsGuarded(AST
));
564 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
565 EXPECT_TRUE(mainIsGuarded(AST
));
573 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
574 EXPECT_TRUE(mainIsGuarded(AST
));
579 #include "self
.h
" // error-ok: FIXME, this would be nice to support
584 EXPECT_THAT(AST
.getDiagnostics(),
585 ElementsAre(diag("recursively when building a preamble")));
586 EXPECT_TRUE(mainIsGuarded(AST
));
596 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
597 EXPECT_TRUE(mainIsGuarded(AST
));
599 // Guarded too late...
601 #include "self
.h
" // error-ok
608 EXPECT_THAT(AST
.getDiagnostics(),
609 ElementsAre(diag("recursively when building a preamble")));
610 EXPECT_FALSE(mainIsGuarded(AST
));
613 #include "self
.h
" // error-ok
620 EXPECT_THAT(AST
.getDiagnostics(),
621 ElementsAre(diag("recursively when building a preamble")));
622 EXPECT_FALSE(mainIsGuarded(AST
));
632 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
633 EXPECT_FALSE(mainIsGuarded(AST
));
636 #include "self
.h
" // error-ok
641 EXPECT_THAT(AST
.getDiagnostics(),
642 ElementsAre(diag("recursively when building a preamble")));
643 EXPECT_TRUE(mainIsGuarded(AST
));
646 #include "self
.h
" // error-ok
651 EXPECT_THAT(AST
.getDiagnostics(),
652 ElementsAre(diag("recursively when building a preamble")));
653 EXPECT_TRUE(mainIsGuarded(AST
));
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 {
668 std::string Implementation
= R
"cpp(
669 // error-ok: we assert on diagnostics explicitly
671 template <class T> unsigned Traits<T>::size() {
677 TU
.ImplicitHeaderGuard
= false; // We're testing include guard handling!
678 TU
.ExtraArgs
.push_back("-xc++-header");
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(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(Interface
);
692 EXPECT_THAT(AST
.getDiagnostics(), IsEmpty());
693 EXPECT_TRUE(mainIsGuarded(AST
));
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(Interface
)}};
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(Interface
)}};
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
));
715 TEST(ParsedASTTest
, DiscoversPragmaMarks
) {
717 TU
.AdditionalFiles
["Header.h"] = R
"(
718 #pragma mark - Something API
720 #pragma mark Something else
724 #pragma mark In Preamble
725 #pragma mark - Something Impl
726 int something() { return 1; }
729 auto AST
= TU
.build();
731 EXPECT_THAT(AST
.getMarks(), ElementsAre(pragmaTrivia(" In Preamble"),
732 pragmaTrivia(" - Something Impl"),
733 pragmaTrivia(" End")));
736 TEST(ParsedASTTest
, GracefulFailureOnAssemblyFile
) {
737 std::string Filename
= "TestTU.S";
738 std::string Code
= R
"S(
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.
748 std::string FullFilename
= testPath(Filename
);
749 FS
.Files
[FullFilename
] = Code
;
751 auto &Argv
= Inputs
.CompileCommand
.CommandLine
;
753 Argv
.push_back(FullFilename
);
754 Inputs
.CompileCommand
.Filename
= FullFilename
;
755 Inputs
.CompileCommand
.Directory
= testRoot();
756 Inputs
.Contents
= Code
;
759 auto CI
= buildCompilerInvocation(Inputs
, Diags
);
760 assert(CI
&& "Failed to build compilation invocation.");
761 auto AST
= ParsedAST::build(FullFilename
, Inputs
, std::move(CI
), {}, nullptr);
763 EXPECT_FALSE(AST
.has_value())
764 << "Should not try to build AST for assembly source file";
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("void foo(__builtin_va_list);");
773 TU
.Code
= "void bar() { foo(2); }";
774 TU
.ExtraArgs
.emplace_back("-target");
775 TU
.ExtraArgs
.emplace_back(kPreambleTarget
);
776 const auto Preamble
= TU
.preamble();
778 // Switch target to wasm.
779 TU
.ExtraArgs
.pop_back();
780 TU
.ExtraArgs
.emplace_back("wasm32");
782 IgnoreDiagnostics Diags
;
784 auto Inputs
= TU
.inputs(FS
);
785 auto CI
= buildCompilerInvocation(Inputs
, Diags
);
786 ASSERT_TRUE(CI
) << "Failed to build compiler invocation";
788 auto AST
= ParsedAST::build(testPath(TU
.Filename
), std::move(Inputs
),
789 std::move(CI
), {}, Preamble
);
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
));
797 } // namespace clangd