[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-05-08 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 98203.
jbangert added a comment.

Ran check-clang


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,40 +60,94 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
 }
 
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl",
+   

[PATCH] D29622: Add a batch query and replace tool based on AST matchers.

2017-04-05 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 94297.
jbangert added a comment.

Fix command line tool.


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,5 @@
+// RUN: cp %s %t.c
+// RUN: clang-query-replace --spec  '{Query: "varDecl(hasName(\"version\"), hasInitializer(integerLiteral().bind(\"init\"))).bind(\"decl\")", FromId: "decl", ToTemplate: "string version = \"$$-${init}\""}' %t.c --
+// RUN: FileCheck --match-full-lines %s < %t.c
+int version = 4;
+// CHECK: string version = "$-4";
Index: test/CMakeLists.txt
===
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -44,6 +44,7 @@
   clang-include-fixer
   clang-move
   clang-query
+  clang-query-replace
   clang-rename
   clang-reorder-fields
   clang-tidy
Index: clang-query/replace-tool/ClangQueryReplace.cpp
===
--- /dev/null
+++ clang-query/replace-tool/ClangQueryReplace.cpp
@@ -0,0 +1,85 @@
+//=== 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 <
+#include 
+#include 
+
+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 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  : Commands) {
+
+llvm::Expected Spec =
+QueryReplaceSpec::parseFromYAML(SpecYaml);
+if (llvm::Error Error = Spec.takeError())
+  llvm::report_fatal_error(std::move(Error), false);
+if (llvm::Error Error = Tool.addOperation(*Spec))
+  llvm::report_fatal_error(std::move(Error), false);
+  }
+
+  return Refactor.runAndSave(tooling::newFrontendActionFactory().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 @@
+//===--- QueryReplace.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 "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/Error.h"
+#include 
+#include 
+#include 
+

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-04-05 Thread Julian Bangert via Phabricator via cfe-commits
jbangert accepted this revision.
jbangert marked 2 inline comments as done.
jbangert added inline comments.



Comment at: unittests/Tooling/RefactoringCallbacksTest.cpp:101
+  std::string Expected = "void f() { FOO }";
+  ReplaceNodeWithTemplate Callback("id", "FOO");
+  expectRewritten(Code, Expected, id("id", declStmt()), Callback);

ioeric wrote:
> Have you rerun the tests? Does this still build?
ninja check-clang-tools works. 


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-04-05 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 94282.
jbangert added a comment.

- Merge branch 'master' of http://llvm.org/git/clang into replace_template


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,40 +60,94 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
 }
 
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  auto 

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-24 Thread Julian Bangert via Phabricator via cfe-commits
jbangert added inline comments.



Comment at: lib/Tooling/RefactoringCallbacks.cpp:213
+llvm::errs() << "Node " << Element.Value
+ << " used in replacement template not bound in Matcher 
\n";
+llvm_unreachable("Unbound node in replacement template.");

sbenza wrote:
> I don't know if stderr is the best place for this error output.
> Maybe we should take a sink of some sort in the constructor.
There's a FIXME: above that addresses the need for better error handling, and 
this one just duplicated their workaround. 
Ultimately, clang::tooling:: should probably provide infrastructure for 
reporting problems (I would imagine all refactoring tools have errors 
occassionally, and they need some way of reporting/handling them). 



Comment at: lib/Tooling/RefactoringCallbacks.cpp:214
+ << " used in replacement template not bound in Matcher 
\n";
+llvm_unreachable("Unbound node in replacement template.");
+  }

sbenza wrote:
> I don't think this is ok.
> afaik, llvm_unreachable leads to undefined behavior if it is reached in opt 
> mode.
> This error can be triggered from user input. We should not fail that way.
Using llvm::report_fatal_error for now. See the above for better error 
handling. 


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-24 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 89730.
jbangert marked an inline comment as done.
jbangert added a comment.

use llvm::report_fatal_error instead of unreachable.


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,40 +60,94 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
 }
 
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = 

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-22 Thread Julian Bangert via Phabricator via cfe-commits
jbangert accepted this revision.
jbangert added a comment.

