1 //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===// 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 #include "HelperDeclRefGraph.h" 10 #include "Move.h" 11 #include "clang/AST/Decl.h" 12 #include "llvm/Support/Debug.h" 13 #include <vector> 14 15 #define DEBUG_TYPE "clang-move" 16 17 namespace clang { 18 namespace move { 19 20 void HelperDeclRefGraph::print(raw_ostream &OS) const { 21 OS << " --- Call graph Dump --- \n"; 22 for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) { 23 const CallGraphNode *N = (I->second).get(); 24 25 OS << " Declarations: "; 26 N->print(OS); 27 OS << " (" << N << ") "; 28 OS << " calls: "; 29 for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) { 30 (*CI)->print(OS); 31 OS << " (" << CI << ") "; 32 } 33 OS << '\n'; 34 } 35 OS.flush(); 36 } 37 38 void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) { 39 assert(Caller); 40 assert(Callee); 41 42 // Ignore the case where Caller equals Callee. This happens in the static 43 // class member definitions in global namespace like "int CLASS::static_var = 44 // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS" 45 // CXXRecordDecl. 46 if (Caller == Callee) return; 47 48 // Allocate a new node, mark it as root, and process it's calls. 49 CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller)); 50 CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee)); 51 CallerNode->addCallee(CalleeNode); 52 } 53 54 void HelperDeclRefGraph::dump() const { print(llvm::errs()); } 55 56 CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) { 57 F = F->getCanonicalDecl(); 58 std::unique_ptr<CallGraphNode> &Node = DeclMap[F]; 59 if (Node) 60 return Node.get(); 61 62 Node = std::make_unique<CallGraphNode>(F); 63 return Node.get(); 64 } 65 66 CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const { 67 auto I = DeclMap.find(D->getCanonicalDecl()); 68 return I == DeclMap.end() ? nullptr : I->second.get(); 69 } 70 71 llvm::DenseSet<const CallGraphNode *> 72 HelperDeclRefGraph::getReachableNodes(const Decl *Root) const { 73 const auto *RootNode = getNode(Root); 74 if (!RootNode) 75 return {}; 76 llvm::DenseSet<const CallGraphNode *> ConnectedNodes; 77 std::function<void(const CallGraphNode *)> VisitNode = 78 [&](const CallGraphNode *Node) { 79 if (ConnectedNodes.count(Node)) 80 return; 81 ConnectedNodes.insert(Node); 82 for (auto It = Node->begin(), End = Node->end(); It != End; ++It) 83 VisitNode(*It); 84 }; 85 86 VisitNode(RootNode); 87 return ConnectedNodes; 88 } 89 90 const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) { 91 const auto *DC = D->getDeclContext(); 92 const auto *Result = D; 93 while (DC) { 94 if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) 95 Result = RD; 96 else if (const auto *FD = dyn_cast<FunctionDecl>(DC)) 97 Result = FD; 98 DC = DC->getParent(); 99 } 100 return Result; 101 } 102 103 void HelperDeclRGBuilder::run( 104 const ast_matchers::MatchFinder::MatchResult &Result) { 105 // Construct the graph by adding a directed edge from caller to callee. 106 // 107 // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it 108 // might be not the targetted Caller Decl, we always use the outmost enclosing 109 // FunctionDecl/CXXRecordDecl of "dc". For example, 110 // 111 // int MoveClass::F() { int a = helper(); return a; } 112 // 113 // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST 114 // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller. 115 if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { 116 const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); 117 assert(DC); 118 LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: " 119 << FuncRef->getDecl()->getNameAsString() << " (" 120 << FuncRef->getDecl() << ")\n"); 121 RG->addEdge( 122 getOutmostClassOrFunDecl(DC->getCanonicalDecl()), 123 getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl())); 124 } else if (const auto *UsedClass = 125 Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) { 126 const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); 127 assert(DC); 128 LLVM_DEBUG(llvm::dbgs() 129 << "Find helper class usage: " << UsedClass->getNameAsString() 130 << " (" << UsedClass << ")\n"); 131 RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); 132 } 133 } 134 135 } // namespace move 136 } // namespace clang 137