1 //===-- ASTTests.cpp --------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
11 #include "Annotations.h"
12 #include "ParsedAST.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"
32 using testing::Contains
;
34 using testing::IsEmpty
;
36 TEST(GetDeducedType
, KwAutoKwDecltypeExpansion
) {
38 StringRef AnnotatedCode
;
39 const char *DeducedType
;
41 {"^auto i = 0;", "int"},
42 {"^auto f(){ return 1;};", "int"},
44 R
"cpp( // auto on struct in a namespace
45 namespace ns1 { struct S {}; }
51 R
"cpp( // decltype on struct
52 namespace ns1 { struct S {}; }
59 R
"cpp(// decltype(auto) on struct&
66 ^decltype(auto) k = j;
71 R
"cpp( // auto on template class
73 template<typename T> class Foo {};
79 R
"cpp( // auto on initializer list.
83 class [[initializer_list]] {};
88 "std::initializer_list<int>",
91 R
"cpp( // auto in function return type with trailing return type
93 ^auto test() -> decltype(Foo()) {
100 R
"cpp( // decltype in trailing return type
102 auto test() -> ^decltype(Foo()) {
109 R
"cpp( // auto in function return type
118 R
"cpp( // auto& in function return type
128 R
"cpp( // auto* in function return type
138 R
"cpp( // const auto& in function return type
140 const ^auto& test() {
148 R
"cpp( // decltype(auto) in function return (value)
150 ^decltype(auto) test() {
157 R
"cpp( // decltype(auto) in function return (ref)
159 ^decltype(auto) test() {
167 R
"cpp( // decltype(auto) in function return (const ref)
169 ^decltype(auto) test() {
177 R
"cpp( // auto on alias
186 // Generic lambda param.
188 auto Generic = [](^auto x) { return 0; };
189 int m = Generic(Foo{});
195 // Generic lambda instantiated twice, matching deduction.
197 auto Generic = [](^auto x, auto y) { return 0; };
198 int m = Generic(Foo{}, "one
");
199 int n = Generic(Foo{}, 2);
201 // No deduction although both instantiations yield the same result :-(
206 // Generic lambda instantiated twice, conflicting deduction.
208 auto Generic = [](^auto y) { return 0; };
209 int m = Generic("one
");
216 // Generic function param.
218 int generic(^auto x) { return 0; }
219 int m = generic(Foo{});
225 // More complicated param type involving auto.
226 template <class> concept C = true;
228 int generic(C ^auto *x) { return 0; }
229 const Foo *Ptr = nullptr;
230 int m = generic(Ptr);
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());
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
);
251 ASSERT_TRUE(DeducedType
);
252 EXPECT_EQ(DeducedType
->getAsString(), T
.DeducedType
);
258 TEST(ClangdAST
, GetOnlyInstantiation
) {
261 llvm::StringLiteral NodeType
;
266 template <typename> class X {};
270 "template<> class X<int> {}",
274 template <typename T> T X = T{};
278 // VarTemplateSpecializationDecl doesn't print as template<>...
283 template <typename T> int X(T) { return 42; }
287 "template<> int X<const char *>(const char *)",
291 int X(auto *x) { return 42; }
295 "template<> int X<const char>(const char *x)",
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;
307 if (auto *Result
= getOnlyInstantiation(
308 const_cast<NamedDecl
*>(&findDecl(AST
, [&](const NamedDecl
&D
) {
309 return D
.getDescribedTemplate() != nullptr &&
310 D
.getDeclKindName() == Case
.NodeType
;
312 llvm::raw_string_ostream
OS(Name
);
313 Result
->print(OS
, PP
);
317 EXPECT_EQ(Case
.Name
, Name
);
319 EXPECT_THAT(Name
, IsEmpty());
323 TEST(ClangdAST
, GetContainedAutoParamType
) {
324 auto TU
= TestTU::withCode(R
"cpp(
343 TU
.ExtraArgs
.push_back("-std=c++20");
344 auto AST
= TU
.build();
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());
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
));
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())
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.
376 llvm::StringRef Test
;
377 std::vector
<llvm::StringRef
> Qualifications
;
378 std::vector
<std::string
> VisibleNamespaces
;
382 namespace ns1 { namespace ns2 { class Foo {}; } }
383 void insert(); // ns1::ns2::Foo
385 void insert(); // ns2::Foo
387 void insert(); // Foo
390 void insert(); // Foo
393 void insert(); // ns2::Foo
395 void insert(); // Foo
397 {"ns1::ns2::", "ns2::", "", "", "ns2::", ""},
402 namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } }
403 void insert(); // ns1::ns2::Bar::Foo
405 void insert(); // ns2::Bar::Foo
407 void insert(); // Bar::Foo
410 void insert(); // Bar::Foo
413 void insert(); // ns2::Bar::Foo
415 void insert(); // Bar::Foo
417 {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::",
423 namespace ns1 { namespace ns2 { void Foo(); } }
424 void insert(); // ns2::Foo
426 void insert(); // ns2::Foo
428 void insert(); // Foo
432 {"ns2::", "ns2::", ""},
442 void insert(); // ns::Foo
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") {
460 if (ND
.getNameAsString() == "insert")
461 InsertionPoints
.push_back(&ND
);
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(),
472 Case
.Qualifications
[I
]);
474 EXPECT_EQ(getQualification(AST
.getASTContext(),
475 D
->getLexicalDeclContext(), TargetDecl
,
476 Case
.VisibleNamespaces
),
477 Case
.Qualifications
[I
]);
483 TEST(ClangdAST
, PrintType
) {
485 llvm::StringRef Test
;
486 std::vector
<llvm::StringRef
> Types
;
490 namespace ns1 { namespace ns2 { class Foo {}; } }
491 void insert(); // ns1::ns2::Foo
493 void insert(); // ns2::Foo
495 void insert(); // Foo
499 {"ns1::ns2::Foo", "ns2::Foo", "Foo"},
506 void insert(); // ns1::Foo
508 void insert(); // Foo
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
)) {
526 } else if (ND
.getNameAsString() == "insert")
527 InsertionPoints
.push_back(ND
.getDeclContext());
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
),
540 TEST(ClangdAST
, IsDeeplyNested
) {
550 TestTU TU
= TestTU::withCode(Test
.code());
551 ParsedAST AST
= TU
.build();
553 EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST
, "Foo"), /*MaxDepth=*/1));
555 isDeeplyNested(&findUnqualifiedDecl(AST
, "Foo"), /*MaxDepth=*/2));
557 EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST
, "bar"), /*MaxDepth=*/2));
559 isDeeplyNested(&findUnqualifiedDecl(AST
, "bar"), /*MaxDepth=*/3));
561 EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST
, "Bar"), /*MaxDepth=*/3));
563 isDeeplyNested(&findUnqualifiedDecl(AST
, "Bar"), /*MaxDepth=*/4));
566 MATCHER_P(attrKind
, K
, "") { return arg
->getKind() == K
; }
568 MATCHER(implicitAttr
, "") { return arg
->isImplicit(); }
570 TEST(ClangdAST
, GetAttributes
) {
571 const char *Code
= R
"cpp(
573 class [[nodiscard]] Y{};
574 void f(int * a, int * __attribute__((nonnull)) b);
580 ParsedAST AST
= TestTU::withCode(Code
).build();
581 auto DeclAttrs
= [&](llvm::StringRef Name
) {
582 return getAttributes(DynTypedNode::create(findUnqualifiedDecl(AST
, Name
)));
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
)));
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
)));
599 TEST(ClangdAST
, HasReservedName
) {
600 ParsedAST AST
= TestTU::withCode(R
"cpp(
603 inline namespace __1 { class error_code; }
604 namespace __detail { int secret; }
609 EXPECT_TRUE(hasReservedName(findUnqualifiedDecl(AST
, "__foo")));
611 hasReservedScope(*findUnqualifiedDecl(AST
, "__foo").getDeclContext()));
613 EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST
, "error_code")));
614 EXPECT_FALSE(hasReservedScope(
615 *findUnqualifiedDecl(AST
, "error_code").getDeclContext()));
617 EXPECT_FALSE(hasReservedName(findUnqualifiedDecl(AST
, "secret")));
619 hasReservedScope(*findUnqualifiedDecl(AST
, "secret").getDeclContext()));
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());
629 TestTU ObjCTU
= TestTU::withCode(R
"cpp(
632 ObjCTU
.Filename
= "TestTU.m";
633 EXPECT_EQ(ComputePreferredDirective(ObjCTU
),
634 Symbol::IncludeDirective::Import
);
636 TestTU HeaderTU
= TestTU::withCode(R
"cpp(
639 HeaderTU
.Filename
= "TestTUHeader.h";
640 HeaderTU
.ExtraArgs
= {"-xobjective-c++-header"};
641 EXPECT_EQ(ComputePreferredDirective(HeaderTU
),
642 Symbol::IncludeDirective::Import
);
644 // ObjC language option is not enough for headers.
645 HeaderTU
.Code
= R
"cpp(
648 EXPECT_EQ(ComputePreferredDirective(HeaderTU
),
649 Symbol::IncludeDirective::Include
);
651 HeaderTU
.Code
= R
"cpp(
657 EXPECT_EQ(ComputePreferredDirective(HeaderTU
),
658 Symbol::IncludeDirective::Import
);
662 } // namespace clangd