blob: d36a080c0435fdfced237156da1bc4a2eae668e4 [file] [log] [blame] [edit]
//===-- 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)
: 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)
: 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