summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-move/HelperDeclRefGraph.cpp')
-rw-r--r--clang-tools-extra/clang-move/HelperDeclRefGraph.cpp128
1 files changed, 128 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp b/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp
new file mode 100644
index 00000000000..8303015171e
--- /dev/null
+++ b/clang-tools-extra/clang-move/HelperDeclRefGraph.cpp
@@ -0,0 +1,128 @@
+//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HelperDeclRefGraph.h"
+#include "ClangMove.h"
+#include "clang/AST/Decl.h"
+#include <vector>
+
+namespace clang {
+namespace move {
+
+void HelperDeclRefGraph::print(raw_ostream &OS) const {
+ OS << " --- Call graph Dump --- \n";
+ for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
+ const CallGraphNode *N = (I->second).get();
+
+ OS << " Declarations: ";
+ N->print(OS);
+ OS << " (" << N << ") ";
+ OS << " calls: ";
+ for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
+ (*CI)->print(OS);
+ OS << " (" << CI << ") ";
+ }
+ OS << '\n';
+ }
+ OS.flush();
+}
+
+void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
+ assert(Caller);
+ assert(Callee);
+
+ // Ignore the case where Caller equals Callee. This happens in the static
+ // class member definitions in global namespace like "int CLASS::static_var =
+ // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
+ // CXXRecordDecl.
+ if (Caller == Callee) return;
+
+ // Allocate a new node, mark it as root, and process it's calls.
+ CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
+ CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
+ CallerNode->addCallee(CalleeNode);
+}
+
+void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
+
+CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
+ F = F->getCanonicalDecl();
+ std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
+ if (Node)
+ return Node.get();
+
+ Node = llvm::make_unique<CallGraphNode>(F);
+ return Node.get();
+}
+
+CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
+ auto I = DeclMap.find(D->getCanonicalDecl());
+ return I == DeclMap.end() ? nullptr : I->second.get();
+}
+
+llvm::DenseSet<const CallGraphNode *>
+HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
+ const auto *RootNode = getNode(Root);
+ if (!RootNode)
+ return {};
+ llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
+ std::function<void(const CallGraphNode *)> VisitNode =
+ [&](const CallGraphNode *Node) {
+ if (ConnectedNodes.count(Node))
+ return;
+ ConnectedNodes.insert(Node);
+ for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
+ VisitNode(*It);
+ };
+
+ VisitNode(RootNode);
+ return ConnectedNodes;
+}
+
+const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
+ const auto *DC = D->getDeclContext();
+ const auto *Result = D;
+ while (DC) {
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
+ Result = RD;
+ else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
+ Result = FD;
+ DC = DC->getParent();
+ }
+ return Result;
+}
+
+void HelperDeclRGBuilder::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ // Construct the graph by adding a directed edge from caller to callee.
+ //
+ // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
+ // might be not the targetted Caller Decl, we always use the outmost enclosing
+ // FunctionDecl/CXXRecordDecl of "dc". For example,
+ //
+ // int MoveClass::F() { int a = helper(); return a; }
+ //
+ // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
+ // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
+ if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
+ const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(DC);
+
+ RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
+ getOutmostClassOrFunDecl(FuncRef->getDecl()));
+ } else if (const auto *UsedClass =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
+ const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(DC);
+ RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
+ }
+}
+
+} // namespace move
+} // namespace clang
OpenPOWER on IntegriCloud