brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.0 KiB · 91ae727 Raw
666 lines · cpp
1//===-- ASTTests.cpp --------------------------------------------*- C++ -*-===//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#include "AST.h"10 11#include "Annotations.h"12#include "ParsedAST.h"13#include "TestTU.h"14#include "index/Symbol.h"15#include "clang/AST/ASTTypeTraits.h"16#include "clang/AST/Attr.h"17#include "clang/AST/Decl.h"18#include "clang/AST/DeclBase.h"19#include "clang/Basic/AttrKinds.h"20#include "clang/Basic/SourceManager.h"21#include "llvm/ADT/StringRef.h"22#include "llvm/Support/Casting.h"23#include "gmock/gmock.h"24#include "gtest/gtest.h"25#include <cstddef>26#include <string>27#include <vector>28 29namespace clang {30namespace clangd {31namespace {32using testing::Contains;33using testing::Each;34using testing::IsEmpty;35 36TEST(GetDeducedType, KwAutoKwDecltypeExpansion) {37  struct Test {38    StringRef AnnotatedCode;39    const char *DeducedType;40  } Tests[] = {41      {"^auto i = 0;", "int"},42      {"^auto f(){ return 1;};", "int"},43      {44          R"cpp( // auto on struct in a namespace45              namespace ns1 { struct S {}; }46              ^auto v = ns1::S{};47          )cpp",48          "ns1::S",49      },50      {51          R"cpp( // decltype on struct52              namespace ns1 { struct S {}; }53              ns1::S i;54              ^decltype(i) j;55          )cpp",56          "ns1::S",57      },58      {59          R"cpp(// decltype(auto) on struct&60            namespace ns1 {61            struct S {};62            } // namespace ns163 64            ns1::S i;65            ns1::S& j = i;66            ^decltype(auto) k = j;67          )cpp",68          "ns1::S &",69      },70      {71          R"cpp( // auto on template class72              class X;73              template<typename T> class Foo {};74              ^auto v = Foo<X>();75          )cpp",76          "Foo<X>",77      },78      {79          R"cpp( // auto on initializer list.80              namespace std81              {82                template<class _E>83                class [[initializer_list]] { const _E *a, *b; };84              }85 86              ^auto i = {1,2};87          )cpp",88          "std::initializer_list<int>",89      },90      {91          R"cpp( // auto in function return type with trailing return type92            struct Foo {};93            ^auto test() -> decltype(Foo()) {94              return Foo();95            }96          )cpp",97          "Foo",98      },99      {100          R"cpp( // decltype in trailing return type101            struct Foo {};102            auto test() -> ^decltype(Foo()) {103              return Foo();104            }105          )cpp",106          "Foo",107      },108      {109          R"cpp( // auto in function return type110            struct Foo {};111            ^auto test() {112              return Foo();113            }114          )cpp",115          "Foo",116      },117      {118          R"cpp( // auto& in function return type119            struct Foo {};120            ^auto& test() {121              static Foo x;122              return x;123            }124          )cpp",125          "Foo",126      },127      {128          R"cpp( // auto* in function return type129            struct Foo {};130            ^auto* test() {131              Foo *x;132              return x;133            }134          )cpp",135          "Foo",136      },137      {138          R"cpp( // const auto& in function return type139            struct Foo {};140            const ^auto& test() {141              static Foo x;142              return x;143            }144          )cpp",145          "Foo",146      },147      {148          R"cpp( // decltype(auto) in function return (value)149            struct Foo {};150            ^decltype(auto) test() {151              return Foo();152            }153          )cpp",154          "Foo",155      },156      {157          R"cpp( // decltype(auto) in function return (ref)158            struct Foo {};159            ^decltype(auto) test() {160              static Foo x;161              return (x);162            }163          )cpp",164          "Foo &",165      },166      {167          R"cpp( // decltype(auto) in function return (const ref)168            struct Foo {};169            ^decltype(auto) test() {170              static const Foo x;171              return (x);172            }173          )cpp",174          "const Foo &",175      },176      {177          R"cpp( // auto on alias178            struct Foo {};179            using Bar = Foo;180            ^auto x = Bar();181          )cpp",182          "Bar",183      },184      {185          R"cpp(186            // Generic lambda param.187            struct Foo{};188            auto Generic = [](^auto x) { return 0; };189            int m = Generic(Foo{});190          )cpp",191          "struct Foo",192      },193      {194          R"cpp(195            // Generic lambda instantiated twice, matching deduction.196            struct Foo{};197            auto Generic = [](^auto x, auto y) { return 0; };198            int m = Generic(Foo{}, "one");199            int n = Generic(Foo{}, 2);200          )cpp",201          // No deduction although both instantiations yield the same result :-(202          nullptr,203      },204      {205          R"cpp(206            // Generic lambda instantiated twice, conflicting deduction.207            struct Foo{};208            auto Generic = [](^auto y) { return 0; };209            int m = Generic("one");210            int n = Generic(2);211          )cpp",212          nullptr,213      },214      {215          R"cpp(216            // Generic function param.217            struct Foo{};218            int generic(^auto x) { return 0; }219            int m = generic(Foo{});220          )cpp",221          "struct Foo",222      },223      {224          R"cpp(225            // More complicated param type involving auto.226            template <class> concept C = true;227            struct Foo{};228            int generic(C ^auto *x) { return 0; }229            const Foo *Ptr = nullptr;230            int m = generic(Ptr);231          )cpp",232          "const struct Foo",233      },234  };235  for (Test T : Tests) {236    Annotations File(T.AnnotatedCode);237    auto TU = TestTU::withCode(File.code());238    TU.ExtraArgs.push_back("-std=c++20");239    auto AST = TU.build();240    SourceManagerForFile SM("foo.cpp", File.code());241 242    SCOPED_TRACE(T.AnnotatedCode);243    EXPECT_FALSE(File.points().empty());244    for (Position Pos : File.points()) {245      auto Location = sourceLocationInMainFile(SM.get(), Pos);246      ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());247      auto DeducedType = getDeducedType(AST.getASTContext(),248                                        AST.getHeuristicResolver(), *Location);249      if (T.DeducedType == nullptr) {250        EXPECT_FALSE(DeducedType);251      } else {252        ASSERT_TRUE(DeducedType);253        EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);254      }255    }256  }257}258 259TEST(ClangdAST, GetOnlyInstantiation) {260  struct {261    const char *Code;262    llvm::StringLiteral NodeType;263    const char *Name;264  } Cases[] = {265      {266          R"cpp(267            template <typename> class X {};268            X<int> x;269          )cpp",270          "CXXRecord",271          "template<> class X<int> {}",272      },273      {274          R"cpp(275            template <typename T> T X = T{};276            int y = X<char>;277          )cpp",278          "Var",279          // VarTemplateSpecializationDecl doesn't print as template<>...280          "char X = char{}",281      },282      {283          R"cpp(284            template <typename T> int X(T) { return 42; }285            int y = X("text");286          )cpp",287          "Function",288          "template<> int X<const char *>(const char *)",289      },290      {291          R"cpp(292            int X(auto *x) { return 42; }293            int y = X("text");294          )cpp",295          "Function",296          "template<> int X<const char>(const char *x)",297      },298  };299 300  for (const auto &Case : Cases) {301    SCOPED_TRACE(Case.Code);302    auto TU = TestTU::withCode(Case.Code);303    TU.ExtraArgs.push_back("-std=c++20");304    auto AST = TU.build();305    PrintingPolicy PP = AST.getASTContext().getPrintingPolicy();306    PP.TerseOutput = true;307    std::string Name;308    if (auto *Result = getOnlyInstantiation(309            const_cast<NamedDecl *>(&findDecl(AST, [&](const NamedDecl &D) {310              return D.getDescribedTemplate() != nullptr &&311                     D.getDeclKindName() == Case.NodeType;312            })))) {313      llvm::raw_string_ostream OS(Name);314      Result->print(OS, PP);315    }316 317    if (Case.Name)318      EXPECT_EQ(Case.Name, Name);319    else320      EXPECT_THAT(Name, IsEmpty());321  }322}323 324TEST(ClangdAST, GetContainedAutoParamType) {325  auto TU = TestTU::withCode(R"cpp(326    int withAuto(327       auto a,328       auto *b,329       const auto *c,330       auto &&d,331       auto *&e,332       auto (*f)(int)333    ){ return 0; };334 335    int withoutAuto(336      int a,337      int *b,338      const int *c,339      int &&d,340      int *&e,341      int (*f)(int)342    ){ return 0; };343  )cpp");344  TU.ExtraArgs.push_back("-std=c++20");345  auto AST = TU.build();346 347  const auto &WithAuto =348      llvm::cast<FunctionTemplateDecl>(findDecl(AST, "withAuto"));349  auto ParamsWithAuto = WithAuto.getTemplatedDecl()->parameters();350  auto *TemplateParamsWithAuto = WithAuto.getTemplateParameters();351  ASSERT_EQ(ParamsWithAuto.size(), TemplateParamsWithAuto->size());352 353  for (unsigned I = 0; I < ParamsWithAuto.size(); ++I) {354    SCOPED_TRACE(ParamsWithAuto[I]->getNameAsString());355    auto Loc = getContainedAutoParamType(356        ParamsWithAuto[I]->getTypeSourceInfo()->getTypeLoc());357    ASSERT_FALSE(Loc.isNull());358    EXPECT_EQ(Loc.getTypePtr()->getDecl(), TemplateParamsWithAuto->getParam(I));359  }360 361  const auto &WithoutAuto =362      llvm::cast<FunctionDecl>(findDecl(AST, "withoutAuto"));363  for (auto *ParamWithoutAuto : WithoutAuto.parameters()) {364    ASSERT_TRUE(getContainedAutoParamType(365                    ParamWithoutAuto->getTypeSourceInfo()->getTypeLoc())366                    .isNull());367  }368}369 370TEST(ClangdAST, GetQualification) {371  // Tries to insert the decl `Foo` into position of each decl named `insert`.372  // This is done to get an appropriate DeclContext for the insertion location.373  // Qualifications are the required nested name specifier to spell `Foo` at the374  // `insert`ion location.375  // VisibleNamespaces are assumed to be visible at every insertion location.376  const struct {377    llvm::StringRef Test;378    std::vector<llvm::StringRef> Qualifications;379    std::vector<std::string> VisibleNamespaces;380  } Cases[] = {381      {382          R"cpp(383            namespace ns1 { namespace ns2 { class Foo {}; } }384            void insert(); // ns1::ns2::Foo385            namespace ns1 {386              void insert(); // ns2::Foo387              namespace ns2 {388                void insert(); // Foo389              }390              using namespace ns2;391              void insert(); // Foo392            }393            using namespace ns1;394            void insert(); // ns2::Foo395            using namespace ns2;396            void insert(); // Foo397          )cpp",398          {"ns1::ns2::", "ns2::", "", "", "ns2::", ""},399          {},400      },401      {402          R"cpp(403            namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } }404            void insert(); // ns1::ns2::Bar::Foo405            namespace ns1 {406              void insert(); // ns2::Bar::Foo407              namespace ns2 {408                void insert(); // Bar::Foo409              }410              using namespace ns2;411              void insert(); // Bar::Foo412            }413            using namespace ns1;414            void insert(); // ns2::Bar::Foo415            using namespace ns2;416            void insert(); // Bar::Foo417          )cpp",418          {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::",419           "Bar::"},420          {},421      },422      {423          R"cpp(424            namespace ns1 { namespace ns2 { void Foo(); } }425            void insert(); // ns1::ns2::Foo426            namespace ns1 {427              void insert(); // ns2::Foo428              namespace ns2 {429                void insert(); // Foo430              }431            }432          )cpp",433          {"ns1::ns2::", "ns2::", ""},434          {"ns1::"},435      },436      {437          R"cpp(438            namespace ns {439            extern "C" {440            typedef int Foo;441            }442            }443            void insert(); // ns::Foo444          )cpp",445          {"ns::"},446          {},447      },448  };449  for (const auto &Case : Cases) {450    Annotations Test(Case.Test);451    TestTU TU = TestTU::withCode(Test.code());452    ParsedAST AST = TU.build();453    std::vector<const Decl *> InsertionPoints;454    const NamedDecl *TargetDecl;455    findDecl(AST, [&](const NamedDecl &ND) {456      if (ND.getNameAsString() == "Foo") {457        TargetDecl = &ND;458        return true;459      }460 461      if (ND.getNameAsString() == "insert")462        InsertionPoints.push_back(&ND);463      return false;464    });465 466    ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size());467    for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {468      const Decl *D = InsertionPoints[I];469      if (Case.VisibleNamespaces.empty()) {470        EXPECT_EQ(getQualification(AST.getASTContext(),471                                   D->getLexicalDeclContext(), D->getBeginLoc(),472                                   TargetDecl),473                  Case.Qualifications[I]);474      } else {475        EXPECT_EQ(getQualification(AST.getASTContext(),476                                   D->getLexicalDeclContext(), TargetDecl,477                                   Case.VisibleNamespaces),478                  Case.Qualifications[I]);479      }480    }481  }482}483 484TEST(ClangdAST, PrintType) {485  const struct {486    llvm::StringRef Test;487    std::vector<llvm::StringRef> Types;488  } Cases[] = {489      {490          R"cpp(491            namespace ns1 { namespace ns2 { class Foo {}; } }492            void insert(); // ns1::ns2::Foo493            namespace ns1 {494              void insert(); // ns2::Foo495              namespace ns2 {496                void insert(); // Foo497              }498            }499          )cpp",500          {"ns1::ns2::Foo", "ns2::Foo", "Foo"},501      },502      {503          R"cpp(504            namespace ns1 {505              typedef int Foo;506            }507            void insert(); // ns1::Foo508            namespace ns1 {509              void insert(); // Foo510            }511          )cpp",512          {"ns1::Foo", "Foo"},513      },514  };515  for (const auto &Case : Cases) {516    Annotations Test(Case.Test);517    TestTU TU = TestTU::withCode(Test.code());518    ParsedAST AST = TU.build();519    std::vector<const DeclContext *> InsertionPoints;520    const TypeDecl *TargetDecl = nullptr;521    findDecl(AST, [&](const NamedDecl &ND) {522      if (ND.getNameAsString() == "Foo") {523        if (const auto *TD = llvm::dyn_cast<TypeDecl>(&ND)) {524          TargetDecl = TD;525          return true;526        }527      } else if (ND.getNameAsString() == "insert")528        InsertionPoints.push_back(ND.getDeclContext());529      return false;530    });531 532    ASSERT_EQ(InsertionPoints.size(), Case.Types.size());533    for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {534      const auto *DC = InsertionPoints[I];535      EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC,536                          /*Placeholder=*/"", /*FullyQualify=*/true),537                Case.Types[I]);538    }539  }540}541 542TEST(ClangdAST, IsDeeplyNested) {543  Annotations Test(544      R"cpp(545        namespace ns {546        class Foo {547          void bar() {548            class Bar {};549          }550        };551        })cpp");552  TestTU TU = TestTU::withCode(Test.code());553  ParsedAST AST = TU.build();554 555  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/1));556  EXPECT_FALSE(557      isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/2));558 559  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/2));560  EXPECT_FALSE(561      isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/3));562 563  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/3));564  EXPECT_FALSE(565      isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/4));566}567 568MATCHER_P(attrKind, K, "") { return arg->getKind() == K; }569 570MATCHER(implicitAttr, "") { return arg->isImplicit(); }571 572TEST(ClangdAST, GetAttributes) {573  const char *Code = R"cpp(574    class X{};575    class [[nodiscard]] Y{};576    void f(int * a, int * __attribute__((nonnull)) b);577    void foo(bool c) {578      if (c)579        [[unlikely]] return;580    }581  )cpp";582  ParsedAST AST = TestTU::withCode(Code).build();583  auto DeclAttrs = [&](llvm::StringRef Name) {584    return getAttributes(DynTypedNode::create(findUnqualifiedDecl(AST, Name)));585  };586  // Implicit attributes may be present (e.g. visibility on windows).587  ASSERT_THAT(DeclAttrs("X"), Each(implicitAttr()));588  ASSERT_THAT(DeclAttrs("Y"), Contains(attrKind(attr::WarnUnusedResult)));589  ASSERT_THAT(DeclAttrs("f"), Each(implicitAttr()));590  ASSERT_THAT(DeclAttrs("a"), Each(implicitAttr()));591  ASSERT_THAT(DeclAttrs("b"), Contains(attrKind(attr::NonNull)));592 593  Stmt *FooBody = cast<FunctionDecl>(findDecl(AST, "foo")).getBody();594  IfStmt *FooIf = cast<IfStmt>(cast<CompoundStmt>(FooBody)->body_front());595  ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf)),596              Each(implicitAttr()));597  ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf->getThen())),598              Contains(attrKind(attr::Unlikely)));599}600 601TEST(ClangdAST, HasReservedName) {602  ParsedAST AST = TestTU::withCode(R"cpp(603    void __foo();604    namespace std {605      inline namespace __1 { class error_code; }606      namespace __detail { int secret; }607    }608  )cpp")609                      .build();610 611  EXPECT_TRUE(hasReservedName(findUnqualifiedDecl(AST, "__foo")));612  EXPECT_FALSE(613      hasReservedScope(*findUnqualifiedDecl(AST, "__foo").getDeclContext()));614 615  EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "error_code")));616  EXPECT_FALSE(hasReservedScope(617      *findUnqualifiedDecl(AST, "error_code").getDeclContext()));618 619  EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "secret")));620  EXPECT_TRUE(621      hasReservedScope(*findUnqualifiedDecl(AST, "secret").getDeclContext()));622}623 624TEST(ClangdAST, PreferredIncludeDirective) {625  auto ComputePreferredDirective = [](TestTU &TU) {626    auto AST = TU.build();627    return preferredIncludeDirective(AST.tuPath(), AST.getLangOpts(),628                                     AST.getIncludeStructure().MainFileIncludes,629                                     AST.getLocalTopLevelDecls());630  };631  TestTU ObjCTU = TestTU::withCode(R"cpp(632  int main() {}633  )cpp");634  ObjCTU.Filename = "TestTU.m";635  EXPECT_EQ(ComputePreferredDirective(ObjCTU),636            Symbol::IncludeDirective::Import);637 638  TestTU HeaderTU = TestTU::withCode(R"cpp(639  #import "TestTU.h"640  )cpp");641  HeaderTU.Filename = "TestTUHeader.h";642  HeaderTU.ExtraArgs = {"-xobjective-c++-header"};643  EXPECT_EQ(ComputePreferredDirective(HeaderTU),644            Symbol::IncludeDirective::Import);645 646  // ObjC language option is not enough for headers.647  HeaderTU.Code = R"cpp(648  #include "TestTU.h"649  )cpp";650  EXPECT_EQ(ComputePreferredDirective(HeaderTU),651            Symbol::IncludeDirective::Include);652 653  HeaderTU.Code = R"cpp(654  @interface Foo655  @end656 657  Foo * getFoo();658  )cpp";659  EXPECT_EQ(ComputePreferredDirective(HeaderTU),660            Symbol::IncludeDirective::Import);661}662 663} // namespace664} // namespace clangd665} // namespace clang666