alexshap removed rL LLVM as the repository for this revision.
alexshap changed the visibility of this Differential Revision from "All Users" 
to "Public (No Login Required)".
alexshap updated this revision to Diff 68943.
alexshap added a comment.

Fix typo


https://reviews.llvm.org/D23279

Files:
  CMakeLists.txt
  clang-reorder-fields/CMakeLists.txt
  clang-reorder-fields/ReorderFieldsAction.cpp
  clang-reorder-fields/ReorderFieldsAction.h
  clang-reorder-fields/tool/CMakeLists.txt
  clang-reorder-fields/tool/ClangReorderFields.cpp
  test/CMakeLists.txt
  test/clang-reorder-fields/CStructAmbiguousName.cpp
  test/clang-reorder-fields/CStructFieldPos.cpp
  test/clang-reorder-fields/CStructFieldsOrder.cpp
  test/clang-reorder-fields/ClassMixedInitialization.cpp
  test/clang-reorder-fields/ClassSimpleCtor.cpp

Index: test/clang-reorder-fields/ClassSimpleCtor.cpp
===================================================================
--- test/clang-reorder-fields/ClassSimpleCtor.cpp
+++ test/clang-reorder-fields/ClassSimpleCtor.cpp
@@ -0,0 +1,26 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {
+public:
+  Foo();
+
+private:
+  int x;              // CHECK: const char *s1;
+  const char *s1;     // CHECK: int x;
+  const char *s2;     // CHECK: double z;
+  double z;           // CHECK: const char *s2;
+};
+
+Foo::Foo():
+  x(12),      // CHECK: s1("abc"),
+  s1("abc"),  // CHECK: x(12),
+  s2("def"),  // CHECK: z(3.14),
+  z(3.14)     // CHECK: s2("def")
+{}
+
+int main() {
+  Foo foo;
+  return 0;
+}
Index: test/clang-reorder-fields/ClassMixedInitialization.cpp
===================================================================
--- test/clang-reorder-fields/ClassMixedInitialization.cpp
+++ test/clang-reorder-fields/ClassMixedInitialization.cpp
@@ -0,0 +1,26 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-reorder-fields -record-name Foo -fields-order e,x,pi,s2,s1 %t.cpp -i -- -std=c++11
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {
+public:
+  Foo();
+
+private:
+  int x;              // CHECK: double e = 2.71;
+  const char *s1;     // CHECK: int x;
+  const char *s2;     // CHECK: double pi = 3.14;
+  double pi = 3.14;   // CHECK: const char *s2;
+  double e = 2.71;    // CHECK: const char *s1;
+};
+
+Foo::Foo():
+  x(12),      // CHECK: x(12),
+  s1("abc"),  // CHECK: s2("def"),
+  s2("def")   // CHECK: s1("abc")
+{}
+
+int main() {
+  Foo foo;
+  return 0;
+}
Index: test/clang-reorder-fields/CStructFieldsOrder.cpp
===================================================================
--- test/clang-reorder-fields/CStructFieldsOrder.cpp
+++ test/clang-reorder-fields/CStructFieldsOrder.cpp
@@ -0,0 +1,18 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+namespace bar {
+struct Foo {
+  const int* x; // CHECK: double z;
+  int y;        // CHECK: int w;
+  double z;     // CHECK: int y;
+  int w;        // CHECK: const int* x;
+};
+} // end namespace bar
+
+int main() {
+  const int x = 13;
+  bar::Foo foo = { &x, 17, 1.29, 0 }; // CHECK: bar::Foo foo = { 1.29, 0, 17, &x };
+  return 0;
+}
Index: test/clang-reorder-fields/CStructFieldPos.cpp
===================================================================
--- test/clang-reorder-fields/CStructFieldPos.cpp
+++ test/clang-reorder-fields/CStructFieldPos.cpp
@@ -0,0 +1,16 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-reorder-fields -record-name ::Foo -field-pos z:0 %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+struct Foo {
+  const int* x; // CHECK: double z;
+  int y;        // CHECK: const int* x;
+  double z;     // CHECK: int y;
+  int w;        // CHECK: int w;
+};
+
+int main() {
+  const int x = 13;
+  Foo foo = { &x, 17, 1.29, 0 }; // CHECK: Foo foo = { 1.29, &x, 17, 0 };
+  return 0;
+}
Index: test/clang-reorder-fields/CStructAmbiguousName.cpp
===================================================================
--- test/clang-reorder-fields/CStructAmbiguousName.cpp
+++ test/clang-reorder-fields/CStructAmbiguousName.cpp
@@ -0,0 +1,20 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-reorder-fields -record-name Foo -fields-order y,x %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+struct Foo {
+  int x;    // CHECK: int x;
+  double y; // CHECK: double y;
+};
+
+namespace bar {
+struct Foo {
+  int x;    // CHECK: int x;
+  double y; // CHECK: double y;
+};
+} // end namespace bar
+
+int main() {
+  Foo foo = { 1, 1.7 }; // CHECK: Foo foo = { 1, 1.7 };
+  return 0;
+}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -45,6 +45,7 @@
   clang-include-fixer
   clang-query
   clang-rename
