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