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