| //===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "HelperDeclRefGraph.h" | 
 | #include "ClangMove.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)->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); | 
 | } | 
 |  | 
 | 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 = llvm::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()->getNameAsString() << " (" | 
 |                             << 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->getNameAsString() | 
 |                << " (" << UsedClass << ")\n"); | 
 |     RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); | 
 |   } | 
 | } | 
 |  | 
 | } // namespace move | 
 | } // namespace clang |