alexshap updated this revision to Diff 68953. alexshap added a comment. Drop support of -field-pos for now
Repository: rL LLVM 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/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/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,104 @@ +//===-- 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> + +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(); + } +}; + +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::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); + + reorder_fields::ReorderFieldsAction Action(RecordName, FieldsOrder, + Tool.getReplacements()); + + auto Factory = tooling::newFrontendActionFactory(&Action); + 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,47 @@ +//===-- 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 { + +class ReorderFieldsAction { + llvm::StringRef RecordName; + llvm::ArrayRef<std::string> DesiredFieldsOrder; + 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(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,263 @@ +//===-- 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 <string> + +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; +} + +static bool setNewFieldsOrder(const CXXRecordDecl *Definition, + ArrayRef<std::string> DesiredFieldsOrder, + SmallVector<unsigned, 4> &NewFieldsOrder) { + 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; +} + +// 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; + std::map<std::string, tooling::Replacements> &Replacements; + +public: + ReorderingConsumer(StringRef RecordName, + ArrayRef<std::string> DesiredFieldsOrder, + std::map<std::string, tooling::Replacements> &Replacements) + : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder), + 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; + } + + SmallVector<unsigned, 4> NewFieldsOrder; + if (!setNewFieldsOrder(RD, DesiredFieldsOrder, NewFieldsOrder)) + 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() { + return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder, + Replacements); +} + +} // 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