xref: /llvm-project/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp (revision 4775ce56ec52f9cfbc6ec1ec263d85054d69d192)
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