clang 24.0.0git
ParentMapContext.cpp
Go to the documentation of this file.
1//===- 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
15#include "clang/AST/Decl.h"
16#include "clang/AST/Expr.h"
19#include "llvm/ADT/SmallPtrSet.h"
20
21using namespace clang;
22
23template <typename T, typename... U>
24static std::tuple<bool, DynTypedNodeList, const T *, const U *...>
25matchParents(const DynTypedNodeList &NodeList,
27
28template <typename, typename...> struct MatchParents;
29
31
32 template <typename, typename...> friend struct ::MatchParents;
33
34 /// Contains parents of a node.
35 class ParentVector {
36 public:
37 ParentVector() = default;
38 explicit ParentVector(size_t N, const DynTypedNode &Value) {
39 Items.reserve(N);
40 for (; N > 0; --N)
41 push_back(Value);
42 }
43 bool contains(const DynTypedNode &Value) const {
44 const void *Identity = Value.getMemoizationData();
45 assert(Identity);
46 return Dedup.contains(Identity);
47 }
48 void push_back(const DynTypedNode &Value) {
49 const void *Identity = Value.getMemoizationData();
50 if (!Identity || Dedup.insert(Identity).second) {
51 Items.push_back(Value);
52 }
53 }
54 ArrayRef<DynTypedNode> view() const { return Items; }
55
56 private:
59 };
60
61 /// Maps from a node to its parents. This is used for nodes that have
62 /// pointer identity only, which are more common and we can save space by
63 /// only storing a unique pointer to them.
64 using ParentMapPointers =
65 llvm::DenseMap<const void *,
66 llvm::PointerUnion<const Decl *, const Stmt *,
67 DynTypedNode *, ParentVector *>>;
68
69 /// Parent map for nodes without pointer identity. We store a full
70 /// DynTypedNode for all keys.
71 using ParentMapOtherNodes =
72 llvm::DenseMap<DynTypedNode,
73 llvm::PointerUnion<const Decl *, const Stmt *,
74 DynTypedNode *, ParentVector *>>;
75
76 ParentMapPointers PointerParents;
77 ParentMapOtherNodes OtherParents;
78 class ASTVisitor;
79
80 static DynTypedNode
81 getSingleDynTypedNodeFromParentMap(ParentMapPointers::mapped_type U) {
82 if (const auto *D = dyn_cast<const Decl *>(U))
83 return DynTypedNode::create(*D);
84 if (const auto *S = dyn_cast<const Stmt *>(U))
85 return DynTypedNode::create(*S);
86 return *cast<DynTypedNode *>(U);
87 }
88
89 template <typename NodeTy, typename MapTy>
90 static DynTypedNodeList getDynNodeFromMap(const NodeTy &Node,
91 const MapTy &Map) {
92 auto I = Map.find(Node);
93 if (I == Map.end()) {
95 }
96 if (const auto *V = dyn_cast<ParentVector *>(I->second)) {
97 return V->view();
98 }
99 return getSingleDynTypedNodeFromParentMap(I->second);
100 }
101
102public:
103 ParentMap(ASTContext &Ctx);
105 for (const auto &Entry : PointerParents) {
106 if (auto *DTN = dyn_cast<DynTypedNode *>(Entry.second)) {
107 delete DTN;
108 } else if (auto *PV = dyn_cast<ParentVector *>(Entry.second)) {
109 delete PV;
110 }
111 }
112 for (const auto &Entry : OtherParents) {
113 if (auto *DTN = dyn_cast<DynTypedNode *>(Entry.second)) {
114 delete DTN;
115 } else if (auto *PV = dyn_cast<ParentVector *>(Entry.second)) {
116 delete PV;
117 }
118 }
119 }
120
122 if (Node.getNodeKind().hasPointerIdentity()) {
123 auto ParentList =
124 getDynNodeFromMap(Node.getMemoizationData(), PointerParents);
125 if (ParentList.size() > 0 && TK == TK_IgnoreUnlessSpelledInSource) {
126
127 const auto *ChildExpr = Node.get<Expr>();
128
129 {
130 // Don't match explicit node types because different stdlib
131 // implementations implement this in different ways and have
132 // different intermediate nodes.
133 // Look up 4 levels for a cxxRewrittenBinaryOperator as that is
134 // enough for the major stdlib implementations.
135 auto RewrittenBinOpParentsList = ParentList;
136 int I = 0;
137 while (ChildExpr && RewrittenBinOpParentsList.size() == 1 &&
138 I++ < 4) {
139 const auto *S = RewrittenBinOpParentsList[0].get<Stmt>();
140 if (!S)
141 break;
142
143 const auto *RWBO = dyn_cast<CXXRewrittenBinaryOperator>(S);
144 if (!RWBO) {
145 RewrittenBinOpParentsList = getDynNodeFromMap(S, PointerParents);
146 continue;
147 }
148 if (RWBO->getLHS()->IgnoreUnlessSpelledInSource() != ChildExpr &&
149 RWBO->getRHS()->IgnoreUnlessSpelledInSource() != ChildExpr)
150 break;
151 return DynTypedNode::create(*RWBO);
152 }
153 }
154
155 const auto *ParentExpr = ParentList[0].get<Expr>();
156 if (ParentExpr && ChildExpr)
157 return AscendIgnoreUnlessSpelledInSource(ParentExpr, ChildExpr);
158
159 {
160 auto AncestorNodes =
162 if (std::get<bool>(AncestorNodes) &&
163 std::get<const CXXForRangeStmt *>(AncestorNodes)
164 ->getLoopVarStmt() ==
165 std::get<const DeclStmt *>(AncestorNodes))
166 return std::get<DynTypedNodeList>(AncestorNodes);
167 }
168 {
170 ParentList, this);
171 if (std::get<bool>(AncestorNodes) &&
172 std::get<const CXXForRangeStmt *>(AncestorNodes)
173 ->getRangeStmt() ==
174 std::get<const DeclStmt *>(AncestorNodes))
175 return std::get<DynTypedNodeList>(AncestorNodes);
176 }
177 {
178 auto AncestorNodes =
180 this);
181 if (std::get<bool>(AncestorNodes))
182 return std::get<DynTypedNodeList>(AncestorNodes);
183 }
184 {
185 auto AncestorNodes =
187 ParentList, this);
188 if (std::get<bool>(AncestorNodes))
189 return std::get<DynTypedNodeList>(AncestorNodes);
190 }
191 }
192 return ParentList;
193 }
194 return getDynNodeFromMap(Node, OtherParents);
195 }
196
198 const Expr *Child) {
199
200 auto ShouldSkip = [](const Expr *E, const Expr *Child) {
202 return true;
203
204 if (isa<FullExpr>(E))
205 return true;
206
208 return true;
209
211 return true;
212
213 if (isa<ParenExpr>(E))
214 return true;
215
217 return true;
218
219 auto SR = Child->getSourceRange();
220
221 if (const auto *C = dyn_cast<CXXFunctionalCastExpr>(E)) {
222 if (C->getSourceRange() == SR)
223 return true;
224 }
225
226 if (const auto *C = dyn_cast<CXXConstructExpr>(E)) {
227 if (C->getSourceRange() == SR || C->isElidable())
228 return true;
229 }
230
231 if (const auto *C = dyn_cast<CXXMemberCallExpr>(E)) {
232 if (C->getSourceRange() == SR)
233 return true;
234 }
235
236 if (const auto *C = dyn_cast<MemberExpr>(E)) {
237 if (C->getSourceRange() == SR)
238 return true;
239 }
240 return false;
241 };
242
243 while (ShouldSkip(E, Child)) {
244 auto It = PointerParents.find(E);
245 if (It == PointerParents.end())
246 break;
247 const auto *S = dyn_cast<const Stmt *>(It->second);
248 if (!S) {
249 if (auto *Vec = dyn_cast<ParentVector *>(It->second))
250 return Vec->view();
251 return getSingleDynTypedNodeFromParentMap(It->second);
252 }
253 const auto *P = dyn_cast<Expr>(S);
254 if (!P)
255 return DynTypedNode::create(*S);
256 Child = E;
257 E = P;
258 }
259 return DynTypedNode::create(*E);
260 }
261};
262
263template <typename T, typename... U> struct MatchParents {
264 static std::tuple<bool, DynTypedNodeList, const T *, const U *...>
265 match(const DynTypedNodeList &NodeList,
267 if (const auto *TypedNode = NodeList[0].get<T>()) {
268 auto NextParentList =
269 ParentMap->getDynNodeFromMap(TypedNode, ParentMap->PointerParents);
270 if (NextParentList.size() == 1) {
271 auto TailTuple = MatchParents<U...>::match(NextParentList, ParentMap);
272 if (std::get<bool>(TailTuple)) {
273 return std::apply(
274 [TypedNode](bool, DynTypedNodeList NodeList, auto... TupleTail) {
275 return std::make_tuple(true, NodeList, TypedNode, TupleTail...);
276 },
277 TailTuple);
278 }
279 }
280 }
281 return std::tuple_cat(std::make_tuple(false, NodeList),
282 std::tuple<const T *, const U *...>());
283 }
284};
285
286template <typename T> struct MatchParents<T> {
287 static std::tuple<bool, DynTypedNodeList, const T *>
288 match(const DynTypedNodeList &NodeList,
290 if (const auto *TypedNode = NodeList[0].get<T>()) {
291 auto NextParentList =
292 ParentMap->getDynNodeFromMap(TypedNode, ParentMap->PointerParents);
293 if (NextParentList.size() == 1)
294 return std::make_tuple(true, NodeList, TypedNode);
295 }
296 return std::make_tuple(false, NodeList, nullptr);
297 }
298};
299
300template <typename T, typename... U>
301std::tuple<bool, DynTypedNodeList, const T *, const U *...>
306
308
310
311void ParentMapContext::clear() { Parents.reset(); }
312
314 return traverseIgnored(const_cast<Expr *>(E));
315}
316
318 if (!E)
319 return nullptr;
320
321 switch (Traversal) {
322 case TK_AsIs:
323 return E;
325 return E->IgnoreUnlessSpelledInSource();
326 }
327 llvm_unreachable("Invalid Traversal type!");
328}
329
330/// Template specializations to abstract away from pointers and TypeLocs.
331/// @{
332template <typename T> static DynTypedNode createDynTypedNode(const T &Node) {
333 return DynTypedNode::create(*Node);
334}
335template <> DynTypedNode createDynTypedNode(const TypeLoc &Node) {
336 return DynTypedNode::create(Node);
337}
338template <>
343 return DynTypedNode::create(Node);
344}
345/// @}
346
347/// A \c RecursiveASTVisitor that builds a map from nodes to their
348/// parents as defined by the \c RecursiveASTVisitor.
349///
350/// Note that the relationship described here is purely in terms of AST
351/// traversal - there are other relationships (for example declaration context)
352/// in the AST that are better modeled by special matchers.
354 : public RecursiveASTVisitor<ASTVisitor> {
355public:
356 ASTVisitor(ParentMap &Map) : Map(Map) {}
357
358private:
359 friend class RecursiveASTVisitor<ASTVisitor>;
360
361 using VisitorBase = RecursiveASTVisitor<ASTVisitor>;
362
363 bool shouldVisitTemplateInstantiations() const { return true; }
364
365 bool shouldVisitImplicitCode() const { return true; }
366
367 /// Record the parent of the node we're visiting.
368 /// MapNode is the child, the parent is on top of ParentStack.
369 /// Parents is the parent storage (either PointerParents or OtherParents).
370 template <typename MapNodeTy, typename MapTy>
371 void addParent(MapNodeTy MapNode, MapTy *Parents) {
372 if (ParentStack.empty())
373 return;
374
375 // FIXME: Currently we add the same parent multiple times, but only
376 // when no memoization data is available for the type.
377 // For example when we visit all subexpressions of template
378 // instantiations; this is suboptimal, but benign: the only way to
379 // visit those is with hasAncestor / hasParent, and those do not create
380 // new matches.
381 // The plan is to enable DynTypedNode to be storable in a map or hash
382 // map. The main problem there is to implement hash functions /
383 // comparison operators for all types that DynTypedNode supports that
384 // do not have pointer identity.
385 auto &NodeOrVector = (*Parents)[MapNode];
386 if (NodeOrVector.isNull()) {
387 if (const auto *D = ParentStack.back().get<Decl>())
388 NodeOrVector = D;
389 else if (const auto *S = ParentStack.back().get<Stmt>())
390 NodeOrVector = S;
391 else
392 NodeOrVector = new DynTypedNode(ParentStack.back());
393 } else {
394 if (!isa<ParentVector *>(NodeOrVector)) {
395 auto *Vector = new ParentVector(
396 1, getSingleDynTypedNodeFromParentMap(NodeOrVector));
397 delete dyn_cast<DynTypedNode *>(NodeOrVector);
398 NodeOrVector = Vector;
399 }
400
401 auto *Vector = cast<ParentVector *>(NodeOrVector);
402 // Skip duplicates for types that have memoization data.
403 // We must check that the type has memoization data before calling
404 // llvm::is_contained() because DynTypedNode::operator== can't compare all
405 // types.
406 bool Found = ParentStack.back().getMemoizationData() &&
407 llvm::is_contained(*Vector, ParentStack.back());
408 if (!Found)
409 Vector->push_back(ParentStack.back());
410 }
411 }
412
413 template <typename T> static bool isNull(T Node) { return !Node; }
414 static bool isNull(ObjCProtocolLoc Node) { return false; }
415
416 template <typename T, typename MapNodeTy, typename BaseTraverseFn,
417 typename MapTy>
418 bool TraverseNode(T Node, MapNodeTy MapNode, BaseTraverseFn BaseTraverse,
419 MapTy *Parents) {
420 if (isNull(Node))
421 return true;
422 addParent(MapNode, Parents);
423 ParentStack.push_back(createDynTypedNode(Node));
424 bool Result = BaseTraverse();
425 ParentStack.pop_back();
426 return Result;
427 }
428
429 bool TraverseDecl(Decl *DeclNode) {
430 return TraverseNode(
431 DeclNode, DeclNode, [&] { return VisitorBase::TraverseDecl(DeclNode); },
432 &Map.PointerParents);
433 }
434 bool TraverseTypeLoc(TypeLoc TypeLocNode, bool TraverseQualifier = true) {
435 return TraverseNode(
436 TypeLocNode, DynTypedNode::create(TypeLocNode),
437 [&] {
438 return VisitorBase::TraverseTypeLoc(TypeLocNode, TraverseQualifier);
439 },
440 &Map.OtherParents);
441 }
442 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLocNode) {
443 return TraverseNode(
444 NNSLocNode, DynTypedNode::create(NNSLocNode),
445 [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); },
446 &Map.OtherParents);
447 }
448 bool TraverseAttr(Attr *AttrNode) {
449 return TraverseNode(
450 AttrNode, AttrNode, [&] { return VisitorBase::TraverseAttr(AttrNode); },
451 &Map.PointerParents);
452 }
453 bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLocNode) {
454 return TraverseNode(
455 ProtocolLocNode, DynTypedNode::create(ProtocolLocNode),
456 [&] { return VisitorBase::TraverseObjCProtocolLoc(ProtocolLocNode); },
457 &Map.OtherParents);
458 }
459
460 // Using generic TraverseNode for Stmt would prevent data-recursion.
461 bool dataTraverseStmtPre(Stmt *StmtNode) {
462 addParent(StmtNode, &Map.PointerParents);
463 ParentStack.push_back(DynTypedNode::create(*StmtNode));
464 return true;
465 }
466 bool dataTraverseStmtPost(Stmt *StmtNode) {
467 ParentStack.pop_back();
468 return true;
469 }
470
471 ParentMap &Map;
472 llvm::SmallVector<DynTypedNode, 16> ParentStack;
473};
474
478
480 if (!Parents)
481 // We build the parent map for the traversal scope (usually whole TU), as
482 // hasAncestor can escape any subtree.
483 Parents = std::make_unique<ParentMap>(ASTCtx);
484 return Parents->getParents(getTraversalKind(), Node);
485}
#define V(N, I)
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc QualifierLoc)
static DynTypedNode createDynTypedNode(const T &Node)
Template specializations to abstract away from pointers and TypeLocs.
static std::tuple< bool, DynTypedNodeList, const T *, const U *... > matchParents(const DynTypedNodeList &NodeList, ParentMapContext::ParentMap *ParentMap)
llvm::DenseMap< Stmt *, Stmt * > MapTy
Definition ParentMap.cpp:21
static bool contains(const std::set< tok::TokenKind > &Terminators, const Token &Tok)
A RecursiveASTVisitor that builds a map from nodes to their parents as defined by the RecursiveASTVis...
DynTypedNodeList getParents(TraversalKind TK, const DynTypedNode &Node)
DynTypedNodeList AscendIgnoreUnlessSpelledInSource(const Expr *E, const Expr *Child)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:245
constexpr bool hasPointerIdentity() const
Check if the given ASTNodeKind identifies a type that offers pointer identity.
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
A dynamically typed AST node container.
ASTNodeKind getNodeKind() const
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
const void * getMemoizationData() const
Returns a pointer that identifies the stored AST node.
This represents one expression.
Definition Expr.h:114
Expr * IgnoreUnlessSpelledInSource()
Skip past any invisible AST nodes which might surround this statement, such as ExprWithCleanups or Im...
Definition Expr.cpp:3182
A C++ nested-name-specifier augmented with source location information.
const Expr * traverseIgnored(const Expr *E) const
void clear()
Clear parent maps.
TraversalKind getTraversalKind() const
DynTypedNodeList getParents(const NodeT &Node)
Returns the parents of the given node (within the traversal scope).
ParentMapContext(ASTContext &Ctx)
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc)
bool TraverseAST(ASTContext &AST)
Recursively visits an entire AST, starting from the TranslationUnitDecl.
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS)
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier=true)
Stmt - This represents one statement.
Definition Stmt.h:85
Base wrapper for a particular "section" of type source info.
Definition TypeLoc.h:59
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Vector
'vector' clause, allowed on 'loop', Combined, and 'routine' directives.
TraversalKind
Defines how we descend a level in the AST when we pass through expressions.
@ TK_AsIs
Will traverse all child nodes.
@ TK_IgnoreUnlessSpelledInSource
Ignore AST nodes not written in the source.
@ Result
The result type of a method or function.
Definition TypeBase.h:906
const FunctionProtoType * T
U cast(CodeGen::Address addr)
Definition Address.h:327
static std::tuple< bool, DynTypedNodeList, const T * > match(const DynTypedNodeList &NodeList, ParentMapContext::ParentMap *ParentMap)
static std::tuple< bool, DynTypedNodeList, const T *, const U *... > match(const DynTypedNodeList &NodeList, ParentMapContext::ParentMap *ParentMap)