1 //===- ParentMapContext.cpp - Map of parents using DynTypedNode -*- 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 //===----------------------------------------------------------------------===//
9 // Similar to ParentMap.cpp, but generalizes to non-Stmt nodes, which can have
12 //===----------------------------------------------------------------------===//
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"
20 using namespace clang
;
22 ParentMapContext::ParentMapContext(ASTContext
&Ctx
) : ASTCtx(Ctx
) {}
24 ParentMapContext::~ParentMapContext() = default;
26 void ParentMapContext::clear() { Parents
.reset(); }
28 const Expr
*ParentMapContext::traverseIgnored(const Expr
*E
) const {
29 return traverseIgnored(const_cast<Expr
*>(E
));
32 Expr
*ParentMapContext::traverseIgnored(Expr
*E
) const {
39 case TK_IgnoreUnlessSpelledInSource
:
40 return E
->IgnoreUnlessSpelledInSource();
42 llvm_unreachable("Invalid Traversal type!");
45 DynTypedNode
ParentMapContext::traverseIgnored(const DynTypedNode
&N
) const {
46 if (const auto *E
= N
.get
<Expr
>()) {
47 return DynTypedNode::create(*traverseIgnored(E
));
52 template <typename T
, typename
... U
>
53 std::tuple
<bool, DynTypedNodeList
, const T
*, const U
*...>
54 matchParents(const DynTypedNodeList
&NodeList
,
55 ParentMapContext::ParentMap
*ParentMap
);
57 template <typename
, typename
...> struct MatchParents
;
59 class ParentMapContext::ParentMap
{
61 template <typename
, typename
...> friend struct ::MatchParents
;
63 /// Contains parents of a node.
64 using ParentVector
= llvm::SmallVector
<DynTypedNode
, 2>;
66 /// Maps from a node to its parents. This is used for nodes that have
67 /// pointer identity only, which are more common and we can save space by
68 /// only storing a unique pointer to them.
69 using ParentMapPointers
=
70 llvm::DenseMap
<const void *,
71 llvm::PointerUnion
<const Decl
*, const Stmt
*,
72 DynTypedNode
*, ParentVector
*>>;
74 /// Parent map for nodes without pointer identity. We store a full
75 /// DynTypedNode for all keys.
76 using ParentMapOtherNodes
=
77 llvm::DenseMap
<DynTypedNode
,
78 llvm::PointerUnion
<const Decl
*, const Stmt
*,
79 DynTypedNode
*, ParentVector
*>>;
81 ParentMapPointers PointerParents
;
82 ParentMapOtherNodes OtherParents
;
86 getSingleDynTypedNodeFromParentMap(ParentMapPointers::mapped_type U
) {
87 if (const auto *D
= U
.dyn_cast
<const Decl
*>())
88 return DynTypedNode::create(*D
);
89 if (const auto *S
= U
.dyn_cast
<const Stmt
*>())
90 return DynTypedNode::create(*S
);
91 return *U
.get
<DynTypedNode
*>();
94 template <typename NodeTy
, typename MapTy
>
95 static DynTypedNodeList
getDynNodeFromMap(const NodeTy
&Node
,
97 auto I
= Map
.find(Node
);
99 return llvm::ArrayRef
<DynTypedNode
>();
101 if (const auto *V
= I
->second
.template dyn_cast
<ParentVector
*>()) {
102 return llvm::ArrayRef(*V
);
104 return getSingleDynTypedNodeFromParentMap(I
->second
);
108 ParentMap(ASTContext
&Ctx
);
110 for (const auto &Entry
: PointerParents
) {
111 if (Entry
.second
.is
<DynTypedNode
*>()) {
112 delete Entry
.second
.get
<DynTypedNode
*>();
113 } else if (Entry
.second
.is
<ParentVector
*>()) {
114 delete Entry
.second
.get
<ParentVector
*>();
117 for (const auto &Entry
: OtherParents
) {
118 if (Entry
.second
.is
<DynTypedNode
*>()) {
119 delete Entry
.second
.get
<DynTypedNode
*>();
120 } else if (Entry
.second
.is
<ParentVector
*>()) {
121 delete Entry
.second
.get
<ParentVector
*>();
126 DynTypedNodeList
getParents(TraversalKind TK
, const DynTypedNode
&Node
) {
127 if (Node
.getNodeKind().hasPointerIdentity()) {
129 getDynNodeFromMap(Node
.getMemoizationData(), PointerParents
);
130 if (ParentList
.size() > 0 && TK
== TK_IgnoreUnlessSpelledInSource
) {
132 const auto *ChildExpr
= Node
.get
<Expr
>();
135 // Don't match explicit node types because different stdlib
136 // implementations implement this in different ways and have
137 // different intermediate nodes.
138 // Look up 4 levels for a cxxRewrittenBinaryOperator as that is
139 // enough for the major stdlib implementations.
140 auto RewrittenBinOpParentsList
= ParentList
;
142 while (ChildExpr
&& RewrittenBinOpParentsList
.size() == 1 &&
144 const auto *S
= RewrittenBinOpParentsList
[0].get
<Stmt
>();
148 const auto *RWBO
= dyn_cast
<CXXRewrittenBinaryOperator
>(S
);
150 RewrittenBinOpParentsList
= getDynNodeFromMap(S
, PointerParents
);
153 if (RWBO
->getLHS()->IgnoreUnlessSpelledInSource() != ChildExpr
&&
154 RWBO
->getRHS()->IgnoreUnlessSpelledInSource() != ChildExpr
)
156 return DynTypedNode::create(*RWBO
);
160 const auto *ParentExpr
= ParentList
[0].get
<Expr
>();
161 if (ParentExpr
&& ChildExpr
)
162 return AscendIgnoreUnlessSpelledInSource(ParentExpr
, ChildExpr
);
166 matchParents
<DeclStmt
, CXXForRangeStmt
>(ParentList
, this);
167 if (std::get
<bool>(AncestorNodes
) &&
168 std::get
<const CXXForRangeStmt
*>(AncestorNodes
)
169 ->getLoopVarStmt() ==
170 std::get
<const DeclStmt
*>(AncestorNodes
))
171 return std::get
<DynTypedNodeList
>(AncestorNodes
);
174 auto AncestorNodes
= matchParents
<VarDecl
, DeclStmt
, CXXForRangeStmt
>(
176 if (std::get
<bool>(AncestorNodes
) &&
177 std::get
<const CXXForRangeStmt
*>(AncestorNodes
)
179 std::get
<const DeclStmt
*>(AncestorNodes
))
180 return std::get
<DynTypedNodeList
>(AncestorNodes
);
184 matchParents
<CXXMethodDecl
, CXXRecordDecl
, LambdaExpr
>(ParentList
,
186 if (std::get
<bool>(AncestorNodes
))
187 return std::get
<DynTypedNodeList
>(AncestorNodes
);
191 matchParents
<FunctionTemplateDecl
, CXXRecordDecl
, LambdaExpr
>(
193 if (std::get
<bool>(AncestorNodes
))
194 return std::get
<DynTypedNodeList
>(AncestorNodes
);
199 return getDynNodeFromMap(Node
, OtherParents
);
202 DynTypedNodeList
AscendIgnoreUnlessSpelledInSource(const Expr
*E
,
205 auto ShouldSkip
= [](const Expr
*E
, const Expr
*Child
) {
206 if (isa
<ImplicitCastExpr
>(E
))
209 if (isa
<FullExpr
>(E
))
212 if (isa
<MaterializeTemporaryExpr
>(E
))
215 if (isa
<CXXBindTemporaryExpr
>(E
))
218 if (isa
<ParenExpr
>(E
))
221 if (isa
<ExprWithCleanups
>(E
))
224 auto SR
= Child
->getSourceRange();
226 if (const auto *C
= dyn_cast
<CXXFunctionalCastExpr
>(E
)) {
227 if (C
->getSourceRange() == SR
)
231 if (const auto *C
= dyn_cast
<CXXConstructExpr
>(E
)) {
232 if (C
->getSourceRange() == SR
|| C
->isElidable())
236 if (const auto *C
= dyn_cast
<CXXMemberCallExpr
>(E
)) {
237 if (C
->getSourceRange() == SR
)
241 if (const auto *C
= dyn_cast
<MemberExpr
>(E
)) {
242 if (C
->getSourceRange() == SR
)
248 while (ShouldSkip(E
, Child
)) {
249 auto It
= PointerParents
.find(E
);
250 if (It
== PointerParents
.end())
252 const auto *S
= It
->second
.dyn_cast
<const Stmt
*>();
254 if (auto *Vec
= It
->second
.dyn_cast
<ParentVector
*>())
255 return llvm::ArrayRef(*Vec
);
256 return getSingleDynTypedNodeFromParentMap(It
->second
);
258 const auto *P
= dyn_cast
<Expr
>(S
);
260 return DynTypedNode::create(*S
);
264 return DynTypedNode::create(*E
);
268 template <typename T
, typename
... U
> struct MatchParents
{
269 static std::tuple
<bool, DynTypedNodeList
, const T
*, const U
*...>
270 match(const DynTypedNodeList
&NodeList
,
271 ParentMapContext::ParentMap
*ParentMap
) {
272 if (const auto *TypedNode
= NodeList
[0].get
<T
>()) {
273 auto NextParentList
=
274 ParentMap
->getDynNodeFromMap(TypedNode
, ParentMap
->PointerParents
);
275 if (NextParentList
.size() == 1) {
276 auto TailTuple
= MatchParents
<U
...>::match(NextParentList
, ParentMap
);
277 if (std::get
<bool>(TailTuple
)) {
279 [TypedNode
](bool, DynTypedNodeList NodeList
, auto... TupleTail
) {
280 return std::make_tuple(true, NodeList
, TypedNode
, TupleTail
...);
286 return std::tuple_cat(std::make_tuple(false, NodeList
),
287 std::tuple
<const T
*, const U
*...>());
291 template <typename T
> struct MatchParents
<T
> {
292 static std::tuple
<bool, DynTypedNodeList
, const T
*>
293 match(const DynTypedNodeList
&NodeList
,
294 ParentMapContext::ParentMap
*ParentMap
) {
295 if (const auto *TypedNode
= NodeList
[0].get
<T
>()) {
296 auto NextParentList
=
297 ParentMap
->getDynNodeFromMap(TypedNode
, ParentMap
->PointerParents
);
298 if (NextParentList
.size() == 1)
299 return std::make_tuple(true, NodeList
, TypedNode
);
301 return std::make_tuple(false, NodeList
, nullptr);
305 template <typename T
, typename
... U
>
306 std::tuple
<bool, DynTypedNodeList
, const T
*, const U
*...>
307 matchParents(const DynTypedNodeList
&NodeList
,
308 ParentMapContext::ParentMap
*ParentMap
) {
309 return MatchParents
<T
, U
...>::match(NodeList
, ParentMap
);
312 /// Template specializations to abstract away from pointers and TypeLocs.
314 template <typename T
> static DynTypedNode
createDynTypedNode(const T
&Node
) {
315 return DynTypedNode::create(*Node
);
317 template <> DynTypedNode
createDynTypedNode(const TypeLoc
&Node
) {
318 return DynTypedNode::create(Node
);
321 DynTypedNode
createDynTypedNode(const NestedNameSpecifierLoc
&Node
) {
322 return DynTypedNode::create(Node
);
324 template <> DynTypedNode
createDynTypedNode(const ObjCProtocolLoc
&Node
) {
325 return DynTypedNode::create(Node
);
329 /// A \c RecursiveASTVisitor that builds a map from nodes to their
330 /// parents as defined by the \c RecursiveASTVisitor.
332 /// Note that the relationship described here is purely in terms of AST
333 /// traversal - there are other relationships (for example declaration context)
334 /// in the AST that are better modeled by special matchers.
335 class ParentMapContext::ParentMap::ASTVisitor
336 : public RecursiveASTVisitor
<ASTVisitor
> {
338 ASTVisitor(ParentMap
&Map
) : Map(Map
) {}
341 friend class RecursiveASTVisitor
<ASTVisitor
>;
343 using VisitorBase
= RecursiveASTVisitor
<ASTVisitor
>;
345 bool shouldVisitTemplateInstantiations() const { return true; }
347 bool shouldVisitImplicitCode() const { return true; }
349 /// Record the parent of the node we're visiting.
350 /// MapNode is the child, the parent is on top of ParentStack.
351 /// Parents is the parent storage (either PointerParents or OtherParents).
352 template <typename MapNodeTy
, typename MapTy
>
353 void addParent(MapNodeTy MapNode
, MapTy
*Parents
) {
354 if (ParentStack
.empty())
357 // FIXME: Currently we add the same parent multiple times, but only
358 // when no memoization data is available for the type.
359 // For example when we visit all subexpressions of template
360 // instantiations; this is suboptimal, but benign: the only way to
361 // visit those is with hasAncestor / hasParent, and those do not create
363 // The plan is to enable DynTypedNode to be storable in a map or hash
364 // map. The main problem there is to implement hash functions /
365 // comparison operators for all types that DynTypedNode supports that
366 // do not have pointer identity.
367 auto &NodeOrVector
= (*Parents
)[MapNode
];
368 if (NodeOrVector
.isNull()) {
369 if (const auto *D
= ParentStack
.back().get
<Decl
>())
371 else if (const auto *S
= ParentStack
.back().get
<Stmt
>())
374 NodeOrVector
= new DynTypedNode(ParentStack
.back());
376 if (!NodeOrVector
.template is
<ParentVector
*>()) {
377 auto *Vector
= new ParentVector(
378 1, getSingleDynTypedNodeFromParentMap(NodeOrVector
));
379 delete NodeOrVector
.template dyn_cast
<DynTypedNode
*>();
380 NodeOrVector
= Vector
;
383 auto *Vector
= NodeOrVector
.template get
<ParentVector
*>();
384 // Skip duplicates for types that have memoization data.
385 // We must check that the type has memoization data before calling
386 // llvm::is_contained() because DynTypedNode::operator== can't compare all
388 bool Found
= ParentStack
.back().getMemoizationData() &&
389 llvm::is_contained(*Vector
, ParentStack
.back());
391 Vector
->push_back(ParentStack
.back());
395 template <typename T
> static bool isNull(T Node
) { return !Node
; }
396 static bool isNull(ObjCProtocolLoc Node
) { return false; }
398 template <typename T
, typename MapNodeTy
, typename BaseTraverseFn
,
400 bool TraverseNode(T Node
, MapNodeTy MapNode
, BaseTraverseFn BaseTraverse
,
404 addParent(MapNode
, Parents
);
405 ParentStack
.push_back(createDynTypedNode(Node
));
406 bool Result
= BaseTraverse();
407 ParentStack
.pop_back();
411 bool TraverseDecl(Decl
*DeclNode
) {
413 DeclNode
, DeclNode
, [&] { return VisitorBase::TraverseDecl(DeclNode
); },
414 &Map
.PointerParents
);
416 bool TraverseTypeLoc(TypeLoc TypeLocNode
) {
418 TypeLocNode
, DynTypedNode::create(TypeLocNode
),
419 [&] { return VisitorBase::TraverseTypeLoc(TypeLocNode
); },
422 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLocNode
) {
424 NNSLocNode
, DynTypedNode::create(NNSLocNode
),
425 [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode
); },
428 bool TraverseAttr(Attr
*AttrNode
) {
430 AttrNode
, AttrNode
, [&] { return VisitorBase::TraverseAttr(AttrNode
); },
431 &Map
.PointerParents
);
433 bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLocNode
) {
435 ProtocolLocNode
, DynTypedNode::create(ProtocolLocNode
),
436 [&] { return VisitorBase::TraverseObjCProtocolLoc(ProtocolLocNode
); },
440 // Using generic TraverseNode for Stmt would prevent data-recursion.
441 bool dataTraverseStmtPre(Stmt
*StmtNode
) {
442 addParent(StmtNode
, &Map
.PointerParents
);
443 ParentStack
.push_back(DynTypedNode::create(*StmtNode
));
446 bool dataTraverseStmtPost(Stmt
*StmtNode
) {
447 ParentStack
.pop_back();
452 llvm::SmallVector
<DynTypedNode
, 16> ParentStack
;
455 ParentMapContext::ParentMap::ParentMap(ASTContext
&Ctx
) {
456 ASTVisitor(*this).TraverseAST(Ctx
);
459 DynTypedNodeList
ParentMapContext::getParents(const DynTypedNode
&Node
) {
461 // We build the parent map for the traversal scope (usually whole TU), as
462 // hasAncestor can escape any subtree.
463 Parents
= std::make_unique
<ParentMap
>(ASTCtx
);
464 return Parents
->getParents(getTraversalKind(), Node
);