clang-tools  15.0.0git
HelperDeclRefGraph.cpp
Go to the documentation of this file.
1 //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "HelperDeclRefGraph.h"
10 #include "Move.h"
11 #include "clang/AST/Decl.h"
12 #include "llvm/Support/Debug.h"
13 #include <vector>
14 
15 #define DEBUG_TYPE "clang-move"
16 
17 namespace clang {
18 namespace move {
19 
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();
24 
25  OS << " Declarations: ";
26  N->print(OS);
27  OS << " (" << N << ") ";
28  OS << " calls: ";
29  for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
30  CI->Callee->print(OS);
31  OS << " (" << CI << ") ";
32  }
33  OS << '\n';
34  }
35  OS.flush();
36 }
37 
38 void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
39  assert(Caller);
40  assert(Callee);
41 
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"
45  // CXXRecordDecl.
46  if (Caller == Callee) return;
47 
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});
52 }
53 
54 void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
55 
56 CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
57  F = F->getCanonicalDecl();
58  std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
59  if (Node)
60  return Node.get();
61 
62  Node = std::make_unique<CallGraphNode>(F);
63  return Node.get();
64 }
65 
66 CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
67  auto I = DeclMap.find(D->getCanonicalDecl());
68  return I == DeclMap.end() ? nullptr : I->second.get();
69 }
70 
71 llvm::DenseSet<const CallGraphNode *>
73  const auto *RootNode = getNode(Root);
74  if (!RootNode)
75  return {};
76  llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
77  std::function<void(const CallGraphNode *)> VisitNode =
78  [&](const CallGraphNode *Node) {
79  if (ConnectedNodes.count(Node))
80  return;
81  ConnectedNodes.insert(Node);
82  for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
83  VisitNode(*It);
84  };
85 
86  VisitNode(RootNode);
87  return ConnectedNodes;
88 }
89 
91  const auto *DC = D->getDeclContext();
92  const auto *Result = D;
93  while (DC) {
94  if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
95  Result = RD;
96  else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
97  Result = FD;
98  DC = DC->getParent();
99  }
100  return Result;
101 }
102 
104  const ast_matchers::MatchFinder::MatchResult &Result) {
105  // Construct the graph by adding a directed edge from caller to callee.
106  //
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,
110  //
111  // int MoveClass::F() { int a = helper(); return a; }
112  //
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");
117  assert(DC);
118  LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
119  << FuncRef->getDecl()->getDeclName() << " ("
120  << FuncRef->getDecl() << ")\n");
121  RG->addEdge(
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");
127  assert(DC);
128  LLVM_DEBUG(llvm::dbgs()
129  << "Find helper class usage: " << UsedClass->getDeclName()
130  << " (" << UsedClass << ")\n");
131  RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
132  }
133 }
134 
135 } // namespace move
136 } // namespace clang
clang::move::HelperDeclRefGraph::getReachableNodes
llvm::DenseSet< const CallGraphNode * > getReachableNodes(const Decl *D) const
Definition: HelperDeclRefGraph.cpp:72
Move.h
CI
std::unique_ptr< CompilerInvocation > CI
Definition: TUScheduler.cpp:491
Root
ASTNode Root
Definition: DumpAST.cpp:332
clang::move::HelperDeclRefGraph::addEdge
void addEdge(const Decl *Caller, const Decl *Callee)
Definition: HelperDeclRefGraph.cpp:38
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::move::HelperDeclRGBuilder::run
void run(const ast_matchers::MatchFinder::MatchResult &Result) override
Definition: HelperDeclRefGraph.cpp:103
clang::move::HelperDeclRGBuilder::getOutmostClassOrFunDecl
static const Decl * getOutmostClassOrFunDecl(const Decl *D)
Definition: HelperDeclRefGraph.cpp:90
clang::move::HelperDeclRefGraph::getNode
CallGraphNode * getNode(const Decl *D) const
Definition: HelperDeclRefGraph.cpp:66
CE
CaptureExpr CE
Definition: AvoidBindCheck.cpp:67
clang::move::HelperDeclRefGraph::dump
void dump() const
Definition: HelperDeclRefGraph.cpp:54
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:160
HelperDeclRefGraph.h