forked from OSchip/llvm-project
Add clang-reorder-fields to clang-tools-extra
This diff adds v0 of clang-reorder-fields tool to clang/tools/extra. The main idea behind this tool is to simplify and make less error-prone refactoring of large codebases when someone needs to change the order fields of a struct/class (for example to remove excess padding). Differential revision: https://reviews.llvm.org/D23279 llvm-svn: 280431
This commit is contained in:
parent
356f79d535
commit
ae4ff453a4
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,260 @@
|
|||
//===-- 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>
|
||||
|
||||
namespace clang {
|
||||
namespace reorder_fields {
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
/// \brief Finds the definition of a record by name.
|
||||
///
|
||||
/// \returns nullptr if the name is ambiguous or not found.
|
||||
static const CXXRecordDecl *findDefinition(StringRef RecordName,
|
||||
ASTContext &Context) {
|
||||
auto Results = match(
|
||||
recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl"),
|
||||
Context);
|
||||
if (Results.empty()) {
|
||||
llvm::errs() << "Definition of " << RecordName << " not found\n";
|
||||
return nullptr;
|
||||
}
|
||||
if (Results.size() > 1) {
|
||||
llvm::errs() << "The name " << RecordName
|
||||
<< " is ambiguous, several definitions found\n";
|
||||
return nullptr;
|
||||
}
|
||||
return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
|
||||
}
|
||||
|
||||
/// \brief Calculates the new order of fields.
|
||||
///
|
||||
/// \returns empty vector if the list of fields doesn't match the definition.
|
||||
static SmallVector<unsigned, 4>
|
||||
getNewFieldsOrder(const CXXRecordDecl *Definition,
|
||||
ArrayRef<std::string> DesiredFieldsOrder) {
|
||||
assert(Definition && "Definition is null");
|
||||
|
||||
llvm::StringMap<unsigned> NameToIndex;
|
||||
for (const auto *Field : Definition->fields())
|
||||
NameToIndex[Field->getName()] = Field->getFieldIndex();
|
||||
|
||||
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
|
||||
llvm::errs() << "Number of provided fields doesn't match definition.\n";
|
||||
return {};
|
||||
}
|
||||
SmallVector<unsigned, 4> NewFieldsOrder;
|
||||
for (const auto &Name : DesiredFieldsOrder) {
|
||||
if (!NameToIndex.count(Name)) {
|
||||
llvm::errs() << "Field " << Name << " not found in definition.\n";
|
||||
return {};
|
||||
}
|
||||
NewFieldsOrder.push_back(NameToIndex[Name]);
|
||||
}
|
||||
assert(NewFieldsOrder.size() == NameToIndex.size());
|
||||
return NewFieldsOrder;
|
||||
}
|
||||
|
||||
// FIXME: error-handling
|
||||
/// \brief Replaces one range of source code by another.
|
||||
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));
|
||||
}
|
||||
|
||||
/// \brief Reorders fields in the definition of a struct/class.
|
||||
///
|
||||
/// At the moment reodering of fields with
|
||||
/// different accesses (public/protected/private) is not supported.
|
||||
/// \returns true on success.
|
||||
static bool 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);
|
||||
|
||||
// Check that the permutation of the fields doesn't change the accesses
|
||||
for (const auto *Field : Definition->fields()) {
|
||||
const auto FieldIndex = Field->getFieldIndex();
|
||||
if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
|
||||
llvm::errs() << "Currently reodering of fields with different accesses "
|
||||
"is not supported\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto *Field : Definition->fields()) {
|
||||
const auto FieldIndex = Field->getFieldIndex();
|
||||
if (FieldIndex == NewFieldsOrder[FieldIndex])
|
||||
continue;
|
||||
addReplacement(Field->getSourceRange(),
|
||||
Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
|
||||
Context, Replacements);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Reorders initializers in a C++ struct/class constructor.
|
||||
///
|
||||
/// A constructor can have initializers for an arbitrary subset of the class's fields.
|
||||
/// Thus, we need to ensure that we reorder just the initializers that are present.
|
||||
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");
|
||||
if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
|
||||
return;
|
||||
|
||||
SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
|
||||
for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
|
||||
NewFieldsPositions[NewFieldsOrder[i]] = i;
|
||||
|
||||
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 i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
|
||||
if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
|
||||
addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
|
||||
NewWrittenInitializersOrder[i]->getSourceRange(), Context,
|
||||
Replacements);
|
||||
}
|
||||
|
||||
/// \brief Reorders initializers in the brace initialization of an aggregate.
|
||||
///
|
||||
/// At the moment partial initialization is not supported.
|
||||
/// \returns true on success
|
||||
static bool reorderFieldsInInitListExpr(
|
||||
const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
|
||||
const ASTContext &Context,
|
||||
std::map<std::string, tooling::Replacements> &Replacements) {
|
||||
assert(InitListEx && "Init list expression is null");
|
||||
// We care only about InitListExprs which originate from source code.
|
||||
// Implicit InitListExprs are created by the semantic analyzer.
|
||||
if (!InitListEx->isExplicit())
|
||||
return true;
|
||||
// The method InitListExpr::getSyntacticForm may return nullptr indicating that
|
||||
// the current initializer list also serves as its syntactic form.
|
||||
if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
|
||||
InitListEx = SyntacticForm;
|
||||
// If there are no initializers we do not need to change anything.
|
||||
if (!InitListEx->getNumInits())
|
||||
return true;
|
||||
if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
|
||||
llvm::errs() << "Currently only full initialization is supported\n";
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
|
||||
if (i != NewFieldsOrder[i])
|
||||
addReplacement(
|
||||
InitListEx->getInit(i)->getSourceRange(),
|
||||
InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
|
||||
Replacements);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
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)
|
||||
return;
|
||||
SmallVector<unsigned, 4> NewFieldsOrder =
|
||||
getNewFieldsOrder(RD, DesiredFieldsOrder);
|
||||
if (NewFieldsOrder.empty())
|
||||
return;
|
||||
if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
|
||||
return;
|
||||
for (const auto *C : RD->ctors())
|
||||
if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
|
||||
reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
|
||||
NewFieldsOrder, Context, Replacements);
|
||||
|
||||
// We only need to reorder init list expressions for aggregate types.
|
||||
// For other types the order of constructor parameters is used,
|
||||
// which we don't change at the moment.
|
||||
// Now (v0) partial initialization is not supported.
|
||||
if (RD->isAggregate())
|
||||
for (auto Result :
|
||||
match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
|
||||
Context))
|
||||
if (!reorderFieldsInInitListExpr(
|
||||
Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
|
||||
Context, Replacements)) {
|
||||
Replacements.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
|
||||
return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
|
||||
Replacements);
|
||||
}
|
||||
|
||||
} // namespace reorder_fields
|
||||
} // namespace clang
|
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,93 @@
|
|||
//===-- 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");
|
||||
|
||||
static cl::opt<std::string>
|
||||
RecordName("record-name", cl::Required,
|
||||
cl::desc("The name of the struct/class."),
|
||||
cl::cat(ClangReorderFieldsCategory));
|
||||
|
||||
static cl::list<std::string> FieldsOrder("fields-order", cl::CommaSeparated,
|
||||
cl::OneOrMore,
|
||||
cl::desc("The desired fields order."),
|
||||
cl::cat(ClangReorderFieldsCategory));
|
||||
|
||||
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
|
||||
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);
|
||||
|
||||
if (Inplace)
|
||||
return Tool.runAndSave(Factory.get());
|
||||
|
||||
int 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);
|
||||
// The method Rewriter::getRewriteBufferFor returns nullptr if
|
||||
// the file has not been changed.
|
||||
if (const auto *RB = Rewrite.getRewriteBufferFor(ID))
|
||||
RB->write(outs());
|
||||
else
|
||||
outs() << Sources.getMemoryBufferForFile(Entry)->getBuffer();
|
||||
}
|
||||
|
||||
return ExitCode;
|
||||
}
|
|
@ -45,6 +45,7 @@ set(CLANG_TOOLS_TEST_DEPS
|
|||
clang-include-fixer
|
||||
clang-query
|
||||
clang-rename
|
||||
clang-reorder-fields
|
||||
clang-tidy
|
||||
find-all-symbols
|
||||
modularize
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
|
||||
|
||||
// The order of fields should not change.
|
||||
class Foo {
|
||||
public:
|
||||
int x; // CHECK: {{^ int x;}}
|
||||
int y; // CHECK-NEXT: {{^ int y;}}
|
||||
int z; // CHECK-NEXT: {{^ int z;}}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Foo foo = { 0, 1 }; // CHECK: {{^ Foo foo = { 0, 1 };}}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// RUN: clang-reorder-fields -record-name ::Foo -fields-order y,x %s -- | FileCheck %s
|
||||
|
||||
struct Foo {
|
||||
int x; // CHECK: {{^ double y;}}
|
||||
double y; // CHECK-NEXT: {{^ int x;}}
|
||||
};
|
||||
|
||||
namespace bar {
|
||||
struct Foo {
|
||||
int x; // CHECK: {{^ int x;}}
|
||||
double y; // CHECK-NEXT: {{^ double y;}}
|
||||
};
|
||||
} // end namespace bar
|
||||
|
||||
int main() {
|
||||
bar::Foo foo = { 1, 1.7 }; // CHECK: {{^ bar::Foo foo = { 1, 1.7 };}}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x %s -- | FileCheck %s
|
||||
|
||||
namespace bar {
|
||||
struct Foo {
|
||||
const int* x; // CHECK: {{^ double z;}}
|
||||
int y; // CHECK-NEXT: {{^ int w;}}
|
||||
double z; // CHECK-NEXT: {{^ int y;}}
|
||||
int w; // CHECK-NEXT: {{^ const int\* x}}
|
||||
};
|
||||
} // end namespace bar
|
||||
|
||||
int main() {
|
||||
const int x = 13;
|
||||
bar::Foo foo = { &x, 0, 1.29, 17 }; // CHECK: {{^ bar::Foo foo = { 1.29, 17, 0, &x };}}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
|
||||
|
||||
// The order of fields should not change.
|
||||
class Foo {
|
||||
public:
|
||||
int x; // CHECK: {{^ int x;}}
|
||||
|
||||
private:
|
||||
int y; // CHECK: {{^ int y;}}
|
||||
int z; // CHECK-NEXT: {{^ int z;}}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Foo foo;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: clang-reorder-fields -record-name Foo -fields-order e,x,pi,s2,s1 %s -- -std=c++11 | FileCheck %s
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
Foo();
|
||||
|
||||
private:
|
||||
int x; // CHECK: {{^ double e = 2.71;}}
|
||||
const char *s1; // CHECK-NEXT: {{^ int x;}}
|
||||
const char *s2; // CHECK-NEXT: {{^ double pi = 3.14;}}
|
||||
double pi = 3.14; // CHECK-NEXT: {{^ const char \*s2;}}
|
||||
double e = 2.71; // CHECK-NEXT: {{^ const char \*s1;}}
|
||||
};
|
||||
|
||||
Foo::Foo():
|
||||
x(12), // CHECK: {{^ x\(12\)}},
|
||||
s1("abc"), // CHECK-NEXT: {{^ s2\("def"\)}},
|
||||
s2("def") // CHECK-NEXT: {{^ s1\("abc"\)}}
|
||||
{}
|
||||
|
||||
int main() {
|
||||
Foo foo;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// RUN: clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 %s -- | FileCheck %s
|
||||
|
||||
class Foo {
|
||||
public:
|
||||
Foo();
|
||||
|
||||
private:
|
||||
int x; // CHECK: {{^ const char \*s1;}}
|
||||
const char *s1; // CHECK-NEXT: {{^ int x;}}
|
||||
const char *s2; // CHECK-NEXT: {{^ double z;}}
|
||||
double z; // CHECK-NEXT: {{^ const char \*s2;}}
|
||||
};
|
||||
|
||||
Foo::Foo():
|
||||
x(12), // CHECK: {{^ s1\("abc"\),}}
|
||||
s1("abc"), // CHECK-NEXT: {{^ x\(12\),}}
|
||||
s2("def"), // CHECK-NEXT: {{^ z\(3.14\),}}
|
||||
z(3.14) // CHECK-NEXT: {{^ s2\("def"\)}}
|
||||
{}
|
||||
|
||||
int main() {
|
||||
Foo foo;
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue