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