799 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// These tests cover clangd's logic to build a TU, which generally uses the APIs10// 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 44namespace clang {45namespace clangd {46namespace {47 48using ::testing::AllOf;49using ::testing::Contains;50using ::testing::ElementsAre;51using ::testing::ElementsAreArray;52using ::testing::IsEmpty;53 54MATCHER_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 65MATCHER_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 a77// NamedDecl and ArgName is an empty string it also matches.78MATCHER_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) == ArgName;98 return false;99}100 101MATCHER_P(pragmaTrivia, P, "") { return arg.Trivia == P; }102 103MATCHER(eqInc, "") {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);108}109 110TEST(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 127TEST(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 LL135 };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 147TEST(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 162TEST(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 206TEST(ParsedASTTest, IgnoresDelayedTemplateParsing) {207 auto TU = TestTU::withCode(R"cpp(208 template <typename T> void xxx() {209 int yyy = 0;210 }211 )cpp");212 TU.ExtraArgs.push_back("-fdelayed-template-parsing");213 auto AST = TU.build();214 EXPECT_EQ(Decl::Var, findUnqualifiedDecl(AST, "yyy").getKind());215}216 217TEST(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 stream227 }228 last_token229)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(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 249TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {250 TestTU TU;251 // this check runs the preprocessor, we need to make sure it does not break252 // our recording logic.253 TU.ClangTidyProvider = addTidyChecks("modernize-use-trailing-return-type");254 TU.Code = "inline int foo() { return 0; }";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 269TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {270 MockFS FS;271 FS.Files = {{testPath("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("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("foo.cpp")};283 EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);284}285 286TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {287 llvm::Annotations TestCase(R"cpp(288 #define ^MACRO_ARGS(X, Y) X Y289 // - preamble ends290 ^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 Y296 #define ^FOO BAR297 #define ^BAR 1298 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() 123304 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 expressions316 ^assert(0 <= ^BAR);317 }318 319 #ifdef ^UNDEFINED320 #endif321 322 #define ^MULTIPLE_DEFINITION 1323 #undef ^MULTIPLE_DEFINITION324 325 #define ^MULTIPLE_DEFINITION 2326 #undef ^MULTIPLE_DEFINITION327 )cpp");328 auto TU = TestTU::withCode(TestCase.code());329 TU.HeaderCode = R"cpp(330 #define ID(X) X331 #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 1337 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(R.StartOffset);344 }345 for (const auto &R : AST.getMacros().UnknownMacros)346 MacroExpansionPositions.push_back(R.StartOffset);347 EXPECT_THAT(MacroExpansionPositions,348 testing::UnorderedElementsAreArray(TestCase.points()));349}350 351MATCHER_P(withFileName, Inc, "") { return arg.FileName == Inc; }352 353TEST(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("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, Diags);379 auto EmptyPreamble =380 buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, 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(testPath("foo.cpp"), Inputs, std::move(CI),388 {}, 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.getOptionalFileRef(testPath("foo.cpp"));401 ASSERT_TRUE(MainFE);402 auto MainID = Includes.getID(*MainFE);403 auto AuxFE = FM.getOptionalFileRef(testPath("sub/aux.h"));404 ASSERT_TRUE(AuxFE);405 auto AuxID = Includes.getID(*AuxFE);406 EXPECT_THAT(Includes.IncludeChildren[*MainID], Contains(*AuxID));407}408 409TEST(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, 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>")));426 427 // Now build an AST using additional includes and check that locations are428 // correctly parsed.429 TU.Code = "";430 Inputs = TU.inputs(FS);431 auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),432 {}, 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(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]);454}455 456// Returns Code guarded by #ifndef guards457std::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,461 Code);462}463 464std::string once(llvm::StringRef Code) {465 return llvm::formatv("#pragma once\n{0}\n", Code);466}467 468bool 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);474}475 476MATCHER_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.481TEST(ParsedASTTest, HeaderGuards) {482 TestTU TU;483 TU.ImplicitHeaderGuard = false;484 485 TU.Code = ";";486 EXPECT_FALSE(mainIsGuarded(TU.build()));487 488 TU.Code = guard(";");489 EXPECT_TRUE(mainIsGuarded(TU.build()));490 491 TU.Code = once(";");492 EXPECT_TRUE(mainIsGuarded(TU.build()));493 494 TU.Code = R"cpp(495 ;496 #pragma once497 )cpp";498 EXPECT_FALSE(mainIsGuarded(TU.build())); // FIXME: true499 500 TU.Code = R"cpp(501 ;502 #ifndef GUARD503 #define GUARD504 ;505 #endif506 )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 have515// 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 semicolon517// forces their sizes to be different. This is significant because the file518// size is part of the lookup key for HeaderFileInfo, and we don't want to519// rely on the preamble's HFI being looked up when parsing the main file.520TEST(ParsedASTTest, HeaderGuardsSelfInclude) {521 // Disable include cleaner diagnostics to prevent them from interfering with522 // 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-ok534 ;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-ok544 )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 once551 #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 once560 ;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 once570 #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 GUARD578 #define GUARD579 #include "self.h" // error-ok: FIXME, this would be nice to support580 #endif581 ;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 GUARD590 #define GUARD591 ;592 #include "self.h"593 #endif594 )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-ok602 #ifndef GUARD603 #define GUARD604 ;605 #endif606 )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-ok614 ;615 #ifndef GUARD616 #define GUARD617 #endif618 )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 GUARD627 #define GUARD628 #include "self.h"629 #endif630 )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-ok637 #pragma once638 ;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-ok647 ;648 #pragma once649 )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 library657// into interface and implementation files (e.g. *.h vs *.inl).658// These files mutually include each other, and need careful handling of include659// guards (which interact with preambles).660TEST(ParsedASTTest, HeaderGuardsImplIface) {661 std::string Interface = R"cpp(662 // error-ok: we assert on diagnostics explicitly663 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 explicitly670 #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("-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(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 we689 // need to transfer it to the main file's HeaderFileInfo.690 TU.Code = once(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(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(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 715TEST(ParsedASTTest, DiscoversPragmaMarks) {716 TestTU TU;717 TU.AdditionalFiles["Header.h"] = R"(718 #pragma mark - Something API719 int something();720 #pragma mark Something else721 )";722 TU.Code = R"cpp(723 #include "Header.h"724 #pragma mark In Preamble725 #pragma mark - Something Impl726 int something() { return 1; }727 #pragma mark End728 )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 736TEST(ParsedASTTest, GracefulFailureOnAssemblyFile) {737 std::string Filename = "TestTU.S";738 std::string Code = R"S(739main:740 # test comment741 bx lr742 )S";743 744 // The rest is a simplified version of TestTU::build().745 // Don't call TestTU::build() itself because it would assert on746 // failure to build an AST.747 MockFS FS;748 std::string FullFilename = testPath(Filename);749 FS.Files[FullFilename] = Code;750 ParseInputs Inputs;751 auto &Argv = Inputs.CompileCommand.CommandLine;752 Argv = {"clang"};753 Argv.push_back(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, Diags);760 assert(CI && "Failed to build compilation invocation.");761 auto AST = ParsedAST::build(FullFilename, Inputs, std::move(CI), {}, nullptr);762 763 EXPECT_FALSE(AST.has_value())764 << "Should not try to build AST for assembly source file";765}766 767TEST(ParsedASTTest, PreambleWithDifferentTarget) {768 constexpr std::string_view kPreambleTarget = "x86_64";769 // Specifically picking __builtin_va_list as it triggers crashes when770 // 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();777 778 // Switch target to wasm.779 TU.ExtraArgs.pop_back();780 TU.ExtraArgs.emplace_back("wasm32");781 782 IgnoreDiagnostics Diags;783 MockFS FS;784 auto Inputs = TU.inputs(FS);785 auto CI = buildCompilerInvocation(Inputs, Diags);786 ASSERT_TRUE(CI) << "Failed to build compiler invocation";787 788 auto AST = ParsedAST::build(testPath(TU.Filename), std::move(Inputs),789 std::move(CI), {}, 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} // namespace797} // namespace clangd798} // namespace clang799