xref: /llvm-project/clang-tools-extra/clangd/unittests/ASTTests.cpp (revision 482c41e992c1edf8833a4577b56ff9dda49fbc83)
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-exception
6 //
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 
29 namespace clang {
30 namespace clangd {
31 namespace {
32 using testing::Contains;
33 using testing::Each;
34 using testing::IsEmpty;
35 
TEST(GetDeducedType,KwAutoKwDecltypeExpansion)36 TEST(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 namespace
45               namespace ns1 { struct S {}; }
46               ^auto v = ns1::S{};
47           )cpp",
48           "ns1::S",
49       },
50       {
51           R"cpp( // decltype on struct
52               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 ns1
63 
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 class
72               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 std
81               {
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 type
92             struct Foo {};
93             ^auto test() -> decltype(Foo()) {
94               return Foo();
95             }
96           )cpp",
97           "Foo",
98       },
99       {
100           R"cpp( // decltype in trailing return type
101             struct Foo {};
102             auto test() -> ^decltype(Foo()) {
103               return Foo();
104             }
105           )cpp",
106           "Foo",
107       },
108       {
109           R"cpp( // auto in function return type
110             struct Foo {};
111             ^auto test() {
112               return Foo();
113             }
114           )cpp",
115           "Foo",
116       },
117       {
118           R"cpp( // auto& in function return type
119             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 type
129             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 type
139             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 alias
178             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(), *Location);
248       if (T.DeducedType == nullptr) {
249         EXPECT_FALSE(DeducedType);
250       } else {
251         ASSERT_TRUE(DeducedType);
252         EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
253       }
254     }
255   }
256 }
257 
TEST(ClangdAST,GetOnlyInstantiation)258 TEST(ClangdAST, GetOnlyInstantiation) {
259   struct {
260     const char *Code;
261     llvm::StringLiteral NodeType;
262     const char *Name;
263   } Cases[] = {
264       {
265           R"cpp(
266             template <typename> class X {};
267             X<int> x;
268           )cpp",
269           "CXXRecord",
270           "template<> class X<int> {}",
271       },
272       {
273           R"cpp(
274             template <typename T> T X = T{};
275             int y = X<char>;
276           )cpp",
277           "Var",
278           // VarTemplateSpecializationDecl doesn't print as template<>...
279           "char X = char{}",
280       },
281       {
282           R"cpp(
283             template <typename T> int X(T) { return 42; }
284             int y = X("text");
285           )cpp",
286           "Function",
287           "template<> int X<const char *>(const char *)",
288       },
289       {
290           R"cpp(
291             int X(auto *x) { return 42; }
292             int y = X("text");
293           )cpp",
294           "Function",
295           "template<> int X<const char>(const char *x)",
296       },
297   };
298 
299   for (const auto &Case : Cases) {
300     SCOPED_TRACE(Case.Code);
301     auto TU = TestTU::withCode(Case.Code);
302     TU.ExtraArgs.push_back("-std=c++20");
303     auto AST = TU.build();
304     PrintingPolicy PP = AST.getASTContext().getPrintingPolicy();
305     PP.TerseOutput = true;
306     std::string Name;
307     if (auto *Result = getOnlyInstantiation(
308             const_cast<NamedDecl *>(&findDecl(AST, [&](const NamedDecl &D) {
309               return D.getDescribedTemplate() != nullptr &&
310                      D.getDeclKindName() == Case.NodeType;
311             })))) {
312       llvm::raw_string_ostream OS(Name);
313       Result->print(OS, PP);
314     }
315 
316     if (Case.Name)
317       EXPECT_EQ(Case.Name, Name);
318     else
319       EXPECT_THAT(Name, IsEmpty());
320   }
321 }
322 
TEST(ClangdAST,GetContainedAutoParamType)323 TEST(ClangdAST, GetContainedAutoParamType) {
324   auto TU = TestTU::withCode(R"cpp(
325     int withAuto(
326        auto a,
327        auto *b,
328        const auto *c,
329        auto &&d,
330        auto *&e,
331        auto (*f)(int)
332     ){};
333 
334     int withoutAuto(
335       int a,
336       int *b,
337       const int *c,
338       int &&d,
339       int *&e,
340       int (*f)(int)
341     ){};
342   )cpp");
343   TU.ExtraArgs.push_back("-std=c++20");
344   auto AST = TU.build();
345 
346   const auto &WithAuto =
347       llvm::cast<FunctionTemplateDecl>(findDecl(AST, "withAuto"));
348   auto ParamsWithAuto = WithAuto.getTemplatedDecl()->parameters();
349   auto *TemplateParamsWithAuto = WithAuto.getTemplateParameters();
350   ASSERT_EQ(ParamsWithAuto.size(), TemplateParamsWithAuto->size());
351 
352   for (unsigned I = 0; I < ParamsWithAuto.size(); ++I) {
353     SCOPED_TRACE(ParamsWithAuto[I]->getNameAsString());
354     auto Loc = getContainedAutoParamType(
355         ParamsWithAuto[I]->getTypeSourceInfo()->getTypeLoc());
356     ASSERT_FALSE(Loc.isNull());
357     EXPECT_EQ(Loc.getTypePtr()->getDecl(), TemplateParamsWithAuto->getParam(I));
358   }
359 
360   const auto &WithoutAuto =
361       llvm::cast<FunctionDecl>(findDecl(AST, "withoutAuto"));
362   for (auto *ParamWithoutAuto : WithoutAuto.parameters()) {
363     ASSERT_TRUE(getContainedAutoParamType(
364                     ParamWithoutAuto->getTypeSourceInfo()->getTypeLoc())
365                     .isNull());
366   }
367 }
368 
TEST(ClangdAST,GetQualification)369 TEST(ClangdAST, GetQualification) {
370   // Tries to insert the decl `Foo` into position of each decl named `insert`.
371   // This is done to get an appropriate DeclContext for the insertion location.
372   // Qualifications are the required nested name specifier to spell `Foo` at the
373   // `insert`ion location.
374   // VisibleNamespaces are assumed to be visible at every insertion location.
375   const struct {
376     llvm::StringRef Test;
377     std::vector<llvm::StringRef> Qualifications;
378     std::vector<std::string> VisibleNamespaces;
379   } Cases[] = {
380       {
381           R"cpp(
382             namespace ns1 { namespace ns2 { class Foo {}; } }
383             void insert(); // ns1::ns2::Foo
384             namespace ns1 {
385               void insert(); // ns2::Foo
386               namespace ns2 {
387                 void insert(); // Foo
388               }
389               using namespace ns2;
390               void insert(); // Foo
391             }
392             using namespace ns1;
393             void insert(); // ns2::Foo
394             using namespace ns2;
395             void insert(); // Foo
396           )cpp",
397           {"ns1::ns2::", "ns2::", "", "", "ns2::", ""},
398           {},
399       },
400       {
401           R"cpp(
402             namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } }
403             void insert(); // ns1::ns2::Bar::Foo
404             namespace ns1 {
405               void insert(); // ns2::Bar::Foo
406               namespace ns2 {
407                 void insert(); // Bar::Foo
408               }
409               using namespace ns2;
410               void insert(); // Bar::Foo
411             }
412             using namespace ns1;
413             void insert(); // ns2::Bar::Foo
414             using namespace ns2;
415             void insert(); // Bar::Foo
416           )cpp",
417           {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::",
418            "Bar::"},
419           {},
420       },
421       {
422           R"cpp(
423             namespace ns1 { namespace ns2 { void Foo(); } }
424             void insert(); // ns2::Foo
425             namespace ns1 {
426               void insert(); // ns2::Foo
427               namespace ns2 {
428                 void insert(); // Foo
429               }
430             }
431           )cpp",
432           {"ns2::", "ns2::", ""},
433           {"ns1::"},
434       },
435       {
436           R"cpp(
437             namespace ns {
438             extern "C" {
439             typedef int Foo;
440             }
441             }
442             void insert(); // ns::Foo
443           )cpp",
444           {"ns::"},
445           {},
446       },
447   };
448   for (const auto &Case : Cases) {
449     Annotations Test(Case.Test);
450     TestTU TU = TestTU::withCode(Test.code());
451     ParsedAST AST = TU.build();
452     std::vector<const Decl *> InsertionPoints;
453     const NamedDecl *TargetDecl;
454     findDecl(AST, [&](const NamedDecl &ND) {
455       if (ND.getNameAsString() == "Foo") {
456         TargetDecl = &ND;
457         return true;
458       }
459 
460       if (ND.getNameAsString() == "insert")
461         InsertionPoints.push_back(&ND);
462       return false;
463     });
464 
465     ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size());
466     for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
467       const Decl *D = InsertionPoints[I];
468       if (Case.VisibleNamespaces.empty()) {
469         EXPECT_EQ(getQualification(AST.getASTContext(),
470                                    D->getLexicalDeclContext(), D->getBeginLoc(),
471                                    TargetDecl),
472                   Case.Qualifications[I]);
473       } else {
474         EXPECT_EQ(getQualification(AST.getASTContext(),
475                                    D->getLexicalDeclContext(), TargetDecl,
476                                    Case.VisibleNamespaces),
477                   Case.Qualifications[I]);
478       }
479     }
480   }
481 }
482 
TEST(ClangdAST,PrintType)483 TEST(ClangdAST, PrintType) {
484   const struct {
485     llvm::StringRef Test;
486     std::vector<llvm::StringRef> Types;
487   } Cases[] = {
488       {
489           R"cpp(
490             namespace ns1 { namespace ns2 { class Foo {}; } }
491             void insert(); // ns1::ns2::Foo
492             namespace ns1 {
493               void insert(); // ns2::Foo
494               namespace ns2 {
495                 void insert(); // Foo
496               }
497             }
498           )cpp",
499           {"ns1::ns2::Foo", "ns2::Foo", "Foo"},
500       },
501       {
502           R"cpp(
503             namespace ns1 {
504               typedef int Foo;
505             }
506             void insert(); // ns1::Foo
507             namespace ns1 {
508               void insert(); // Foo
509             }
510           )cpp",
511           {"ns1::Foo", "Foo"},
512       },
513   };
514   for (const auto &Case : Cases) {
515     Annotations Test(Case.Test);
516     TestTU TU = TestTU::withCode(Test.code());
517     ParsedAST AST = TU.build();
518     std::vector<const DeclContext *> InsertionPoints;
519     const TypeDecl *TargetDecl = nullptr;
520     findDecl(AST, [&](const NamedDecl &ND) {
521       if (ND.getNameAsString() == "Foo") {
522         if (const auto *TD = llvm::dyn_cast<TypeDecl>(&ND)) {
523           TargetDecl = TD;
524           return true;
525         }
526       } else if (ND.getNameAsString() == "insert")
527         InsertionPoints.push_back(ND.getDeclContext());
528       return false;
529     });
530 
531     ASSERT_EQ(InsertionPoints.size(), Case.Types.size());
532     for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
533       const auto *DC = InsertionPoints[I];
534       EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC),
535                 Case.Types[I]);
536     }
537   }
538 }
539 
TEST(ClangdAST,IsDeeplyNested)540 TEST(ClangdAST, IsDeeplyNested) {
541   Annotations Test(
542       R"cpp(
543         namespace ns {
544         class Foo {
545           void bar() {
546             class Bar {};
547           }
548         };
549         })cpp");
550   TestTU TU = TestTU::withCode(Test.code());
551   ParsedAST AST = TU.build();
552 
553   EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/1));
554   EXPECT_FALSE(
555       isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/2));
556 
557   EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/2));
558   EXPECT_FALSE(
559       isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/3));
560 
561   EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/3));
562   EXPECT_FALSE(
563       isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/4));
564 }
565 
566 MATCHER_P(attrKind, K, "") { return arg->getKind() == K; }
567 
568 MATCHER(implicitAttr, "") { return arg->isImplicit(); }
569 
TEST(ClangdAST,GetAttributes)570 TEST(ClangdAST, GetAttributes) {
571   const char *Code = R"cpp(
572     class X{};
573     class [[nodiscard]] Y{};
574     void f(int * a, int * __attribute__((nonnull)) b);
575     void foo(bool c) {
576       if (c)
577         [[unlikely]] return;
578     }
579   )cpp";
580   ParsedAST AST = TestTU::withCode(Code).build();
581   auto DeclAttrs = [&](llvm::StringRef Name) {
582     return getAttributes(DynTypedNode::create(findUnqualifiedDecl(AST, Name)));
583   };
584   // Implicit attributes may be present (e.g. visibility on windows).
585   ASSERT_THAT(DeclAttrs("X"), Each(implicitAttr()));
586   ASSERT_THAT(DeclAttrs("Y"), Contains(attrKind(attr::WarnUnusedResult)));
587   ASSERT_THAT(DeclAttrs("f"), Each(implicitAttr()));
588   ASSERT_THAT(DeclAttrs("a"), Each(implicitAttr()));
589   ASSERT_THAT(DeclAttrs("b"), Contains(attrKind(attr::NonNull)));
590 
591   Stmt *FooBody = cast<FunctionDecl>(findDecl(AST, "foo")).getBody();
592   IfStmt *FooIf = cast<IfStmt>(cast<CompoundStmt>(FooBody)->body_front());
593   ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf)),
594               Each(implicitAttr()));
595   ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf->getThen())),
596               Contains(attrKind(attr::Unlikely)));
597 }
598 
TEST(ClangdAST,HasReservedName)599 TEST(ClangdAST, HasReservedName) {
600   ParsedAST AST = TestTU::withCode(R"cpp(
601     void __foo();
602     namespace std {
603       inline namespace __1 { class error_code; }
604       namespace __detail { int secret; }
605     }
606   )cpp")
607                       .build();
608 
609   EXPECT_TRUE(hasReservedName(findUnqualifiedDecl(AST, "__foo")));
610   EXPECT_FALSE(
611       hasReservedScope(*findUnqualifiedDecl(AST, "__foo").getDeclContext()));
612 
613   EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "error_code")));
614   EXPECT_FALSE(hasReservedScope(
615       *findUnqualifiedDecl(AST, "error_code").getDeclContext()));
616 
617   EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST, "secret")));
618   EXPECT_TRUE(
619       hasReservedScope(*findUnqualifiedDecl(AST, "secret").getDeclContext()));
620 }
621 
TEST(ClangdAST,PreferredIncludeDirective)622 TEST(ClangdAST, PreferredIncludeDirective) {
623   auto ComputePreferredDirective = [](TestTU &TU) {
624     auto AST = TU.build();
625     return preferredIncludeDirective(AST.tuPath(), AST.getLangOpts(),
626                                      AST.getIncludeStructure().MainFileIncludes,
627                                      AST.getLocalTopLevelDecls());
628   };
629   TestTU ObjCTU = TestTU::withCode(R"cpp(
630   int main() {}
631   )cpp");
632   ObjCTU.Filename = "TestTU.m";
633   EXPECT_EQ(ComputePreferredDirective(ObjCTU),
634             Symbol::IncludeDirective::Import);
635 
636   TestTU HeaderTU = TestTU::withCode(R"cpp(
637   #import "TestTU.h"
638   )cpp");
639   HeaderTU.Filename = "TestTUHeader.h";
640   HeaderTU.ExtraArgs = {"-xobjective-c++-header"};
641   EXPECT_EQ(ComputePreferredDirective(HeaderTU),
642             Symbol::IncludeDirective::Import);
643 
644   // ObjC language option is not enough for headers.
645   HeaderTU.Code = R"cpp(
646   #include "TestTU.h"
647   )cpp";
648   EXPECT_EQ(ComputePreferredDirective(HeaderTU),
649             Symbol::IncludeDirective::Include);
650 
651   HeaderTU.Code = R"cpp(
652   @interface Foo
653   @end
654 
655   Foo * getFoo();
656   )cpp";
657   EXPECT_EQ(ComputePreferredDirective(HeaderTU),
658             Symbol::IncludeDirective::Import);
659 }
660 
661 } // namespace
662 } // namespace clangd
663 } // namespace clang
664