1 //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "HelperDeclRefGraph.h"
11 #include "clang/AST/Decl.h"
12 #include "llvm/Support/Debug.h"
15 #define DEBUG_TYPE "clang-move"
20 void HelperDeclRefGraph::print(raw_ostream
&OS
) const {
21 OS
<< " --- Call graph Dump --- \n";
22 for (auto I
= DeclMap
.begin(); I
!= DeclMap
.end(); ++I
) {
23 const CallGraphNode
*N
= (I
->second
).get();
25 OS
<< " Declarations: ";
27 OS
<< " (" << N
<< ") ";
29 for (auto CI
= N
->begin(), CE
= N
->end(); CI
!= CE
; ++CI
) {
30 CI
->Callee
->print(OS
);
31 OS
<< " (" << CI
<< ") ";
38 void HelperDeclRefGraph::addEdge(const Decl
*Caller
, const Decl
*Callee
) {
42 // Ignore the case where Caller equals Callee. This happens in the static
43 // class member definitions in global namespace like "int CLASS::static_var =
44 // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
46 if (Caller
== Callee
) return;
48 // Allocate a new node, mark it as root, and process it's calls.
49 CallGraphNode
*CallerNode
= getOrInsertNode(const_cast<Decl
*>(Caller
));
50 CallGraphNode
*CalleeNode
= getOrInsertNode(const_cast<Decl
*>(Callee
));
51 CallerNode
->addCallee({CalleeNode
, /*CallExpr=*/nullptr});
54 void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
56 CallGraphNode
*HelperDeclRefGraph::getOrInsertNode(Decl
*F
) {
57 F
= F
->getCanonicalDecl();
58 std::unique_ptr
<CallGraphNode
> &Node
= DeclMap
[F
];
62 Node
= std::make_unique
<CallGraphNode
>(F
);
66 CallGraphNode
*HelperDeclRefGraph::getNode(const Decl
*D
) const {
67 auto I
= DeclMap
.find(D
->getCanonicalDecl());
68 return I
== DeclMap
.end() ? nullptr : I
->second
.get();
71 llvm::DenseSet
<const CallGraphNode
*>
72 HelperDeclRefGraph::getReachableNodes(const Decl
*Root
) const {
73 const auto *RootNode
= getNode(Root
);
76 llvm::DenseSet
<const CallGraphNode
*> ConnectedNodes
;
77 std::function
<void(const CallGraphNode
*)> VisitNode
=
78 [&](const CallGraphNode
*Node
) {
79 if (!ConnectedNodes
.insert(Node
).second
)
81 for (const CallGraphNode::CallRecord
&Callee
: *Node
)
86 return ConnectedNodes
;
89 const Decl
*HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl
*D
) {
90 const auto *DC
= D
->getDeclContext();
91 const auto *Result
= D
;
93 if (const auto *RD
= dyn_cast
<CXXRecordDecl
>(DC
))
95 else if (const auto *FD
= dyn_cast
<FunctionDecl
>(DC
))
102 void HelperDeclRGBuilder::run(
103 const ast_matchers::MatchFinder::MatchResult
&Result
) {
104 // Construct the graph by adding a directed edge from caller to callee.
106 // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
107 // might be not the targetted Caller Decl, we always use the outmost enclosing
108 // FunctionDecl/CXXRecordDecl of "dc". For example,
110 // int MoveClass::F() { int a = helper(); return a; }
112 // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
113 // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
114 if (const auto *FuncRef
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("func_ref")) {
115 const auto *DC
= Result
.Nodes
.getNodeAs
<Decl
>("dc");
117 LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
118 << FuncRef
->getDecl()->getDeclName() << " ("
119 << FuncRef
->getDecl() << ")\n");
121 getOutmostClassOrFunDecl(DC
->getCanonicalDecl()),
122 getOutmostClassOrFunDecl(FuncRef
->getDecl()->getCanonicalDecl()));
123 } else if (const auto *UsedClass
=
124 Result
.Nodes
.getNodeAs
<CXXRecordDecl
>("used_class")) {
125 const auto *DC
= Result
.Nodes
.getNodeAs
<Decl
>("dc");
127 LLVM_DEBUG(llvm::dbgs()
128 << "Find helper class usage: " << UsedClass
->getDeclName()
129 << " (" << UsedClass
<< ")\n");
130 RG
->addEdge(getOutmostClassOrFunDecl(DC
->getCanonicalDecl()), UsedClass
);