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