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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits