brintos

brintos / llvm-project-archived public Read only

0
0
Text · 24.5 KiB · f9752d5 Raw
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