blob: 6287bc8c1d72bc8acb3a2150bc97dfef2e19064e [file] [log] [blame]
Reid Kleckner8a81daa2019-12-09 17:03:47 -08001//===- ParentMapContext.cpp - Map of parents using DynTypedNode -*- 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// Similar to ParentMap.cpp, but generalizes to non-Stmt nodes, which can have
10// multiple parents.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/AST/ParentMapContext.h"
15#include "clang/AST/RecursiveASTVisitor.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/Expr.h"
18#include "clang/AST/TemplateBase.h"
19
20using namespace clang;
21
22ParentMapContext::ParentMapContext(ASTContext &Ctx) : ASTCtx(Ctx) {}
23
24ParentMapContext::~ParentMapContext() = default;
25
26void ParentMapContext::clear() { Parents.clear(); }
27
28const Expr *ParentMapContext::traverseIgnored(const Expr *E) const {
29 return traverseIgnored(const_cast<Expr *>(E));
30}
31
32Expr *ParentMapContext::traverseIgnored(Expr *E) const {
33 if (!E)
34 return nullptr;
35
36 switch (Traversal) {
37 case ast_type_traits::TK_AsIs:
38 return E;
39 case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses:
40 return E->IgnoreParenImpCasts();
41 case ast_type_traits::TK_IgnoreUnlessSpelledInSource:
42 return E->IgnoreUnlessSpelledInSource();
43 }
44 llvm_unreachable("Invalid Traversal type!");
45}
46
47ast_type_traits::DynTypedNode
48ParentMapContext::traverseIgnored(const ast_type_traits::DynTypedNode &N) const {
49 if (const auto *E = N.get<Expr>()) {
50 return ast_type_traits::DynTypedNode::create(*traverseIgnored(E));
51 }
52 return N;
53}
54
55class ParentMapContext::ParentMap {
56 /// Contains parents of a node.
57 using ParentVector = llvm::SmallVector<ast_type_traits::DynTypedNode, 2>;
58
59 /// Maps from a node to its parents. This is used for nodes that have
60 /// pointer identity only, which are more common and we can save space by
61 /// only storing a unique pointer to them.
62 using ParentMapPointers = llvm::DenseMap<
63 const void *,
64 llvm::PointerUnion<const Decl *, const Stmt *,
65 ast_type_traits::DynTypedNode *, ParentVector *>>;
66
67 /// Parent map for nodes without pointer identity. We store a full
68 /// DynTypedNode for all keys.
69 using ParentMapOtherNodes = llvm::DenseMap<
70 ast_type_traits::DynTypedNode,
71 llvm::PointerUnion<const Decl *, const Stmt *,
72 ast_type_traits::DynTypedNode *, ParentVector *>>;
73
74 ParentMapPointers PointerParents;
75 ParentMapOtherNodes OtherParents;
76 class ASTVisitor;
77
78 static ast_type_traits::DynTypedNode
79 getSingleDynTypedNodeFromParentMap(ParentMapPointers::mapped_type U) {
80 if (const auto *D = U.dyn_cast<const Decl *>())
81 return ast_type_traits::DynTypedNode::create(*D);
82 if (const auto *S = U.dyn_cast<const Stmt *>())
83 return ast_type_traits::DynTypedNode::create(*S);
84 return *U.get<ast_type_traits::DynTypedNode *>();
85 }
86
87 template <typename NodeTy, typename MapTy>
88 static DynTypedNodeList getDynNodeFromMap(const NodeTy &Node,
89 const MapTy &Map) {
90 auto I = Map.find(Node);
91 if (I == Map.end()) {
92 return llvm::ArrayRef<ast_type_traits::DynTypedNode>();
93 }
94 if (const auto *V = I->second.template dyn_cast<ParentVector *>()) {
95 return llvm::makeArrayRef(*V);
96 }
97 return getSingleDynTypedNodeFromParentMap(I->second);
98 }
99
100public:
101 ParentMap(ASTContext &Ctx);
102 ~ParentMap() {
103 for (const auto &Entry : PointerParents) {
104 if (Entry.second.is<ast_type_traits::DynTypedNode *>()) {
105 delete Entry.second.get<ast_type_traits::DynTypedNode *>();
106 } else if (Entry.second.is<ParentVector *>()) {
107 delete Entry.second.get<ParentVector *>();
108 }
109 }
110 for (const auto &Entry : OtherParents) {
111 if (Entry.second.is<ast_type_traits::DynTypedNode *>()) {
112 delete Entry.second.get<ast_type_traits::DynTypedNode *>();
113 } else if (Entry.second.is<ParentVector *>()) {
114 delete Entry.second.get<ParentVector *>();
115 }
116 }
117 }
118
119 DynTypedNodeList getParents(const ast_type_traits::DynTypedNode &Node) {
120 if (Node.getNodeKind().hasPointerIdentity())
121 return getDynNodeFromMap(Node.getMemoizationData(), PointerParents);
122 return getDynNodeFromMap(Node, OtherParents);
123 }
124};
125
126/// Template specializations to abstract away from pointers and TypeLocs.
127/// @{
128template <typename T>
129static ast_type_traits::DynTypedNode createDynTypedNode(const T &Node) {
130 return ast_type_traits::DynTypedNode::create(*Node);
131}
132template <>
133ast_type_traits::DynTypedNode createDynTypedNode(const TypeLoc &Node) {
134 return ast_type_traits::DynTypedNode::create(Node);
135}
136template <>
137ast_type_traits::DynTypedNode
138createDynTypedNode(const NestedNameSpecifierLoc &Node) {
139 return ast_type_traits::DynTypedNode::create(Node);
140}
141/// @}
142
143/// A \c RecursiveASTVisitor that builds a map from nodes to their
144/// parents as defined by the \c RecursiveASTVisitor.
145///
146/// Note that the relationship described here is purely in terms of AST
147/// traversal - there are other relationships (for example declaration context)
148/// in the AST that are better modeled by special matchers.
149///
150/// FIXME: Currently only builds up the map using \c Stmt and \c Decl nodes.
151class ParentMapContext::ParentMap::ASTVisitor
152 : public RecursiveASTVisitor<ASTVisitor> {
153public:
154 ASTVisitor(ParentMap &Map, ParentMapContext &MapCtx)
155 : Map(Map), MapCtx(MapCtx) {}
156
157private:
158 friend class RecursiveASTVisitor<ASTVisitor>;
159
160 using VisitorBase = RecursiveASTVisitor<ASTVisitor>;
161
162 bool shouldVisitTemplateInstantiations() const { return true; }
163
164 bool shouldVisitImplicitCode() const { return true; }
165
166 template <typename T, typename MapNodeTy, typename BaseTraverseFn,
167 typename MapTy>
168 bool TraverseNode(T Node, MapNodeTy MapNode, BaseTraverseFn BaseTraverse,
169 MapTy *Parents) {
170 if (!Node)
171 return true;
172 if (ParentStack.size() > 0) {
173 // FIXME: Currently we add the same parent multiple times, but only
174 // when no memoization data is available for the type.
175 // For example when we visit all subexpressions of template
176 // instantiations; this is suboptimal, but benign: the only way to
177 // visit those is with hasAncestor / hasParent, and those do not create
178 // new matches.
179 // The plan is to enable DynTypedNode to be storable in a map or hash
180 // map. The main problem there is to implement hash functions /
181 // comparison operators for all types that DynTypedNode supports that
182 // do not have pointer identity.
183 auto &NodeOrVector = (*Parents)[MapNode];
184 if (NodeOrVector.isNull()) {
185 if (const auto *D = ParentStack.back().get<Decl>())
186 NodeOrVector = D;
187 else if (const auto *S = ParentStack.back().get<Stmt>())
188 NodeOrVector = S;
189 else
190 NodeOrVector = new ast_type_traits::DynTypedNode(ParentStack.back());
191 } else {
192 if (!NodeOrVector.template is<ParentVector *>()) {
193 auto *Vector = new ParentVector(
194 1, getSingleDynTypedNodeFromParentMap(NodeOrVector));
195 delete NodeOrVector
196 .template dyn_cast<ast_type_traits::DynTypedNode *>();
197 NodeOrVector = Vector;
198 }
199
200 auto *Vector = NodeOrVector.template get<ParentVector *>();
201 // Skip duplicates for types that have memoization data.
202 // We must check that the type has memoization data before calling
203 // std::find() because DynTypedNode::operator== can't compare all
204 // types.
205 bool Found = ParentStack.back().getMemoizationData() &&
206 std::find(Vector->begin(), Vector->end(),
207 ParentStack.back()) != Vector->end();
208 if (!Found)
209 Vector->push_back(ParentStack.back());
210 }
211 }
212 ParentStack.push_back(createDynTypedNode(Node));
213 bool Result = BaseTraverse();
214 ParentStack.pop_back();
215 return Result;
216 }
217
218 bool TraverseDecl(Decl *DeclNode) {
219 return TraverseNode(
220 DeclNode, DeclNode, [&] { return VisitorBase::TraverseDecl(DeclNode); },
221 &Map.PointerParents);
222 }
223
224 bool TraverseStmt(Stmt *StmtNode) {
225 Stmt *FilteredNode = StmtNode;
226 if (auto *ExprNode = dyn_cast_or_null<Expr>(FilteredNode))
227 FilteredNode = MapCtx.traverseIgnored(ExprNode);
228 return TraverseNode(FilteredNode, FilteredNode,
229 [&] { return VisitorBase::TraverseStmt(FilteredNode); },
230 &Map.PointerParents);
231 }
232
233 bool TraverseTypeLoc(TypeLoc TypeLocNode) {
234 return TraverseNode(
235 TypeLocNode, ast_type_traits::DynTypedNode::create(TypeLocNode),
236 [&] { return VisitorBase::TraverseTypeLoc(TypeLocNode); },
237 &Map.OtherParents);
238 }
239
240 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLocNode) {
241 return TraverseNode(
242 NNSLocNode, ast_type_traits::DynTypedNode::create(NNSLocNode),
243 [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); },
244 &Map.OtherParents);
245 }
246
247 ParentMap &Map;
248 ParentMapContext &MapCtx;
249 llvm::SmallVector<ast_type_traits::DynTypedNode, 16> ParentStack;
250};
251
252ParentMapContext::ParentMap::ParentMap(ASTContext &Ctx) {
253 ASTVisitor(*this, Ctx.getParentMapContext()).TraverseAST(Ctx);
254}
255
256DynTypedNodeList
257ParentMapContext::getParents(const ast_type_traits::DynTypedNode &Node) {
258 std::unique_ptr<ParentMap> &P = Parents[Traversal];
259 if (!P)
260 // We build the parent map for the traversal scope (usually whole TU), as
261 // hasAncestor can escape any subtree.
262 P = std::make_unique<ParentMap>(ASTCtx);
263 return P->getParents(Node);
264}
265