//===- ParentMapContext.cpp - Map of parents using DynTypedNode -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Similar to ParentMap.cpp, but generalizes to non-Stmt nodes, which can have // multiple parents. // //===----------------------------------------------------------------------===// #include "clang/AST/ParentMapContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/TemplateBase.h" using namespace clang; ParentMapContext::ParentMapContext(ASTContext &Ctx) : ASTCtx(Ctx) {} ParentMapContext::~ParentMapContext() = default; void ParentMapContext::clear() { Parents.reset(); } const Expr *ParentMapContext::traverseIgnored(const Expr *E) const { return traverseIgnored(const_cast(E)); } Expr *ParentMapContext::traverseIgnored(Expr *E) const { if (!E) return nullptr; switch (Traversal) { case TK_AsIs: return E; case TK_IgnoreUnlessSpelledInSource: return E->IgnoreUnlessSpelledInSource(); } llvm_unreachable("Invalid Traversal type!"); } DynTypedNode ParentMapContext::traverseIgnored(const DynTypedNode &N) const { if (const auto *E = N.get()) { return DynTypedNode::create(*traverseIgnored(E)); } return N; } class ParentMapContext::ParentMap { /// Contains parents of a node. using ParentVector = llvm::SmallVector; /// Maps from a node to its parents. This is used for nodes that have /// pointer identity only, which are more common and we can save space by /// only storing a unique pointer to them. using ParentMapPointers = llvm::DenseMap>; /// Parent map for nodes without pointer identity. We store a full /// DynTypedNode for all keys. using ParentMapOtherNodes = llvm::DenseMap>; ParentMapPointers PointerParents; ParentMapOtherNodes OtherParents; class ASTVisitor; static DynTypedNode getSingleDynTypedNodeFromParentMap(ParentMapPointers::mapped_type U) { if (const auto *D = U.dyn_cast()) return DynTypedNode::create(*D); if (const auto *S = U.dyn_cast()) return DynTypedNode::create(*S); return *U.get(); } template static DynTypedNodeList getDynNodeFromMap(const NodeTy &Node, const MapTy &Map) { auto I = Map.find(Node); if (I == Map.end()) { return llvm::ArrayRef(); } if (const auto *V = I->second.template dyn_cast()) { return llvm::makeArrayRef(*V); } return getSingleDynTypedNodeFromParentMap(I->second); } public: ParentMap(ASTContext &Ctx); ~ParentMap() { for (const auto &Entry : PointerParents) { if (Entry.second.is()) { delete Entry.second.get(); } else if (Entry.second.is()) { delete Entry.second.get(); } } for (const auto &Entry : OtherParents) { if (Entry.second.is()) { delete Entry.second.get(); } else if (Entry.second.is()) { delete Entry.second.get(); } } } DynTypedNodeList getParents(TraversalKind TK, const DynTypedNode &Node) { if (Node.getNodeKind().hasPointerIdentity()) { auto ParentList = getDynNodeFromMap(Node.getMemoizationData(), PointerParents); if (ParentList.size() == 1 && TK == TK_IgnoreUnlessSpelledInSource) { const auto *E = ParentList[0].get(); const auto *Child = Node.get(); if (E && Child) return AscendIgnoreUnlessSpelledInSource(E, Child); } return ParentList; } return getDynNodeFromMap(Node, OtherParents); } DynTypedNodeList AscendIgnoreUnlessSpelledInSource(const Expr *E, const Expr *Child) { auto ShouldSkip = [](const Expr *E, const Expr *Child) { if (isa(E)) return true; if (isa(E)) return true; if (isa(E)) return true; if (isa(E)) return true; if (isa(E)) return true; if (isa(E)) return true; auto SR = Child->getSourceRange(); if (const auto *C = dyn_cast(E)) { if (C->getSourceRange() == SR) return true; } if (const auto *C = dyn_cast(E)) { if (C->getSourceRange() == SR || C->isElidable()) return true; } if (const auto *C = dyn_cast(E)) { if (C->getSourceRange() == SR) return true; } if (const auto *C = dyn_cast(E)) { if (C->getSourceRange() == SR) return true; } return false; }; while (ShouldSkip(E, Child)) { auto It = PointerParents.find(E); if (It == PointerParents.end()) break; const auto *S = It->second.dyn_cast(); if (!S) { if (auto *Vec = It->second.dyn_cast()) return llvm::makeArrayRef(*Vec); return getSingleDynTypedNodeFromParentMap(It->second); } const auto *P = dyn_cast(S); if (!P) return DynTypedNode::create(*S); Child = E; E = P; } return DynTypedNode::create(*E); } }; /// Template specializations to abstract away from pointers and TypeLocs. /// @{ template static DynTypedNode createDynTypedNode(const T &Node) { return DynTypedNode::create(*Node); } template <> DynTypedNode createDynTypedNode(const TypeLoc &Node) { return DynTypedNode::create(Node); } template <> DynTypedNode createDynTypedNode(const NestedNameSpecifierLoc &Node) { return DynTypedNode::create(Node); } /// @} /// A \c RecursiveASTVisitor that builds a map from nodes to their /// parents as defined by the \c RecursiveASTVisitor. /// /// Note that the relationship described here is purely in terms of AST /// traversal - there are other relationships (for example declaration context) /// in the AST that are better modeled by special matchers. class ParentMapContext::ParentMap::ASTVisitor : public RecursiveASTVisitor { public: ASTVisitor(ParentMap &Map) : Map(Map) {} private: friend class RecursiveASTVisitor; using VisitorBase = RecursiveASTVisitor; bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } /// Record the parent of the node we're visiting. /// MapNode is the child, the parent is on top of ParentStack. /// Parents is the parent storage (either PointerParents or OtherParents). template void addParent(MapNodeTy MapNode, MapTy *Parents) { if (ParentStack.empty()) return; // FIXME: Currently we add the same parent multiple times, but only // when no memoization data is available for the type. // For example when we visit all subexpressions of template // instantiations; this is suboptimal, but benign: the only way to // visit those is with hasAncestor / hasParent, and those do not create // new matches. // The plan is to enable DynTypedNode to be storable in a map or hash // map. The main problem there is to implement hash functions / // comparison operators for all types that DynTypedNode supports that // do not have pointer identity. auto &NodeOrVector = (*Parents)[MapNode]; if (NodeOrVector.isNull()) { if (const auto *D = ParentStack.back().get()) NodeOrVector = D; else if (const auto *S = ParentStack.back().get()) NodeOrVector = S; else NodeOrVector = new DynTypedNode(ParentStack.back()); } else { if (!NodeOrVector.template is()) { auto *Vector = new ParentVector( 1, getSingleDynTypedNodeFromParentMap(NodeOrVector)); delete NodeOrVector.template dyn_cast(); NodeOrVector = Vector; } auto *Vector = NodeOrVector.template get(); // Skip duplicates for types that have memoization data. // We must check that the type has memoization data before calling // std::find() because DynTypedNode::operator== can't compare all // types. bool Found = ParentStack.back().getMemoizationData() && std::find(Vector->begin(), Vector->end(), ParentStack.back()) != Vector->end(); if (!Found) Vector->push_back(ParentStack.back()); } } template bool TraverseNode(T Node, MapNodeTy MapNode, BaseTraverseFn BaseTraverse, MapTy *Parents) { if (!Node) return true; addParent(MapNode, Parents); ParentStack.push_back(createDynTypedNode(Node)); bool Result = BaseTraverse(); ParentStack.pop_back(); return Result; } bool TraverseDecl(Decl *DeclNode) { return TraverseNode( DeclNode, DeclNode, [&] { return VisitorBase::TraverseDecl(DeclNode); }, &Map.PointerParents); } bool TraverseTypeLoc(TypeLoc TypeLocNode) { return TraverseNode( TypeLocNode, DynTypedNode::create(TypeLocNode), [&] { return VisitorBase::TraverseTypeLoc(TypeLocNode); }, &Map.OtherParents); } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSLocNode) { return TraverseNode( NNSLocNode, DynTypedNode::create(NNSLocNode), [&] { return VisitorBase::TraverseNestedNameSpecifierLoc(NNSLocNode); }, &Map.OtherParents); } // Using generic TraverseNode for Stmt would prevent data-recursion. bool dataTraverseStmtPre(Stmt *StmtNode) { addParent(StmtNode, &Map.PointerParents); ParentStack.push_back(DynTypedNode::create(*StmtNode)); return true; } bool dataTraverseStmtPost(Stmt *StmtNode) { ParentStack.pop_back(); return true; } ParentMap ⤅ llvm::SmallVector ParentStack; }; ParentMapContext::ParentMap::ParentMap(ASTContext &Ctx) { ASTVisitor(*this).TraverseAST(Ctx); } DynTypedNodeList ParentMapContext::getParents(const DynTypedNode &Node) { if (!Parents) // We build the parent map for the traversal scope (usually whole TU), as // hasAncestor can escape any subtree. Parents = std::make_unique(ASTCtx); return Parents->getParents(getTraversalKind(), Node); }