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 | |
| 26 | void ParentMapContext::clear() { Parents.clear(); } |
| 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 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 | |
| 47 | ast_type_traits::DynTypedNode |
| 48 | ParentMapContext::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 | |
| 55 | class 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 | |
| 100 | public: |
| 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 | /// @{ |
| 128 | template <typename T> |
| 129 | static ast_type_traits::DynTypedNode createDynTypedNode(const T &Node) { |
| 130 | return ast_type_traits::DynTypedNode::create(*Node); |
| 131 | } |
| 132 | template <> |
| 133 | ast_type_traits::DynTypedNode createDynTypedNode(const TypeLoc &Node) { |
| 134 | return ast_type_traits::DynTypedNode::create(Node); |
| 135 | } |
| 136 | template <> |
| 137 | ast_type_traits::DynTypedNode |
| 138 | createDynTypedNode(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. |
| 151 | class ParentMapContext::ParentMap::ASTVisitor |
| 152 | : public RecursiveASTVisitor<ASTVisitor> { |
| 153 | public: |
| 154 | ASTVisitor(ParentMap &Map, ParentMapContext &MapCtx) |
| 155 | : Map(Map), MapCtx(MapCtx) {} |
| 156 | |
| 157 | private: |
| 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 ⤅ |
| 248 | ParentMapContext &MapCtx; |
| 249 | llvm::SmallVector<ast_type_traits::DynTypedNode, 16> ParentStack; |
| 250 | }; |
| 251 | |
| 252 | ParentMapContext::ParentMap::ParentMap(ASTContext &Ctx) { |
| 253 | ASTVisitor(*this, Ctx.getParentMapContext()).TraverseAST(Ctx); |
| 254 | } |
| 255 | |
| 256 | DynTypedNodeList |
| 257 | ParentMapContext::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 | |