clang-tools 22.0.0git
ASTTests.cpp
Go to the documentation of this file.
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
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 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(),
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 else
320 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 the
374 // `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::Foo
385 namespace ns1 {
386 void insert(); // ns2::Foo
387 namespace ns2 {
388 void insert(); // Foo
389 }
390 using namespace ns2;
391 void insert(); // Foo
392 }
393 using namespace ns1;
394 void insert(); // ns2::Foo
395 using namespace ns2;
396 void insert(); // Foo
397 )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::Foo
405 namespace ns1 {
406 void insert(); // ns2::Bar::Foo
407 namespace ns2 {
408 void insert(); // Bar::Foo
409 }
410 using namespace ns2;
411 void insert(); // Bar::Foo
412 }
413 using namespace ns1;
414 void insert(); // ns2::Bar::Foo
415 using namespace ns2;
416 void insert(); // Bar::Foo
417 )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::Foo
426 namespace ns1 {
427 void insert(); // ns2::Foo
428 namespace ns2 {
429 void insert(); // Foo
430 }
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::Foo
444 )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::Foo
493 namespace ns1 {
494 void insert(); // ns2::Foo
495 namespace ns2 {
496 void insert(); // Foo
497 }
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::Foo
508 namespace ns1 {
509 void insert(); // Foo
510 }
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";
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) {
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),
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),
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),
652
653 HeaderTU.Code = R"cpp(
654 @interface Foo
655 @end
656
657 Foo * getFoo();
658 )cpp";
659 EXPECT_EQ(ComputePreferredDirective(HeaderTU),
661}
662
663} // namespace
664} // namespace clangd
665} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
Stores and provides access to parsed AST.
Definition ParsedAST.h:46
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition TestTU.cpp:220
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder, bool FullyQualify)
Returns a QualType as string.
Definition AST.cpp:417
std::string getQualification(ASTContext &Context, const DeclContext *DestContext, SourceLocation InsertionPoint, const NamedDecl *ND)
Gets the nested name specifier necessary for spelling ND in DestContext, at InsertionPoint.
Definition AST.cpp:698
NamedDecl * getOnlyInstantiation(NamedDecl *TemplatedDecl)
Definition AST.cpp:662
std::optional< QualType > getDeducedType(ASTContext &ASTCtx, const HeuristicResolver *Resolver, SourceLocation Loc)
Retrieves the deduced type at a given location (auto, decltype).
Definition AST.cpp:623
MATCHER_P(named, N, "")
Symbol::IncludeDirective preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts, ArrayRef< Inclusion > MainFileIncludes, ArrayRef< const Decl * > TopLevelDecls)
Infer the include directive to use for the given FileName.
Definition AST.cpp:386
TEST(BackgroundQueueTest, Priority)
bool hasReservedName(const Decl &D)
Returns true if this is a NamedDecl with a reserved name.
Definition AST.cpp:444
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition TestTU.cpp:261
std::vector< const Attr * > getAttributes(const DynTypedNode &N)
Return attributes attached directly to a node.
Definition AST.cpp:674
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
bool hasReservedScope(const DeclContext &DC)
Returns true if this scope would be written with a reserved name.
Definition AST.cpp:451
TemplateTypeParmTypeLoc getContainedAutoParamType(TypeLoc TL)
Definition AST.cpp:635
bool isDeeplyNested(const Decl *D, unsigned MaxDepth)
Checks whether D is more than MaxDepth away from translation unit scope.
Definition AST.cpp:742
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
@ Include
#include "header.h"
Definition Symbol.h:93
@ Import
#import "header.h"
Definition Symbol.h:95
ParsedAST build() const
Definition TestTU.cpp:115
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36