Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang-tools-extra/clang-reorder-fields/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
)

add_clang_library(clangReorderFields STATIC
Designator.cpp
ReorderFieldsAction.cpp

DEPENDS
Expand Down
219 changes: 219 additions & 0 deletions clang-tools-extra/clang-reorder-fields/Designator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//===-- tools/extra/clang-reorder-fields/utils/Designator.cpp ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the definition of the Designator and Designators utility
/// classes.
///
//===----------------------------------------------------------------------===//

#include "Designator.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "llvm/Support/raw_ostream.h"

namespace clang {
namespace reorder_fields {

void Designator::advanceToNextField() {
assert(!isFinished() && "Iterator is already finished");
switch (Tag) {
case STRUCT:
if (StructIt.Record->isUnion()) {
// Union always finishes on first increment.
StructIt.Field = StructIt.Record->field_end();
Type = QualType();
break;
}
++StructIt.Field;
if (StructIt.Field != StructIt.Record->field_end()) {
Type = StructIt.Field->getType();
} else {
Type = QualType();
}
break;
case ARRAY:
++ArrayIt.Index;
break;
case ARRAY_RANGE:
ArrayIt.Index = ArrayRangeIt.End + 1;
ArrayIt.Size = ArrayRangeIt.Size;
Tag = ARRAY;
break;
}
}

bool Designator::isFinished() {
switch (Tag) {
case STRUCT:
return StructIt.Field == StructIt.Record->field_end();
case ARRAY:
return ArrayIt.Index == ArrayIt.Size;
case ARRAY_RANGE:
return ArrayRangeIt.End == ArrayRangeIt.Size;
}
return false;
}

Designators::Designators(const Expr *Init, const InitListExpr *ILE,
const ASTContext *Context)
: ILE(ILE), Context(Context) {
if (ILE->getType()->isArrayType()) {
const ConstantArrayType *CAT =
Context->getAsConstantArrayType(ILE->getType());
// Only constant size arrays are supported.
if (!CAT) {
DesignatorList.clear();
return;
}
DesignatorList.push_back(
{CAT->getElementType(), 0, CAT->getSize().getZExtValue()});
} else {
const RecordDecl *DesignatorRD = ILE->getType()->getAsRecordDecl();
DesignatorList.push_back({DesignatorRD->field_begin()->getType(),
DesignatorRD->field_begin(), DesignatorRD});
}

// If the designator list is empty at this point, then there must be excess
// elements in the initializer list. They are not currently supported.
if (DesignatorList.empty())
return;

if (!enterImplicitInitLists(Init))
DesignatorList.clear();
}

Designators::Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE,
const ASTContext *Context)
: ILE(ILE), Context(Context) {
for (const auto &D : DIE->designators()) {
if (D.isFieldDesignator()) {
RecordDecl *DesignatorRecord = D.getFieldDecl()->getParent();
for (auto FieldIt = DesignatorRecord->field_begin();
FieldIt != DesignatorRecord->field_end(); ++FieldIt) {
if (*FieldIt == D.getFieldDecl()) {
DesignatorList.push_back(
{FieldIt->getType(), FieldIt, DesignatorRecord});
break;
}
}
} else {
const QualType CurrentType = DesignatorList.empty()
? ILE->getType()
: DesignatorList.back().getType();
const ConstantArrayType *CAT =
Context->getAsConstantArrayType(CurrentType);
if (!CAT) {
// Non-constant-sized arrays are not supported.
DesignatorList.clear();
return;
}
if (D.isArrayDesignator()) {
DesignatorList.push_back({CAT->getElementType(),
DIE->getArrayIndex(D)
->EvaluateKnownConstInt(*Context)
.getZExtValue(),
CAT->getSize().getZExtValue()});
} else if (D.isArrayRangeDesignator()) {
DesignatorList.push_back({CAT->getElementType(),
DIE->getArrayRangeStart(D)
->EvaluateKnownConstInt(*Context)
.getZExtValue(),
DIE->getArrayRangeEnd(D)
->EvaluateKnownConstInt(*Context)
.getZExtValue(),
CAT->getSize().getZExtValue()});
} else {
llvm_unreachable("Unexpected designator kind");
}
}
}
}

bool Designators::advanceToNextField(const Expr *Init) {
// Remove all designators that refer to the last field of a struct or final
// element of the array.
while (!DesignatorList.empty()) {
auto &CurrentDesignator = DesignatorList.back();
CurrentDesignator.advanceToNextField();
if (CurrentDesignator.isFinished()) {
DesignatorList.pop_back();
continue;
}
break;
}

// If the designator list is empty at this point, then there must be excess
// elements in the initializer list. They are not currently supported.
if (DesignatorList.empty())
return false;

if (!enterImplicitInitLists(Init)) {
DesignatorList.clear();
return false;
}

return true;
}

bool Designators::enterImplicitInitLists(const Expr *Init) {
// Check for missing braces by comparing the type of the last designator and
// type of Init.
while (true) {
const QualType T = DesignatorList.back().getType();
// If the types match, there are no missing braces.
if (Init->getType() == T)
break;

// If the current type is a struct, then get its first field.
if (T->isRecordType()) {
DesignatorList.push_back({T->getAsRecordDecl()->field_begin()->getType(),
T->getAsRecordDecl()->field_begin(),
T->getAsRecordDecl()});
continue;
}
// If the current type is an array, then get its first element.
if (T->isArrayType()) {
DesignatorList.push_back(
{Context->getAsArrayType(T)->getElementType(), 0,
Context->getAsConstantArrayType(T)->getSize().getZExtValue()});
continue;
}

// The initializer doesn't match the expected type. The initializer list is
// invalid.
return false;
}

return true;
}

std::string Designators::toString() const {
if (DesignatorList.empty())
return "";
std::string Designator;
llvm::raw_string_ostream OS(Designator);
for (auto &I : DesignatorList) {
switch (I.getTag()) {
case Designator::STRUCT:
OS << '.' << I.getStructIter()->getName();
break;
case Designator::ARRAY:
OS << '[' << I.getArrayIndex() << ']';
break;
case Designator::ARRAY_RANGE:
OS << '[' << I.getArrayRangeStart() << "..." << I.getArrayRangeEnd()
<< ']';
}
}
OS << " = ";
return Designator;
}

} // namespace reorder_fields
} // namespace clang
165 changes: 165 additions & 0 deletions clang-tools-extra/clang-reorder-fields/Designator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//===-- tools/extra/clang-reorder-fields/utils/Designator.h -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the declarations of the Designator and Designators
/// utility classes.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H

