jbangert created this revision.
Herald added a subscriber: mgorny.

This requires https://reviews.llvm.org/D29613. It adds a new tool, 
clang-query-replace that replaces and AST node with the result of evaluating a 
simple template language.


https://reviews.llvm.org/D29622

Files:
  clang-query/CMakeLists.txt
  clang-query/QueryReplace.cpp
  clang-query/QueryReplace.h
  clang-query/replace-tool/CMakeLists.txt
  clang-query/replace-tool/ClangQueryReplace.cpp
  test/CMakeLists.txt
  test/clang-query/replacement.c

Index: test/clang-query/replacement.c
===================================================================
--- /dev/null
+++ test/clang-query/replacement.c
@@ -0,0 +1,6 @@
+// RUN: clang-query-replace --spec  '{Query: "varDecl(hasName(\"version\"),
+// hasInitializer(integerLiteral().bind(\"init\"))).bind(\"decl\")", FromId:
+// "decl", ToTemplate: "string version = \"$$-${init}\""}' %s -- && FileCheck %s
+// < %;
+int version = 4;
+// CHECK: string version = "$-4";
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -46,6 +46,7 @@
   clang-include-fixer
   clang-move
   clang-query
+  clang-query-replace
   clang-rename
   clang-reorder-fields
   clang-tidy
@@ -56,11 +57,9 @@
   # Unit tests
   ExtraToolsUnitTests
   )
-
 add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
   ${CMAKE_CURRENT_BINARY_DIR}
   DEPENDS ${CLANG_TOOLS_TEST_DEPS}
   ARGS ${CLANG_TOOLS_TEST_EXTRA_ARGS}
   )
 set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests")
