johannes created this revision.
Herald added subscribers: mgorny, klimek.

This adds a functions to remove AST nodes.
It works for Decl, and for Stmt nodes that are children of a
CompoundStmt.

Sometimes it is not possible to remove a Decl from its context
despite DeclContext.containsDecl() returning true. Instead the program
crashes. An example for this is in the tests. So this might be a
bug although it may never occur under normal circumstances.


https://reviews.llvm.org/D37005

Files:
  include/clang/Tooling/ASTDiff/ASTPatch.h
  unittests/Tooling/ASTPatchTest.cpp
  unittests/Tooling/CMakeLists.txt

Index: unittests/Tooling/CMakeLists.txt
===================================================================
--- unittests/Tooling/CMakeLists.txt
+++ unittests/Tooling/CMakeLists.txt
@@ -11,6 +11,7 @@
 endif()
 
 add_clang_unittest(ToolingTests
+  ASTPatchTest.cpp
   CastExprTest.cpp
   CommentHandlerTest.cpp
   CompilationDatabaseTest.cpp
Index: unittests/Tooling/ASTPatchTest.cpp
===================================================================
--- /dev/null
+++ unittests/Tooling/ASTPatchTest.cpp
@@ -0,0 +1,64 @@
+//===- unittests/Tooling/ASTPatchTest.cpp ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/ASTDiff/ASTPatch.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace ast_matchers;
+
+namespace {
+template <class T> struct DeleteMatch : public MatchFinder::MatchCallback {
+  unsigned NumFound = 0;
+  bool Success = true;
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    auto *Node = Result.Nodes.getNodeAs<T>("id");
+    if (!Node)
+      return;
+    ++NumFound;
+    Success = Success && patch::remove<T>(Node, *Result.Context);
+  }
+};
+} // end anonymous namespace
+
+template <class T, class NodeMatcher>
+static testing::AssertionResult
+isRemovalSuccessful(const NodeMatcher &StmtMatch, StringRef Code) {
+  DeleteMatch<T> Deleter;
+  MatchFinder Finder;
+  Finder.addMatcher(StmtMatch, &Deleter);
+  std::unique_ptr<FrontendActionFactory> Factory(
+      newFrontendActionFactory(&Finder));
+  if (!runToolOnCode(Factory->create(), Code))
+    return testing::AssertionFailure()
+           << R"(Parsing error in ")" << Code.str() << R"(")";
+  if (Deleter.NumFound == 0)
+    return testing::AssertionFailure() << "Matcher didn't find any statements";
+  return testing::AssertionResult(Deleter.Success);
+}
+
+TEST(ASTPatch, RemoveStmt) {
+  ASSERT_TRUE(isRemovalSuccessful<Stmt>(returnStmt().bind("id"),
+                                        R"(void x(){ return;})"));
+}
+
+TEST(ASTPatch, RemoveDecl) {
+  ASSERT_TRUE(isRemovalSuccessful<Decl>(varDecl().bind("id"),
+                                        R"(int x = 0;)"));
+  ASSERT_TRUE(isRemovalSuccessful<Decl>(functionTemplateDecl().bind("id"), R"(
+template <class T> struct pred {};
+template <class T> pred<pred<T> > swap();
+template <class T> pred<pred<T> > swap();
+void swap();
+)"));
+}
Index: include/clang/Tooling/ASTDiff/ASTPatch.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/ASTDiff/ASTPatch.h
@@ -0,0 +1,70 @@
+//===- ASTPatch.h - AST patching ------------------------------*- C++ -*- -===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_ASTDIFF_ASTPATCH_H
+#define LLVM_CLANG_TOOLING_ASTDIFF_ASTPATCH_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+
+using namespace clang;
+using namespace ast_type_traits;
+
+namespace clang {
+namespace patch {
+
+bool remove(Decl *D, ASTContext &Context) {
+  auto *Ctx = D->getLexicalDeclContext();
+  if (!Ctx->containsDecl(D))
+    return false;
+  Ctx->removeDecl(D);
+  return true;
+}
+
+static bool removeFrom(Stmt *S, ASTContext &Context, DynTypedNode Parent) {
+  auto *ConstParentS = Parent.template get<Stmt>();
+  if (!ConstParentS)
+    return false;
+  auto *ParentS = const_cast<Stmt *>(ConstParentS);
+  if (auto *CS = dyn_cast<CompoundStmt>(ParentS)) {
+    std::vector<Stmt *> Stmts(CS->child_begin(), CS->child_end());
+    auto End = Stmts.end();
+    Stmts.erase(std::remove(Stmts.begin(), Stmts.end(), S), Stmts.end());
+    CS->setStmts(Context, Stmts);
+    return Stmts.end() != End;
+  }
+  return false;
+}
+
+bool remove(Stmt *Node, ASTContext &Context) {
+  if (!Node)
+    return false;
+  auto DTN = DynTypedNode::create(*Node);
+  const auto &Parents = Context.getParents(DTN);
+  if (Parents.empty())
+    return false;
+  auto &Parent = Parents[0];
+  return removeFrom(Node, Context, Parent);
+}
+
+template <class T> bool remove(const T *N, ASTContext &Context) {
+  return remove(const_cast<T *>(N), Context);
+}
+
+bool remove(DynTypedNode DTN, ASTContext &Context) {
+  if (auto *S = DTN.get<Stmt>())
+    return remove(S, Context);
+  if (auto *D = DTN.get<Decl>())
+    return remove(D, Context);
+  return false;
+}
+}
+}
+#endif
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to