+  clang-reorder-fields
   clang-tidy
   find-all-symbols
   modularize
Index: clang-reorder-fields/tool/ClangReorderFields.cpp
===================================================================
--- clang-reorder-fields/tool/ClangReorderFields.cpp
+++ clang-reorder-fields/tool/ClangReorderFields.cpp
@@ -0,0 +1,135 @@
+//===-- tools/extra/clang-reorder-fields/tool/ClangReorderFields.cpp -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation of clang-reorder-fields tool
+///
+//===----------------------------------------------------------------------===//
+
+#include "../ReorderFieldsAction.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include <cstdlib>
+#include <string>
+#include <system_error>
+#include <tuple>
+
+using namespace llvm;
+using namespace clang;
+
+cl::OptionCategory ClangReorderFieldsCategory("clang-reorder-fields options");
+
+// FIXME: error-handling
+struct FieldsOrderParser : cl::parser<SmallVector<std::string, 4>> {
+  explicit FieldsOrderParser(cl::Option &O)
+      : cl::parser<SmallVector<std::string, 4>>(O){};
+
+  bool parse(cl::Option &O, StringRef ArgName, const std::string &ArgValue,
+             SmallVector<std::string, 4> &Val) {
+    Val.clear();
+    SmallVector<StringRef, 4> Parts;
+    StringRef(ArgValue).split(Parts, ',');
+    for (const auto Part : Parts)
+      Val.push_back(Part.str());
+    return Val.empty();
+  }
+};
+
+// FIXME: error-handling
+struct FieldPositionParser : cl::parser<reorder_fields::FieldPosition> {
+  explicit FieldPositionParser(cl::Option &O)
+      : cl::parser<reorder_fields::FieldPosition>(O){};
+
+  bool parse(cl::Option &O, StringRef ArgName, const std::string &ArgValue,
+             reorder_fields::FieldPosition &Val) {
+    StringRef Name;
+    StringRef Position;
+    std::tie(Name, Position) = StringRef(ArgValue).split(":");
+    Val.Name = Name.str();
+    return Position.getAsInteger(10, Val.Position);
+  }
+};
+
+static cl::opt<std::string> RecordName("record-name",
+                                       cl::desc("The name of the struct/class."),
+                                       cl::cat(ClangReorderFieldsCategory));
+
+static cl::opt<SmallVector<std::string, 4>, false, FieldsOrderParser>
+    FieldsOrder("fields-order", cl::desc("The desired fields order."),
+                cl::cat(ClangReorderFieldsCategory));
+
+static cl::list<reorder_fields::FieldPosition, bool, FieldPositionParser>
+    FieldsPositions("field-pos", cl::desc("The new field position."),
+                    cl::cat(ClangReorderFieldsCategory));
+
+static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
+                             cl::cat(ClangReorderFieldsCategory));
+
+const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
+
+int main(int argc, const char **argv) {
+  tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
+                                  Usage);
+
+  auto Files = OP.getSourcePathList();
+  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+
+  if (!FieldsOrder.empty() && !FieldsPositions.empty()) {
+    errs() << "Either fields-order or field-pos should not be specified\n";
+    exit(1);
+  }
+
+  std::unique_ptr<reorder_fields::ReorderFieldsAction> Action;
+  if (!FieldsOrder.empty())
+    Action.reset(new reorder_fields::ReorderFieldsAction(
+      RecordName, FieldsOrder, Tool.getReplacements()));
+
+  if (!FieldsPositions.empty())
+    Action.reset(new reorder_fields::ReorderFieldsAction(
+      RecordName, FieldsPositions, Tool.getReplacements()));
+
+  auto Factory = tooling::newFrontendActionFactory(Action.get());
+  int ExitCode;
+
+  if (Inplace) {
+    ExitCode = Tool.runAndSave(Factory.get());
+  } else {
+    ExitCode = Tool.run(Factory.get());
+    
+    LangOptions DefaultLangOptions;
+    IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+    TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+    DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+
+    auto &FileMgr = Tool.getFiles();
+    SourceManager Sources(Diagnostics, FileMgr);
+    Rewriter Rewrite(Sources, DefaultLangOptions);
+    Tool.applyAllReplacements(Rewrite);
+
+    for (const auto &File : Files) {
+      const auto *Entry = FileMgr.getFile(File);
+      const auto ID = Sources.translateFile(Entry);
+      Rewrite.getEditBuffer(ID).write(outs());
+    }
+  }
+  exit(ExitCode);
+}
Index: clang-reorder-fields/tool/CMakeLists.txt
===================================================================
--- clang-reorder-fields/tool/CMakeLists.txt
+++ clang-reorder-fields/tool/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_clang_executable(clang-reorder-fields ClangReorderFields.cpp)
+
+target_link_libraries(clang-reorder-fields
+  clangBasic
+  clangFrontend
+  clangReorderFields
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-reorder-fields RUNTIME DESTINATION bin)
Index: clang-reorder-fields/ReorderFieldsAction.h
===================================================================
--- clang-reorder-fields/ReorderFieldsAction.h
+++ clang-reorder-fields/ReorderFieldsAction.h
@@ -0,0 +1,59 @@
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.h -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declarations of the ReorderFieldsAction class and
+/// the FieldPosition struct.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+
+namespace reorder_fields {
+struct FieldPosition {
+  std::string Name;
+  unsigned Position;
+};
+
+class ReorderFieldsAction {
+  llvm::StringRef RecordName;
+  llvm::ArrayRef<std::string> DesiredFieldsOrder;
+  llvm::ArrayRef<FieldPosition> DesiredFieldsPositions;
+  std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+  ReorderFieldsAction(
+      llvm::StringRef RecordName,
+      llvm::ArrayRef<std::string> DesiredFieldsOrder,
+      std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+        Replacements(Replacements) {}
+
+  ReorderFieldsAction(
+      llvm::StringRef RecordName,
+      llvm::ArrayRef<FieldPosition> DesiredFieldsPositions,
+      std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsPositions(DesiredFieldsPositions),
+        Replacements(Replacements) {}
+
+  ReorderFieldsAction(const ReorderFieldsAction &) = delete;
+  ReorderFieldsAction &operator=(const ReorderFieldsAction &) = delete;
+
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+};
+} // namespace reorder_fields
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
Index: clang-reorder-fields/ReorderFieldsAction.cpp
===================================================================
--- clang-reorder-fields/ReorderFieldsAction.cpp
+++ clang-reorder-fields/ReorderFieldsAction.cpp
@@ -0,0 +1,323 @@
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the
+/// ReorderFieldsAction::newASTConsumer method
+///
+//===----------------------------------------------------------------------===//
+
+#include "ReorderFieldsAction.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::ast_matchers;
+
+static const CXXRecordDecl *findDefinition(StringRef RecordName,
+                                           ASTContext &Context) {
+  struct DefinitionMatchCallback : MatchFinder::MatchCallback {
+    DefinitionMatchCallback() : RecordDecl(nullptr) {}
+    virtual void run(const MatchFinder::MatchResult &Result) override {
+      const auto *RD = Result.Nodes.getNodeAs<CXXRecordDecl>("cxxRecordDecl");
+      if (RecordDecl) {
+        errs() << "Ambiguous name, several matches found: "
+               << RecordDecl->getQualifiedNameAsString() << ", "
+               << RD->getQualifiedNameAsString() << "\n";
+        RecordDecl = nullptr;
+        return;
+      }
+      RecordDecl = RD;
+    }
+    const CXXRecordDecl *RecordDecl;
+  };
+
+  MatchFinder Finder;
+  DefinitionMatchCallback Callback;
+  const auto Matcher =
+      recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl");
+  Finder.addMatcher(Matcher, &Callback);
+  Finder.matchAST(Context);
+  return Callback.RecordDecl;
+}
+
+// FIXME: error-handling
+static void
+addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
+               std::map<std::string, tooling::Replacements> &Replacements) {
+  StringRef NewText =
+      Lexer::getSourceText(CharSourceRange::getTokenRange(New),
+                           Context.getSourceManager(), Context.getLangOpts());
+  tooling::Replacement R(Context.getSourceManager(),
+                         CharSourceRange::getTokenRange(Old), NewText,
+                         Context.getLangOpts());
+  consumeError(Replacements[R.getFilePath()].add(R));
+}
+
+static void reorderFieldsInDefinition(
+    const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(Definition && "Definition is null");
+
+  SmallVector<const FieldDecl *, 10> Fields;
+  for (const auto *Field : Definition->fields())
+    Fields.push_back(Field);
+  for (const auto *Field : Definition->fields()) {
+    const auto FieldIndex = Field->getFieldIndex();
+    if (FieldIndex == NewFieldsOrder[FieldIndex])
+      continue;
+    assert(Field->getAccess() ==
+               Fields[NewFieldsOrder[FieldIndex]]->getAccess() &&
+           "Currently reodering of fields with different accesses is not "
+           "supported");
+    addReplacement(Field->getSourceRange(),
+                   Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
+                   Context, Replacements);
+  }
+}
+
+static void reorderFieldsInConstructor(
+    const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(CtorDecl && "Constructor declaration is null");
+  assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
+
+  SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
+  for (unsigned Index = 0; Index < NewFieldsOrder.size(); ++Index)
+    NewFieldsPositions[NewFieldsOrder[Index]] = Index;
+
+  SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
+  SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
+  for (const auto *Initializer : CtorDecl->inits()) {
+    if (!Initializer->isWritten())
+      continue;
+    OldWrittenInitializersOrder.push_back(Initializer);
+    NewWrittenInitializersOrder.push_back(Initializer);
+  }
+  auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
+                                const CXXCtorInitializer *RHS) {
+    assert(LHS && RHS);
+    return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
+           NewFieldsPositions[RHS->getMember()->getFieldIndex()];
+  };
+  std::sort(std::begin(NewWrittenInitializersOrder),
+            std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
+  assert(OldWrittenInitializersOrder.size() ==
+         NewWrittenInitializersOrder.size());
+  for (unsigned Index = 0; Index < NewWrittenInitializersOrder.size(); ++Index)
+    addReplacement(OldWrittenInitializersOrder[Index]->getSourceRange(),
+                   NewWrittenInitializersOrder[Index]->getSourceRange(),
+                   Context, Replacements);
+}
+
+static void reorderFieldsInInitListExpr(
+    const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(InitListEx && "Init list expression is null");
+  // FIXME: InitListExpr::isExplicit probably should be const because
+  // SourceLocation::isValid is const.
+  if (!const_cast<clang::InitListExpr *>(InitListEx)->isExplicit() ||
+      !InitListEx->getNumInits())
+    return;
+  assert(InitListEx->getNumInits() == NewFieldsOrder.size() &&
+         "Currently only full initialization is supported");
+  for (unsigned Index = 0, NumInits = InitListEx->getNumInits();
+       Index < NumInits; ++Index) {
+    if (Index == NewFieldsOrder[Index])
+      continue;
+    addReplacement(InitListEx->getInit(Index)->getSourceRange(),
+                   InitListEx->getInit(NewFieldsOrder[Index])->getSourceRange(),
+                   Context, Replacements);
+  }
+}
+
+namespace clang {
+namespace reorder_fields {
+namespace {
+class InitListExprMatchCallback : public MatchFinder::MatchCallback {
+  const CXXRecordDecl *Definition;
+  ArrayRef<unsigned> NewFieldsOrder;
+  const ASTContext &Context;
+  std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+  InitListExprMatchCallback(
+      const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
+      const ASTContext &Context,
+      std::map<std::string, tooling::Replacements> &Replacements)
+      : Definition(Definition), NewFieldsOrder(NewFieldsOrder),
+        Context(Context), Replacements(Replacements) {}
+
+  virtual void run(const MatchFinder::MatchResult &Result) override {
+    const auto *E = Result.Nodes.getNodeAs<InitListExpr>("initListExpr");
+    assert(E && "InitListExpr is null");
+    const auto *T = E->getType().getCanonicalType().getTypePtr();
+    assert(T && "Type is null");
+    const auto *RD = T->getAsCXXRecordDecl();
+    if (!RD)
+      return;
+    if (Definition != RD->getDefinition())
+      return;
+    reorderFieldsInInitListExpr(E, NewFieldsOrder, Context, Replacements);
+  }
+};
+
+class ReorderingConsumer : public ASTConsumer {
+  StringRef RecordName;
+  ArrayRef<std::string> DesiredFieldsOrder;
+  ArrayRef<FieldPosition> DesiredFieldsPositions;
+  std::map<std::string, tooling::Replacements> &Replacements;
+  SmallVector<unsigned, 4> NewFieldsOrder;
+
+  bool setNewFieldsOrder(const CXXRecordDecl *Definition,
+                         ArrayRef<std::string> DesiredFieldsOrder) {
+    assert(Definition && "Definition is null");
+
+    StringMap<unsigned> NameToIndex;
+    for (const auto *Field : Definition->fields())
+      NameToIndex[Field->getName()] = Field->getFieldIndex();
+
+    if (DesiredFieldsOrder.size() != NameToIndex.size()) {
+      errs() << "Number of provided fields doesn't match definition.\n";
+      return false;
+    }
+
+    NewFieldsOrder.clear();
+    for (const auto &Name : DesiredFieldsOrder) {
+      if (!NameToIndex.count(Name)) {
+        errs() << "Field " << Name << " not found in definition.\n";
+        return false;
+      }
+      NewFieldsOrder.push_back(NameToIndex[Name]);
+    }
+    assert(NewFieldsOrder.size() == NameToIndex.size());
+    return true;
+  }
+
+  bool setNewFieldsOrder(const CXXRecordDecl *Definition,
+                         ArrayRef<FieldPosition> DesiredFieldsPositions) {
+    assert(Definition && "Definition is null");
+
+    StringMap<unsigned> NameToIndex;
+    for (const auto *Field : Definition->fields())
+      NameToIndex[Field->getName()] = Field->getFieldIndex();
+
+    StringSet<> AllocatedFields;
+    LLVM_CONSTEXPR unsigned INF = std::numeric_limits<unsigned>::max();
+    NewFieldsOrder.assign(NameToIndex.size(), INF);
+    for (const auto &FP : DesiredFieldsPositions) {
+      if (!NameToIndex.count(FP.Name)) {
+        errs() << "Field " << FP.Name << " not found in the definition\n";
+        return false;
+      }
+      if (FP.Position >= NewFieldsOrder.size()) {
+        errs() << "Field [" << FP.Name << "]"
+               << " position [" << FP.Position << "] out of range\n";
+        return false;
+      }
+      if (AllocatedFields.count(FP.Name)) {
+        errs() << "Field " << FP.Name << " specified twice\n";
+        return false;
+      }
+      NewFieldsOrder[FP.Position] = NameToIndex[FP.Name];
+      AllocatedFields.insert(FP.Name);
+    }
+
+    unsigned NextFreePosition = 0;
+    for (const auto *Field : Definition->fields()) {
+      if (AllocatedFields.count(Field->getName()))
+        continue;
+      while (NextFreePosition < NewFieldsOrder.size() &&
+             NewFieldsOrder[NextFreePosition] != INF)
+        ++NextFreePosition;
+      assert(NextFreePosition < NewFieldsOrder.size());
+      NewFieldsOrder[NextFreePosition] = Field->getFieldIndex();
+    }
+    return true;
+  }
+
+public:
+  ReorderingConsumer(StringRef RecordName,
+                     ArrayRef<std::string> DesiredFieldsOrder,
+                     std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+        Replacements(Replacements) {}
+
+  ReorderingConsumer(StringRef RecordName,
+                     ArrayRef<FieldPosition> DesiredFieldsPositions,
+                     std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsPositions(DesiredFieldsPositions),
+        Replacements(Replacements) {}
+
+  ReorderingConsumer(const ReorderingConsumer &) = delete;
+  ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
+
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const CXXRecordDecl *RD = findDefinition(RecordName, Context);
+    if (!RD) {
+      errs() << "Definition for name " << RecordName
+             << " not found or ambiguous.\n";
+      return;
+    }
+    if (!DesiredFieldsOrder.empty() &&
+        !setNewFieldsOrder(RD, DesiredFieldsOrder))
+      return;
+    if (!DesiredFieldsPositions.empty() &&
+        !setNewFieldsOrder(RD, DesiredFieldsPositions))
+      return;
+    reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements);
+    for (const auto *C : RD->ctors()) {
+      if (C->isImplicit() || C->isDelegatingConstructor())
+        continue;
+      const auto *D = C->getDefinition();
+      if (!D)
+        continue;
+      reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
+                                 NewFieldsOrder, Context, Replacements);
+    }
+    if (!RD->isAggregate())
+      return;
+    MatchFinder Finder;
+    InitListExprMatchCallback Callback(RD, NewFieldsOrder, Context,
+                                       Replacements);
+    const auto Matcher = initListExpr().bind("initListExpr");
+    Finder.addMatcher(Matcher, &Callback);
+    Finder.matchAST(Context);
+  }
+};
+} // end anonymous namespace
+
+std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
+  assert(!DesiredFieldsOrder.empty() || !DesiredFieldsPositions.empty());
+  if (!DesiredFieldsOrder.empty())
+    return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
+                                                 Replacements);
+  if (!DesiredFieldsPositions.empty())
+    return llvm::make_unique<ReorderingConsumer>(
+        RecordName, DesiredFieldsPositions, Replacements);
+  llvm_unreachable("Either list of fields or list of positions is not empty");
+}
+
+} // namespace reorder_fields
+} // namespace clang
Index: clang-reorder-fields/CMakeLists.txt
===================================================================
--- clang-reorder-fields/CMakeLists.txt
+++ clang-reorder-fields/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangReorderFields
+  ReorderFieldsAction.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangIndex
+  clangLex
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -1,5 +1,6 @@
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-rename)
+add_subdirectory(clang-reorder-fields)
 add_subdirectory(modularize)
 if(CLANG_ENABLE_STATIC_ANALYZER)
 add_subdirectory(clang-tidy)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to