#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"

namespace clang {
namespace reorder_fields {

/// Represents a part of a designation in a C99/C++20 designated initializer. It
/// is a tagged union of different kinds of designators: struct, array and array
/// range. Holds enough information to be able to advance to the next field and
/// to know when all fields have been iterated through.
class Designator {
public:
enum Kind { STRUCT, ARRAY, ARRAY_RANGE };

Designator(const QualType Type, RecordDecl::field_iterator Field,
const RecordDecl *RD)
: Tag(STRUCT), Type(Type), StructIt({Field, RD}) {}

Designator(const QualType Type, uint64_t Idx, uint64_t Size)
: Tag(ARRAY), Type(Type), ArrayIt({Idx, Size}) {}

Designator(const QualType Type, uint64_t Start, uint64_t End, uint64_t Size)
: Tag(ARRAY_RANGE), Type(Type), ArrayRangeIt({Start, End, Size}) {}

/// Moves the iterator to the next element.
void advanceToNextField();

/// Checks if the iterator has iterated through all elements.
bool isFinished();

Kind getTag() const { return Tag; }
QualType getType() const { return Type; }

const RecordDecl::field_iterator getStructIter() const {
assert(Tag == STRUCT && "Must be a field designator");
return StructIt.Field;
}

const RecordDecl *getStructDecl() const {
assert(Tag == STRUCT && "Must be a field designator");
return StructIt.Record;
}

uint64_t getArrayIndex() const {
assert(Tag == ARRAY && "Must be an array designator");
return ArrayIt.Index;
}

uint64_t getArrayRangeStart() const {
assert(Tag == ARRAY_RANGE && "Must be an array range designator");
return ArrayRangeIt.Start;
}

uint64_t getArrayRangeEnd() const {
assert(Tag == ARRAY_RANGE && "Must be an array range designator");
return ArrayRangeIt.End;
}

uint64_t getArraySize() const {
assert((Tag == ARRAY || Tag == ARRAY_RANGE) &&
"Must be an array or range designator");
if (Tag == ARRAY)
return ArrayIt.Size;
return ArrayRangeIt.Size;
}

private:
/// Type of the designator.
Kind Tag;

/// Type of the designated entry. For arrays this is the type of the element.
QualType Type;

/// Field designator has the iterator to the field and the record the field
/// is declared in.
struct StructIter {
RecordDecl::field_iterator Field;
const RecordDecl *Record;
};

/// Array designator has an index and size of the array.
struct ArrayIter {
uint64_t Index;
uint64_t Size;
};

/// Array range designator has a start and end index and size of the array.
struct ArrayRangeIter {
uint64_t Start;
uint64_t End;
uint64_t Size;
};

union {
StructIter StructIt;
ArrayIter ArrayIt;
ArrayRangeIter ArrayRangeIt;
};
};

/// List of designators.
class Designators {
public:
/// Initialize to the first member of the struct/array. Enters implicit
/// initializer lists until a type that matches Init is found.
Designators(const Expr *Init, const InitListExpr *ILE,
const ASTContext *Context);

/// Initialize to the designators of the given expression.
Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE,
const ASTContext *Context);

/// Return whether this designator list is valid.
bool isValid() const { return !DesignatorList.empty(); }

/// Moves the designators to the next initializer in the struct/array. If the
/// type of next initializer doesn't match the expected type then there are
/// omitted braces and we add new designators to reflect that.
bool advanceToNextField(const Expr *Init);

/// Gets a string representation from a list of designators. This string will
/// be inserted before an initializer expression to make it designated.
std::string toString() const;

size_t size() const { return DesignatorList.size(); }

SmallVector<Designator>::const_iterator begin() const {
return DesignatorList.begin();
}
SmallVector<Designator>::const_iterator end() const {
return DesignatorList.end();
}

private:
/// Enters any implicit initializer lists until a type that matches the given
/// expression is found.
bool enterImplicitInitLists(const Expr *Init);

const InitListExpr *ILE;
const ASTContext *Context;
SmallVector<Designator, 1> DesignatorList;
};

} // namespace reorder_fields
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H
Loading
Loading