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
.count(Node
))
81 ConnectedNodes
.insert(Node
);
82 for (auto It
= Node
->begin(), End
= Node
->end(); It
!= End
; ++It
)
87 return ConnectedNodes
;
90 const Decl
*HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl
*D
) {
91 const auto *DC
= D
->getDeclContext();
92 const auto *Result
= D
;
94 if (const auto *RD
= dyn_cast
<CXXRecordDecl
>(DC
))
96 else if (const auto *FD
= dyn_cast
<FunctionDecl
>(DC
))
103 void HelperDeclRGBuilder::run(
104 const ast_matchers::MatchFinder::MatchResult
&Result
) {
105 // Construct the graph by adding a directed edge from caller to callee.
107 // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
108 // might be not the targetted Caller Decl, we always use the outmost enclosing
109 // FunctionDecl/CXXRecordDecl of "dc". For example,
111 // int MoveClass::F() { int a = helper(); return a; }
113 // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
114 // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
115 if (const auto *FuncRef
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("func_ref")) {
116 const auto *DC
= Result
.Nodes
.getNodeAs
<Decl
>("dc");
118 LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
119 << FuncRef
->getDecl()->getDeclName() << " ("
120 << FuncRef
->getDecl() << ")\n");
122 getOutmostClassOrFunDecl(DC
->getCanonicalDecl()),
123 getOutmostClassOrFunDecl(FuncRef
->getDecl()->getCanonicalDecl()));
124 } else if (const auto *UsedClass
=
125 Result
.Nodes
.getNodeAs
<CXXRecordDecl
>("used_class")) {
126 const auto *DC
= Result
.Nodes
.getNodeAs
<Decl
>("dc");
128 LLVM_DEBUG(llvm::dbgs()
129 << "Find helper class usage: " << UsedClass
->getDeclName()
130 << " (" << UsedClass
<< ")\n");
131 RG
->addEdge(getOutmostClassOrFunDecl(DC
->getCanonicalDecl()), UsedClass
);