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