Thanks, added tests for parser failures.


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-22 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 89420.
jbangert marked an inline comment as done.
jbangert added a comment.

- additional tests


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,40 +60,94 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
 }
 
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  auto Callback = 

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-09 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 87879.
jbangert added a comment.

- fix test


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,39 +60,68 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  auto Callback = ReplaceNodeWithTemplate::create("id", "FOO");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected, id("id", declStmt()), **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}");
+  EXPECT_FALSE(Callback.takeError());
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  **Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  auto Callback = ReplaceNodeWithTemplate::create("decl",
+

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-09 Thread Julian Bangert via Phabricator via cfe-commits
jbangert marked 2 inline comments as done.
jbangert added a comment.

Fixed the test -- I am still getting used to the different ninja test targets.




Comment at: lib/Tooling/RefactoringCallbacks.cpp:160
+llvm::Expected
+ReplaceNodeWithTemplate::create(StringRef FromId, StringRef ToTemplate) {
+  std::vector ParsedTemplate;

ioeric wrote:
> Is this covered in the test?
Now it is. 


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29622: Add a batch query and replace tool based on AST matchers.

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert added a comment.

Thank you for the feedback, addressed and reformatted using llvm style.




Comment at: clang-query/QueryReplace.h:35-36
+
+  /// \brief Replacement text. %"identifier" will be substituted by the text of
+  /// an identifier.
+  std::string ToTemplate;

sbenza wrote:
> klimek wrote:
> > This doesn't seem to come up in the test? (and I don't understand what it's 
> > trying to tell me :)
> In the other change the identifier was specified with ${identifier}, not 
> %"identifier".
> They should be consistent, no?
I edited the comment for consistency. This indeed has an older syntax. 


https://reviews.llvm.org/D29622



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29622: Add a batch query and replace tool based on AST matchers.

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 87580.
jbangert marked 7 inline comments as done.
jbangert added a comment.

Response to initial code review.


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,3 @@
+// RUN: clang-query-replace --spec  '{Query: "varDecl(hasName(\"version\"), hasInitializer(integerLiteral().bind(\"init\"))).bind(\"decl\")", FromId: "decl", ToTemplate: "string version = \"$$-${init}\""}' %s -- && FileCheck %s < %s
+int version = 4;
+// CHECK: string version = "$-4";
Index: test/CMakeLists.txt
===
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -47,6 +47,7 @@
   clang-include-fixer
   clang-move
   clang-query
+  clang-query-replace
   clang-rename
   clang-reorder-fields
   clang-tidy
@@ -57,11 +58,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,85 @@
+//=== 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 <
+#include 
+#include 
+
+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 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  : Commands) {
+
+llvm::Expected Spec =
+QueryReplaceSpec::parseFromYAML(SpecYaml);
+if (llvm::Error Error = Spec.takeError())
+  llvm::report_fatal_error(std::move(Error), false);
+if (llvm::Error Error = Tool.addOperation(*Spec))
+  llvm::report_fatal_error(std::move(Error), false);
+  }
+
+  return Refactor.runAndSave(tooling::newFrontendActionFactory().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 @@
+//===--- QueryReplace.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 

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 87579.
jbangert added a comment.

- use iter


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,39 +60,64 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  ReplaceNodeWithTemplate Callback("id", "FOO");
+  expectRewritten(Code, Expected, id("id", declStmt()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  ReplaceNodeWithTemplate Callback("decl", "long x = ${init}");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  ReplaceNodeWithTemplate Callback("decl", "string x = \"$$-${init}\"");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert marked 5 inline comments as done.
jbangert added a comment.

Thank you for the initial feedback!




Comment at: include/clang/Tooling/RefactoringCallbacks.h:61
+MatchFinder.addMatcher(Matcher, Callback);
+Callbacks.emplace_back(Callback);
+  }

sbenza wrote:
> Why emplace_back instead of push_back?
Changed to push_back.  Is there ever an advantage to using push_back over 
emplace_back (the latter falls back to a copy constructor). 


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert marked 2 inline comments as done.
jbangert added a comment.

(marking other comments as done).


https://reviews.llvm.org/D29621



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 87575.
jbangert added a comment.

- change to push_back


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,39 +60,64 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  ReplaceNodeWithTemplate Callback("id", "FOO");
+  expectRewritten(Code, Expected, id("id", declStmt()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  ReplaceNodeWithTemplate Callback("decl", "long x = ${init}");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  ReplaceNodeWithTemplate Callback("decl", "string x = \"$$-${init}\"");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", 

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 87574.
jbangert added a comment.

Address code feedback & Reformat with clang-format --style llvm.


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,39 +60,64 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  ReplaceNodeWithTemplate Callback("id", "FOO");
+  expectRewritten(Code, Expected, id("id", declStmt()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  ReplaceNodeWithTemplate Callback("decl", "long x = ${init}");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  ReplaceNodeWithTemplate Callback("decl", "string x = \"$$-${init}\"");
+  expectRewritten(Code, Expected,
+  

[PATCH] D29621: Add ASTMatchRefactorer and ReplaceNodeWithTemplate to RefactoringCallbacks

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
jbangert created this revision.

This is the first change as part of developing a clang-query based search and 
replace tool.


https://reviews.llvm.org/D29621

Files:
  include/clang/Tooling/RefactoringCallbacks.h
  lib/Tooling/RefactoringCallbacks.cpp
  unittests/Tooling/RefactoringCallbacksTest.cpp

Index: unittests/Tooling/RefactoringCallbacksTest.cpp
===
--- unittests/Tooling/RefactoringCallbacksTest.cpp
+++ unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -7,31 +7,30 @@
 //
 //===--===//
 
-#include "clang/Tooling/RefactoringCallbacks.h"
 #include "RewriterTestContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
 #include "gtest/gtest.h"
 
 namespace clang {
 namespace tooling {
 
 using namespace ast_matchers;
 
 template 
-void expectRewritten(const std::string ,
- const std::string ,
- const T ,
- RefactoringCallback ) {
-  MatchFinder Finder;
+void expectRewritten(const std::string , const std::string ,
+ const T , RefactoringCallback ) {
+  std::map FileToReplace;
+  ASTMatchRefactorer Finder(FileToReplace);
   Finder.addMatcher(AMatcher, );
   std::unique_ptr Factory(
   tooling::newFrontendActionFactory());
   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
   << "Parsing error in \"" << Code << "\"";
   RewriterTestContext Context;
   FileID ID = Context.createInMemoryFile("input.cc", Code);
-  EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+  EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"],
 Context.Rewrite));
   EXPECT_EQ(Expected, Context.getRewrittenText(ID));
 }
@@ -61,40 +60,65 @@
   std::string Code = "void f() { int i = 1; }";
   std::string Expected = "void f() { int i = 2; }";
   ReplaceStmtWithText Callback("id", "2");
-  expectRewritten(Code, Expected, id("id", expr(integerLiteral())),
-  Callback);
+  expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
   std::string Code = "void f() { int i = false ? 1 : i * 2; }";
   std::string Expected = "void f() { int i = i * 2; }";
   ReplaceStmtWithStmt Callback("always-false", "should-be");
-  expectRewritten(Code, Expected,
-  id("always-false", conditionalOperator(
-  hasCondition(cxxBoolLiteral(equals(false))),
-  hasFalseExpression(id("should-be", expr(),
+  expectRewritten(
+  Code, Expected,
+  id("always-false",
+ conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expr(),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
   std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
   std::string Expected = "bool a; void f() { f(); }";
   ReplaceIfStmtWithItsBody Callback("id", true);
-  expectRewritten(Code, Expected,
-  id("id", ifStmt(
-  hasCondition(implicitCastExpr(hasSourceExpression(
-  declRefExpr(to(varDecl(hasName("a"),
+  expectRewritten(
+  Code, Expected,
+  id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression(
+   declRefExpr(to(varDecl(hasName("a"),
   Callback);
 }
 
 TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
   std::string Code = "void f() { if (false) int i = 0; }";
   std::string Expected = "void f() {  }";
   ReplaceIfStmtWithItsBody Callback("id", false);
   expectRewritten(Code, Expected,
-  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
-  Callback);
+  id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateJustText) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { FOO }";
+  ReplaceNodeWithTemplate Callback("id", "FOO");
+  expectRewritten(Code, Expected, id("id", declStmt()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateSimpleSubst) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { long x = 1; }";
+  ReplaceNodeWithTemplate Callback("decl", "long x = ${init}");
+  expectRewritten(Code, Expected,
+  id("decl", varDecl(hasInitializer(id("init", expr(),
+  Callback);
+}
+
+TEST(RefactoringCallbacksTest, TemplateLiteral) {
+  std::string Code = "void f() { int i = 1; }";
+  std::string Expected = "void f() { string x = \"$-1\"; }";
+  ReplaceNodeWithTemplate Callback("decl", "string x = \"$$-${init}\"");
+  expectRewritten(Code, Expected,
+  id("decl", 

[PATCH] D29622: Add a batch query and replace tool based on AST matchers.

2017-02-07 Thread Julian Bangert via Phabricator via cfe-commits
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 <
+#include 
+#include 
+
+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 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  : Commands) {
+llvm::ErrorOr 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().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.
+//

[PATCH] D26015: Correctly classify main file includes if there is a prefix added

2017-01-11 Thread Julian Bangert via Phabricator via cfe-commits
jbangert updated this revision to Diff 84001.
jbangert updated the summary for this revision.
jbangert added a comment.

- Add header to unit test fixture.


https://reviews.llvm.org/D26015

Files:
  clang-tidy/utils/IncludeSorter.cpp
  unittests/clang-tidy/IncludeInserterTest.cpp

Index: unittests/clang-tidy/IncludeInserterTest.cpp
===
--- unittests/clang-tidy/IncludeInserterTest.cpp
+++ unittests/clang-tidy/IncludeInserterTest.cpp
@@ -73,6 +73,17 @@
   bool IsAngledInclude() const override { return false; }
 };
 
+class EarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  EarlyInAlphabetHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+  : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector HeadersToInclude() const override {
+return {"a/header.h"};
+  }
+  bool IsAngledInclude() const override { return false; }
+};
+
 class MultipleHeaderInserterCheck : public IncludeInserterCheckBase {
 public:
   MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
@@ -114,6 +125,7 @@
"insert_includes_test_header.h",
"\n"},
   // Non system headers
+  {"a/header.h", "\n"},
   {"path/to/a/header.h", "\n"},
   {"path/to/z/header.h", "\n"},
   {"path/to/header.h", "\n"},
@@ -522,6 +534,77 @@
"insert_includes_test_header.cc"));
 }
 
+TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include 
+#include 
+#include 
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include 
+#include 
+#include 
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode(
+  PreCode, "workspace_folder/clang_tidy/tests/"
+   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, HandleOrderInSubdirectory) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include 
+#include 
+#include 
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include 
+#include 
+#include 
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode(
+  PreCode, "workspace_folder/clang_tidy/tests/"
+   "insert_includes_test_header.cc"));
+}
+
 } // anonymous namespace
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/utils/IncludeSorter.cpp
===
--- clang-tidy/utils/IncludeSorter.cpp
+++ clang-tidy/utils/IncludeSorter.cpp
@@ -61,7 +61,8 @@
   : IncludeSorter::IK_CXXSystemInclude;
   }
   StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
-  if (CanonicalFile.equals(CanonicalInclude)) {
+  if (CanonicalFile.endswith(CanonicalInclude)
+  || CanonicalInclude.endswith(CanonicalFile)) {
 return IncludeSorter::IK_MainTUInclude;
   }
   if (Style == IncludeSorter::IS_Google) {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits