Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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 | |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 26 | void ParentMapContext::clear() { Parents.reset(); } |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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) { |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 37 | case TK_AsIs: |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 38 | return E; |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 39 | case TK_IgnoreImplicitCastsAndParentheses: |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 40 | return E->IgnoreParenImpCasts(); |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 41 | case TK_IgnoreUnlessSpelledInSource: |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 42 | return E->IgnoreUnlessSpelledInSource(); |
| 43 | } |
| 44 | llvm_unreachable("Invalid Traversal type!"); |
| 45 | } |
| 46 | |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 47 | DynTypedNode ParentMapContext::traverseIgnored(const DynTypedNode &N) const { |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 48 | if (const auto *E = N.get<Expr>()) { |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 49 | return DynTypedNode::create(*traverseIgnored(E)); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 50 | } |
| 51 | return N; |
| 52 | } |
| 53 | |
| 54 | class ParentMapContext::ParentMap { |
| 55 | /// Contains parents of a node. |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 56 | using ParentVector = llvm::SmallVector<DynTypedNode, 2>; |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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. |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 61 | using ParentMapPointers = |
| 62 | llvm::DenseMap<const void *, |
| 63 | llvm::PointerUnion<const Decl *, const Stmt *, |
| 64 | DynTypedNode *, ParentVector *>>; |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 65 | |
| 66 | /// Parent map for nodes without pointer identity. We store a full |
| 67 | /// DynTypedNode for all keys. |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 68 | using ParentMapOtherNodes = |
| 69 | llvm::DenseMap<DynTypedNode, |
| 70 | llvm::PointerUnion<const Decl *, const Stmt *, |
| 71 | DynTypedNode *, ParentVector *>>; |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 72 | |
| 73 | ParentMapPointers PointerParents; |
| 74 | ParentMapOtherNodes OtherParents; |
| 75 | class ASTVisitor; |
| 76 | |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 77 | static DynTypedNode |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 78 | getSingleDynTypedNodeFromParentMap(ParentMapPointers::mapped_type U) { |
| 79 | if (const auto *D = U.dyn_cast<const Decl *>()) |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 80 | return DynTypedNode::create(*D); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 81 | if (const auto *S = U.dyn_cast<const Stmt *>()) |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 82 | return DynTypedNode::create(*S); |
| 83 | return *U.get<DynTypedNode *>(); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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()) { |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 91 | return llvm::ArrayRef<DynTypedNode>(); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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) { |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 103 | if (Entry.second.is<DynTypedNode *>()) { |
| 104 | delete Entry.second.get<DynTypedNode *>(); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 105 | } else if (Entry.second.is<ParentVector *>()) { |
| 106 | delete Entry.second.get<ParentVector *>(); |
| 107 | } |
| 108 | } |
| 109 | for (const auto &Entry : OtherParents) { |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 110 | if (Entry.second.is<DynTypedNode *>()) { |
| 111 | delete Entry.second.get<DynTypedNode *>(); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 112 | } else if (Entry.second.is<ParentVector *>()) { |
| 113 | delete Entry.second.get<ParentVector *>(); |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 118 | DynTypedNodeList getParents(TraversalKind TK, const DynTypedNode &Node) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 119 | if (Node.getNodeKind().hasPointerIdentity()) { |
| 120 | auto ParentList = |
| 121 | getDynNodeFromMap(Node.getMemoizationData(), PointerParents); |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 122 | if (ParentList.size() == 1 && TK == TK_IgnoreUnlessSpelledInSource) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 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 | } |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 130 | return getDynNodeFromMap(Node, OtherParents); |
| 131 | } |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 132 | |
Stephen Kelly | 0a57d14 | 2020-01-27 11:16:50 +0000 | [diff] [blame] | 133 | DynTypedNodeList AscendIgnoreUnlessSpelledInSource(const Expr *E, |
| 134 | const Expr *Child) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 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 *>(); |
Stephen Kelly | 0a57d14 | 2020-01-27 11:16:50 +0000 | [diff] [blame] | 179 | if (!S) { |
| 180 | if (auto *Vec = It->second.dyn_cast<ParentVector *>()) |
| 181 | return llvm::makeArrayRef(*Vec); |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 182 | return getSingleDynTypedNodeFromParentMap(It->second); |
Stephen Kelly | 0a57d14 | 2020-01-27 11:16:50 +0000 | [diff] [blame] | 183 | } |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 184 | const auto *P = dyn_cast<Expr>(S); |
| 185 | if (!P) |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 186 | return DynTypedNode::create(*S); |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 187 | Child = E; |
| 188 | E = P; |
| 189 | } |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 190 | return DynTypedNode::create(*E); |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 191 | } |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 192 | }; |
| 193 | |
| 194 | /// Template specializations to abstract away from pointers and TypeLocs. |
| 195 | /// @{ |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 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); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 201 | } |
| 202 | template <> |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 203 | DynTypedNode createDynTypedNode(const NestedNameSpecifierLoc &Node) { |
| 204 | return DynTypedNode::create(Node); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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: |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 219 | ASTVisitor(ParentMap &Map) : Map(Map) {} |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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 |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 254 | NodeOrVector = new DynTypedNode(ParentStack.back()); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 255 | } else { |
| 256 | if (!NodeOrVector.template is<ParentVector *>()) { |
| 257 | auto *Vector = new ParentVector( |
| 258 | 1, getSingleDynTypedNodeFromParentMap(NodeOrVector)); |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 259 | delete NodeOrVector.template dyn_cast<DynTypedNode *>(); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 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) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 288 | return TraverseNode(StmtNode, StmtNode, |
| 289 | [&] { return VisitorBase::TraverseStmt(StmtNode); }, |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 290 | &Map.PointerParents); |
| 291 | } |
| 292 | |
| 293 | bool TraverseTypeLoc(TypeLoc TypeLocNode) { |
| 294 | return TraverseNode( |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 295 | TypeLocNode, DynTypedNode::create(TypeLocNode), |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 296 | [&] { return VisitorBase::TraverseTypeLoc(TypeLocNode); }, |
| 297 | &Map.OtherParents); |
| 298 | } |
| 299 | |
| 300 | bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLocNode) { |
| 301 | return TraverseNode( |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 302 | NNSLocNode, DynTypedNode::create(NNSLocNode), |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 303 | [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); }, |
| 304 | &Map.OtherParents); |
| 305 | } |
| 306 | |
| 307 | ParentMap ⤅ |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 308 | llvm::SmallVector<DynTypedNode, 16> ParentStack; |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 309 | }; |
| 310 | |
| 311 | ParentMapContext::ParentMap::ParentMap(ASTContext &Ctx) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 312 | ASTVisitor(*this).TraverseAST(Ctx); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 313 | } |
| 314 | |
Reid Kleckner | cd62511 | 2020-02-12 11:34:13 -0800 | [diff] [blame] | 315 | DynTypedNodeList ParentMapContext::getParents(const DynTypedNode &Node) { |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 316 | if (!Parents) |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 317 | // We build the parent map for the traversal scope (usually whole TU), as |
| 318 | // hasAncestor can escape any subtree. |
Stephen Kelly | f29204d | 2020-01-24 23:46:54 +0000 | [diff] [blame] | 319 | Parents = std::make_unique<ParentMap>(ASTCtx); |
| 320 | return Parents->getParents(getTraversalKind(), Node); |
Reid Kleckner | 8a81daa | 2019-12-09 17:03:47 -0800 | [diff] [blame] | 321 | } |