1 | //===- unittest/AST/ASTContextParentMapTest.cpp - AST parent map test -----===// |
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 | // Tests for the getParents(...) methods of ASTContext. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/AST/ASTContext.h" |
14 | #include "MatchVerifier.h" |
15 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
16 | #include "clang/ASTMatchers/ASTMatchers.h" |
17 | #include "clang/Tooling/Tooling.h" |
18 | #include "gtest/gtest.h" |
19 | #include "gmock/gmock.h" |
20 | |
21 | using testing::ElementsAre; |
22 | |
23 | namespace clang { |
24 | namespace ast_matchers { |
25 | |
26 | TEST(GetParents, ReturnsParentForDecl) { |
27 | MatchVerifier<Decl> Verifier; |
28 | EXPECT_TRUE( |
29 | Verifier.match("class C { void f(); };" , |
30 | cxxMethodDecl(hasParent(recordDecl(hasName("C" )))))); |
31 | } |
32 | |
33 | TEST(GetParents, ReturnsParentForStmt) { |
34 | MatchVerifier<Stmt> Verifier; |
35 | EXPECT_TRUE(Verifier.match("class C { void f() { if (true) {} } };" , |
36 | ifStmt(hasParent(compoundStmt())))); |
37 | } |
38 | |
39 | TEST(GetParents, ReturnsParentForTypeLoc) { |
40 | MatchVerifier<TypeLoc> Verifier; |
41 | EXPECT_TRUE( |
42 | Verifier.match("namespace a { class b {}; } void f(a::b) {}" , |
43 | typeLoc(hasParent(typeLoc(hasParent(functionDecl())))))); |
44 | } |
45 | |
46 | TEST(GetParents, ReturnsParentForNestedNameSpecifierLoc) { |
47 | MatchVerifier<NestedNameSpecifierLoc> Verifier; |
48 | EXPECT_TRUE(Verifier.match("namespace a { class b {}; } void f(a::b) {}" , |
49 | nestedNameSpecifierLoc(hasParent(typeLoc())))); |
50 | } |
51 | |
52 | TEST(GetParents, ReturnsParentInsideTemplateInstantiations) { |
53 | MatchVerifier<Decl> DeclVerifier; |
54 | EXPECT_TRUE(DeclVerifier.match( |
55 | "template<typename T> struct C { void f() {} };" |
56 | "void g() { C<int> c; c.f(); }" , |
57 | cxxMethodDecl(hasName("f" ), |
58 | hasParent(cxxRecordDecl(isTemplateInstantiation()))))); |
59 | EXPECT_TRUE(DeclVerifier.match( |
60 | "template<typename T> struct C { void f() {} };" |
61 | "void g() { C<int> c; c.f(); }" , |
62 | cxxMethodDecl(hasName("f" ), |
63 | hasParent(cxxRecordDecl(unless(isTemplateInstantiation())))))); |
64 | EXPECT_FALSE(DeclVerifier.match( |
65 | "template<typename T> struct C { void f() {} };" |
66 | "void g() { C<int> c; c.f(); }" , |
67 | cxxMethodDecl( |
68 | hasName("f" ), |
69 | allOf(hasParent(cxxRecordDecl(unless(isTemplateInstantiation()))), |
70 | hasParent(cxxRecordDecl(isTemplateInstantiation())))))); |
71 | } |
72 | |
73 | TEST(GetParents, ReturnsMultipleParentsInTemplateInstantiations) { |
74 | MatchVerifier<Stmt> TemplateVerifier; |
75 | EXPECT_TRUE(TemplateVerifier.match( |
76 | "template<typename T> struct C { void f() {} };" |
77 | "void g() { C<int> c; c.f(); }" , |
78 | compoundStmt(allOf( |
79 | hasAncestor(cxxRecordDecl(isTemplateInstantiation())), |
80 | hasAncestor(cxxRecordDecl(unless(isTemplateInstantiation()))))))); |
81 | } |
82 | |
83 | TEST(GetParents, RespectsTraversalScope) { |
84 | auto AST = tooling::buildASTFromCode( |
85 | Code: "struct foo { int bar; }; struct baz{};" , FileName: "foo.cpp" , |
86 | PCHContainerOps: std::make_shared<PCHContainerOperations>()); |
87 | auto &Ctx = AST->getASTContext(); |
88 | auto &TU = *Ctx.getTranslationUnitDecl(); |
89 | auto &Foo = *TU.lookup(&Ctx.Idents.get(Name: "foo" )).front(); |
90 | auto &Bar = *cast<DeclContext>(Foo).lookup(&Ctx.Idents.get(Name: "bar" )).front(); |
91 | auto &Baz = *TU.lookup(&Ctx.Idents.get(Name: "baz" )).front(); |
92 | |
93 | // Initially, scope is the whole TU. |
94 | EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); |
95 | EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU))); |
96 | EXPECT_THAT(Ctx.getParents(Baz), ElementsAre(DynTypedNode::create(TU))); |
97 | |
98 | // Restrict the scope, now some parents are gone. |
99 | Ctx.setTraversalScope({&Foo}); |
100 | EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); |
101 | EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU))); |
102 | EXPECT_THAT(Ctx.getParents(Baz), ElementsAre()); |
103 | |
104 | // Reset the scope, we get back the original results. |
105 | Ctx.setTraversalScope({&TU}); |
106 | EXPECT_THAT(Ctx.getParents(Bar), ElementsAre(DynTypedNode::create(Foo))); |
107 | EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU))); |
108 | EXPECT_THAT(Ctx.getParents(Baz), ElementsAre(DynTypedNode::create(TU))); |
109 | } |
110 | |
111 | TEST(GetParents, ImplicitLambdaNodes) { |
112 | MatchVerifier<Decl> LambdaVerifier; |
113 | EXPECT_TRUE(LambdaVerifier.match( |
114 | "auto x = []{int y;};" , |
115 | varDecl(hasName("y" ), hasAncestor(functionDecl( |
116 | hasOverloadedOperatorName("()" ), |
117 | hasParent(cxxRecordDecl( |
118 | isImplicit(), hasParent(lambdaExpr())))))), |
119 | Lang_CXX11)); |
120 | } |
121 | |
122 | TEST(GetParents, FriendTypeLoc) { |
123 | auto AST = tooling::buildASTFromCode(Code: "struct A { friend struct Fr; };" |
124 | "struct B { friend struct Fr; };" |
125 | "struct Fr;" ); |
126 | auto &Ctx = AST->getASTContext(); |
127 | auto &TU = *Ctx.getTranslationUnitDecl(); |
128 | auto &A = *TU.lookup(&Ctx.Idents.get(Name: "A" )).front(); |
129 | auto &B = *TU.lookup(&Ctx.Idents.get(Name: "B" )).front(); |
130 | auto &FrA = *cast<FriendDecl>(*++(cast<CXXRecordDecl>(A).decls_begin())); |
131 | auto &FrB = *cast<FriendDecl>(*++(cast<CXXRecordDecl>(B).decls_begin())); |
132 | TypeLoc FrALoc = FrA.getFriendType()->getTypeLoc(); |
133 | TypeLoc FrBLoc = FrB.getFriendType()->getTypeLoc(); |
134 | TagDecl *FrATagDecl = |
135 | FrALoc.getTypePtr()->getAs<ElaboratedType>()->getOwnedTagDecl(); |
136 | TagDecl *FrBTagDecl = |
137 | FrBLoc.getTypePtr()->getAs<ElaboratedType>()->getOwnedTagDecl(); |
138 | |
139 | EXPECT_THAT(Ctx.getParents(A), ElementsAre(DynTypedNode::create(TU))); |
140 | EXPECT_THAT(Ctx.getParents(B), ElementsAre(DynTypedNode::create(TU))); |
141 | EXPECT_THAT(Ctx.getParents(FrA), ElementsAre(DynTypedNode::create(A))); |
142 | EXPECT_THAT(Ctx.getParents(FrB), ElementsAre(DynTypedNode::create(B))); |
143 | EXPECT_THAT(Ctx.getParents(FrALoc), ElementsAre(DynTypedNode::create(FrA))); |
144 | EXPECT_THAT(Ctx.getParents(FrBLoc), ElementsAre(DynTypedNode::create(FrB))); |
145 | EXPECT_TRUE(FrATagDecl); |
146 | EXPECT_FALSE(FrBTagDecl); |
147 | EXPECT_THAT(Ctx.getParents(*FrATagDecl), |
148 | ElementsAre(DynTypedNode::create(FrA))); |
149 | } |
150 | |
151 | } // end namespace ast_matchers |
152 | } // end namespace clang |
153 | |