|  | //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "HelperDeclRefGraph.h" | 
|  | #include "Move.h" | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "llvm/Support/Debug.h" | 
|  | #include <vector> | 
|  |  | 
|  | #define DEBUG_TYPE "clang-move" | 
|  |  | 
|  | namespace clang { | 
|  | namespace move { | 
|  |  | 
|  | void HelperDeclRefGraph::print(raw_ostream &OS) const { | 
|  | OS << " --- Call graph Dump --- \n"; | 
|  | for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) { | 
|  | const CallGraphNode *N = (I->second).get(); | 
|  |  | 
|  | OS << "  Declarations: "; | 
|  | N->print(OS); | 
|  | OS << " (" << N << ") "; | 
|  | OS << " calls: "; | 
|  | for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) { | 
|  | CI->Callee->print(OS); | 
|  | OS << " (" << CI << ") "; | 
|  | } | 
|  | OS << '\n'; | 
|  | } | 
|  | OS.flush(); | 
|  | } | 
|  |  | 
|  | void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) { | 
|  | assert(Caller); | 
|  | assert(Callee); | 
|  |  | 
|  | // Ignore the case where Caller equals Callee. This happens in the static | 
|  | // class member definitions in global namespace like "int CLASS::static_var = | 
|  | // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS" | 
|  | // CXXRecordDecl. | 
|  | if (Caller == Callee) return; | 
|  |  | 
|  | // Allocate a new node, mark it as root, and process it's calls. | 
|  | CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller)); | 
|  | CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee)); | 
|  | CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr}); | 
|  | } | 
|  |  | 
|  | void HelperDeclRefGraph::dump() const { print(llvm::errs()); } | 
|  |  | 
|  | CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) { | 
|  | F = F->getCanonicalDecl(); | 
|  | std::unique_ptr<CallGraphNode> &Node = DeclMap[F]; | 
|  | if (Node) | 
|  | return Node.get(); | 
|  |  | 
|  | Node = std::make_unique<CallGraphNode>(F); | 
|  | return Node.get(); | 
|  | } | 
|  |  | 
|  | CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const { | 
|  | auto I = DeclMap.find(D->getCanonicalDecl()); | 
|  | return I == DeclMap.end() ? nullptr : I->second.get(); | 
|  | } | 
|  |  | 
|  | llvm::DenseSet<const CallGraphNode *> | 
|  | HelperDeclRefGraph::getReachableNodes(const Decl *Root) const { | 
|  | const auto *RootNode = getNode(Root); | 
|  | if (!RootNode) | 
|  | return {}; | 
|  | llvm::DenseSet<const CallGraphNode *> ConnectedNodes; | 
|  | std::function<void(const CallGraphNode *)> VisitNode = | 
|  | [&](const CallGraphNode *Node) { | 
|  | if (ConnectedNodes.count(Node)) | 
|  | return; | 
|  | ConnectedNodes.insert(Node); | 
|  | for (auto It = Node->begin(), End = Node->end(); It != End; ++It) | 
|  | VisitNode(*It); | 
|  | }; | 
|  |  | 
|  | VisitNode(RootNode); | 
|  | return ConnectedNodes; | 
|  | } | 
|  |  | 
|  | const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) { | 
|  | const auto *DC = D->getDeclContext(); | 
|  | const auto *Result = D; | 
|  | while (DC) { | 
|  | if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) | 
|  | Result = RD; | 
|  | else if (const auto *FD = dyn_cast<FunctionDecl>(DC)) | 
|  | Result = FD; | 
|  | DC = DC->getParent(); | 
|  | } | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | void HelperDeclRGBuilder::run( | 
|  | const ast_matchers::MatchFinder::MatchResult &Result) { | 
|  | // Construct the graph by adding a directed edge from caller to callee. | 
|  | // | 
|  | // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it | 
|  | // might be not the targetted Caller Decl, we always use the outmost enclosing | 
|  | // FunctionDecl/CXXRecordDecl of "dc". For example, | 
|  | // | 
|  | //   int MoveClass::F() { int a = helper(); return a; } | 
|  | // | 
|  | // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST | 
|  | // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller. | 
|  | if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { | 
|  | const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); | 
|  | assert(DC); | 
|  | LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: " | 
|  | << FuncRef->getDecl()->getDeclName() << " (" | 
|  | << FuncRef->getDecl() << ")\n"); | 
|  | RG->addEdge( | 
|  | getOutmostClassOrFunDecl(DC->getCanonicalDecl()), | 
|  | getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl())); | 
|  | } else if (const auto *UsedClass = | 
|  | Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) { | 
|  | const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); | 
|  | assert(DC); | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "Find helper class usage: " << UsedClass->getDeclName() | 
|  | << " (" << UsedClass << ")\n"); | 
|  | RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace move | 
|  | } // namespace clang |