xref: /llvm-project/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp (revision 9be7f7748484e4fb6ca653ac92ba0548ef844ce3)
1ffe9f00cSFangrui Song //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===//
23626516bSHaojian Wu //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63626516bSHaojian Wu //
73626516bSHaojian Wu //===----------------------------------------------------------------------===//
83626516bSHaojian Wu 
93626516bSHaojian Wu #include "HelperDeclRefGraph.h"
1071ebc9ebSNico Weber #include "Move.h"
113626516bSHaojian Wu #include "clang/AST/Decl.h"
124775ce56SHaojian Wu #include "llvm/Support/Debug.h"
133626516bSHaojian Wu #include <vector>
143626516bSHaojian Wu 
154775ce56SHaojian Wu #define DEBUG_TYPE "clang-move"
164775ce56SHaojian Wu 
173626516bSHaojian Wu namespace clang {
183626516bSHaojian Wu namespace move {
193626516bSHaojian Wu 
203626516bSHaojian Wu void HelperDeclRefGraph::print(raw_ostream &OS) const {
213626516bSHaojian Wu   OS << " --- Call graph Dump --- \n";
223626516bSHaojian Wu   for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
233626516bSHaojian Wu     const CallGraphNode *N = (I->second).get();
243626516bSHaojian Wu 
253626516bSHaojian Wu     OS << "  Declarations: ";
263626516bSHaojian Wu     N->print(OS);
273626516bSHaojian Wu     OS << " (" << N << ") ";
283626516bSHaojian Wu     OS << " calls: ";
293626516bSHaojian Wu     for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
30d68c7b8eSRoman Lebedev       CI->Callee->print(OS);
313626516bSHaojian Wu       OS << " (" << CI << ") ";
323626516bSHaojian Wu     }
333626516bSHaojian Wu     OS << '\n';
343626516bSHaojian Wu   }
353626516bSHaojian Wu   OS.flush();
363626516bSHaojian Wu }
373626516bSHaojian Wu 
383626516bSHaojian Wu void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
393626516bSHaojian Wu   assert(Caller);
403626516bSHaojian Wu   assert(Callee);
413626516bSHaojian Wu 
423626516bSHaojian Wu   // Ignore the case where Caller equals Callee. This happens in the static
433626516bSHaojian Wu   // class member definitions in global namespace like "int CLASS::static_var =
443626516bSHaojian Wu   // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
453626516bSHaojian Wu   // CXXRecordDecl.
463626516bSHaojian Wu   if (Caller == Callee) return;
473626516bSHaojian Wu 
483626516bSHaojian Wu   // Allocate a new node, mark it as root, and process it's calls.
493626516bSHaojian Wu   CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
503626516bSHaojian Wu   CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
51d68c7b8eSRoman Lebedev   CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr});
523626516bSHaojian Wu }
533626516bSHaojian Wu 
543626516bSHaojian Wu void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
553626516bSHaojian Wu 
563626516bSHaojian Wu CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
573626516bSHaojian Wu   F = F->getCanonicalDecl();
583626516bSHaojian Wu   std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
593626516bSHaojian Wu   if (Node)
603626516bSHaojian Wu     return Node.get();
613626516bSHaojian Wu 
621c705d9cSJonas Devlieghere   Node = std::make_unique<CallGraphNode>(F);
633626516bSHaojian Wu   return Node.get();
643626516bSHaojian Wu }
653626516bSHaojian Wu 
663626516bSHaojian Wu CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
673626516bSHaojian Wu   auto I = DeclMap.find(D->getCanonicalDecl());
683626516bSHaojian Wu   return I == DeclMap.end() ? nullptr : I->second.get();
693626516bSHaojian Wu }
703626516bSHaojian Wu 
713626516bSHaojian Wu llvm::DenseSet<const CallGraphNode *>
723626516bSHaojian Wu HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
733626516bSHaojian Wu   const auto *RootNode = getNode(Root);
743626516bSHaojian Wu   if (!RootNode)
753626516bSHaojian Wu     return {};
763626516bSHaojian Wu   llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
773626516bSHaojian Wu   std::function<void(const CallGraphNode *)> VisitNode =
783626516bSHaojian Wu       [&](const CallGraphNode *Node) {
7978768c52SKazu Hirata         if (!ConnectedNodes.insert(Node).second)
803626516bSHaojian Wu           return;
81*9be7f774SKazu Hirata         for (const CallGraphNode::CallRecord &Callee : *Node)
82*9be7f774SKazu Hirata           VisitNode(Callee);
833626516bSHaojian Wu       };
843626516bSHaojian Wu 
853626516bSHaojian Wu   VisitNode(RootNode);
863626516bSHaojian Wu   return ConnectedNodes;
873626516bSHaojian Wu }
883626516bSHaojian Wu 
893626516bSHaojian Wu const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
903626516bSHaojian Wu   const auto *DC = D->getDeclContext();
913626516bSHaojian Wu   const auto *Result = D;
923626516bSHaojian Wu   while (DC) {
933626516bSHaojian Wu     if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
943626516bSHaojian Wu       Result = RD;
953626516bSHaojian Wu     else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
963626516bSHaojian Wu       Result = FD;
973626516bSHaojian Wu     DC = DC->getParent();
983626516bSHaojian Wu   }
993626516bSHaojian Wu   return Result;
1003626516bSHaojian Wu }
1013626516bSHaojian Wu 
1023626516bSHaojian Wu void HelperDeclRGBuilder::run(
1033626516bSHaojian Wu     const ast_matchers::MatchFinder::MatchResult &Result) {
1043626516bSHaojian Wu   // Construct the graph by adding a directed edge from caller to callee.
1053626516bSHaojian Wu   //
1063626516bSHaojian Wu   // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
1073626516bSHaojian Wu   // might be not the targetted Caller Decl, we always use the outmost enclosing
1083626516bSHaojian Wu   // FunctionDecl/CXXRecordDecl of "dc". For example,
1093626516bSHaojian Wu   //
1103626516bSHaojian Wu   //   int MoveClass::F() { int a = helper(); return a; }
1113626516bSHaojian Wu   //
1123626516bSHaojian Wu   // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
1133626516bSHaojian Wu   // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
1143626516bSHaojian Wu   if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
1153626516bSHaojian Wu     const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
1163626516bSHaojian Wu     assert(DC);
1170efd5248SNicola Zaghen     LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
11819701458SBruno Ricci                             << FuncRef->getDecl()->getDeclName() << " ("
1194775ce56SHaojian Wu                             << FuncRef->getDecl() << ")\n");
1204775ce56SHaojian Wu     RG->addEdge(
1214775ce56SHaojian Wu         getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
1224775ce56SHaojian Wu         getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
1233626516bSHaojian Wu   } else if (const auto *UsedClass =
1243626516bSHaojian Wu                  Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
1253626516bSHaojian Wu     const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
1263626516bSHaojian Wu     assert(DC);
1270efd5248SNicola Zaghen     LLVM_DEBUG(llvm::dbgs()
12819701458SBruno Ricci                << "Find helper class usage: " << UsedClass->getDeclName()
1290efd5248SNicola Zaghen                << " (" << UsedClass << ")\n");
1303626516bSHaojian Wu     RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
1313626516bSHaojian Wu   }
1323626516bSHaojian Wu }
1333626516bSHaojian Wu 
1343626516bSHaojian Wu } // namespace move
1353626516bSHaojian Wu } // namespace clang
136