-
Index: clang-query/replace-tool/ClangQueryReplace.cpp
===================================================================
--- /dev/null
+++ clang-query/replace-tool/ClangQueryReplace.cpp
@@ -0,0 +1,86 @@
+//===---- ClangQueryReplace.cpp - clang-query-replace tool ----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool automatically modifies source files based on AST matchers.
+//
+// A given AST matcher is applied to the source, and a bound node in
+// this matcher is replaced by the result of substituting the text
+// corresponding to other bound nodes into a string template.
+//
+// For example:
+// clang-query-replace --spec <<SPEC foo.c
+//        Query: 'varDecl(hasName("version"),
+//                       hasInitializer(integerLiteral().bind("init")))
+//                       .bind("decl")',
+//        FromId: 'decl',
+//        ToTemplate: 'string version = "$$-${init}";' }
+// SPEC
+//
+// If foo.c contains this:
+//
+// int version = 5;
+// void bar() { long version = 9; }
+//
+// it will be replaced with:
+// string version = "$-5";
+// void bar() { string version = "$-9"; }
+// ===---------------------------------------------------------------------===//
+
+#include "QueryReplace.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Signals.h"
+
+#include <fstream>
+#include <string>
+#include <system_error>
+
+using namespace clang;
+using namespace clang::query;
+using namespace llvm;
+
+using std::error_code;
+using clang::tooling::CommonOptionsParser;
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ClangQueryCategory("clang-query-replace options");
+
+static cl::list<std::string> Commands("spec",
+                                      cl::desc("YAML action specification"),
+                                      cl::value_desc("spec"),
+                                      cl::cat(ClangQueryCategory));
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
+  if (Commands.empty()) {
+    llvm::errs() << "Must specify at least one --spec \n";
+    return 1;
+  }
+
+  tooling::RefactoringTool Refactor(OptionsParser.getCompilations(),
+                                    OptionsParser.getSourcePathList());
+
+  QueryReplaceTool Tool(Refactor.getReplacements());
+  for (auto &SpecYaml : Commands) {
+    llvm::ErrorOr<QueryReplaceSpec> Spec =
+        QueryReplaceSpec::parseFromJSON(SpecYaml);
+    if (error_code EC = Spec.getError()) {
+      llvm::errs() << " Error " << EC.message()
+                   << " while parsing spec: " << SpecYaml << "\n";
+      return 1;
+    }
+    Tool.addOperation(*Spec);
+  }
+
+  return Refactor.runAndSave(tooling::newFrontendActionFactory(&Tool).get());
+}
Index: clang-query/replace-tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-query/replace-tool/CMakeLists.txt
@@ -0,0 +1,14 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-query-replace ClangQueryReplace.cpp)
+target_link_libraries(clang-query-replace
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  clangQuery
+  clangTooling
+  )
+
+install(TARGETS clang-query-replace RUNTIME DESTINATION bin)
\ No newline at end of file
Index: clang-query/QueryReplace.h
===================================================================
--- /dev/null
+++ clang-query/QueryReplace.h
@@ -0,0 +1,58 @@
+//===--- ReplaceSpec.h - clang-query-replace ------------------------*- 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_TOOLS_EXTRA_CLANG_QUERY_QUERYREPLACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERYREPLACE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "clang/AST/ASTConsumer.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorOr.h"
+
+namespace clang {
+namespace query {
+/// \brief Describes an AST query and replace operation.
+struct QueryReplaceSpec {
+  static llvm::ErrorOr<QueryReplaceSpec> parseFromJSON(llvm::StringRef Spec);
+  /// \brief clang-query style matcher expression
+  std::string Query;
+
+  /// \brief Identifier of the bound node to replace
+  std::string FromId;
+
+  /// \brief Replacement text. %"identifier" will be substituted by the text of
+  /// an identifier.
+  std::string ToTemplate;
+};
+
+class QueryReplaceTool {
+public:
+  QueryReplaceTool(
+      std::map<std::string, tooling::Replacements> &FileToReplaces);
+  void addOperation(QueryReplaceSpec &spec);
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+  struct ReplaceOp {
+    ast_matchers::dynamic::DynTypedMatcher Matcher;
+    std::unique_ptr<tooling::RefactoringCallback> Callback;
+  };
+  std::vector<ReplaceOp> Operations;
+
+  tooling::ASTMatchRefactorer Refactorer;
+};
+}
+}
+#endif
Index: clang-query/QueryReplace.cpp
===================================================================
--- /dev/null
+++ clang-query/QueryReplace.cpp
@@ -0,0 +1,73 @@
+#include "QueryReplace.h"
+#include "QueryParser.h"
+#include "QuerySession.h"
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using clang::query::QueryReplaceSpec;
+using clang::ast_matchers::MatchFinder;
+using clang::ast_type_traits::DynTypedNode;
+using clang::tooling::ReplaceNodeWithTemplate;
+namespace llvm {
+namespace yaml {
+template <>
+struct MappingTraits<clang::query::QueryReplaceSpec> {
+  static void mapping(IO &IO, clang::query::QueryReplaceSpec &Op) {
+    IO.mapRequired("Query", Op.Query);
+    IO.mapRequired("FromId", Op.FromId);
+    IO.mapRequired("ToTemplate", Op.ToTemplate);
+  }
+};
+}
+}
+
+namespace clang {
+namespace query {
+
+llvm::ErrorOr<QueryReplaceSpec> QueryReplaceSpec::parseFromJSON(
+    StringRef Spec) {
+  llvm::yaml::Input Input(Spec);
+  QueryReplaceSpec Op;
+  Input >> Op;
+  if (Input.error()) return Input.error();
+  return Op;
+}
+
+QueryReplaceTool::QueryReplaceTool(
+    std::map<std::string, tooling::Replacements> &FileToReplaces)
+    : Refactorer(FileToReplaces) {}
+
+void QueryReplaceTool::addOperation(clang::query::QueryReplaceSpec &Spec) {
+  ast_matchers::dynamic::Diagnostics Diag;
+  // FIXME: Support query sessions (for interactive use).
+  auto Matcher = ast_matchers::dynamic::Parser::parseMatcherExpression(
+      Spec.Query, nullptr, nullptr, &Diag);
+  if (!Matcher) {
+    llvm::errs() << "Cannot parse Query:";
+    Diag.printToStreamFull(llvm::errs());
+    llvm::errs() << "\n";
+  }
+
+  Operations.emplace_back(
+      ReplaceOp{Matcher.getValue(), llvm::make_unique<ReplaceNodeWithTemplate>(
+                                        Spec.FromId, Spec.ToTemplate)});
+
+  Refactorer.addDynamicMatcher(Operations.back().Matcher,
+                               Operations.back().Callback.get());
+}
+
+std::unique_ptr<ASTConsumer> QueryReplaceTool::newASTConsumer() {
+  return Refactorer.newASTConsumer();
+}
+}
+}
Index: clang-query/CMakeLists.txt
===================================================================
--- clang-query/CMakeLists.txt
+++ clang-query/CMakeLists.txt
@@ -6,6 +6,9 @@
 add_clang_library(clangQuery
   Query.cpp
   QueryParser.cpp
+  QueryReplace.cpp
+  QueryReplace.h
+
 
   LINK_LIBS
   clangAST
@@ -16,3 +19,4 @@
   )
 
 add_subdirectory(tool)
+add_subdirectory(replace-tool)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to