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