https://github.com/vvuksanovic created 
https://github.com/llvm/llvm-project/pull/142150

Initializer lists with designators, missing elements or omitted braces can now 
be rewritten. Any missing designators are added and they get sorted according 
to the new order.

```
struct Foo {
  int a;
  int b;
  int c;
};
struct Foo foo = { .a = 1, 2, 3 }
```

when reordering elements to "b,a,c" becomes:

```
struct Foo {
  int b;
  int a;
  int c;
};
struct Foo foo = { .b = 2, .a = 1, .c = 3 }
```

>From 67febc3e5b388d38025a87e7824f5a0e5d6adaaa Mon Sep 17 00:00:00 2001
From: Vladimir Vuksanovic <vladimir.vuksano...@htecgroup.com>
Date: Fri, 30 May 2025 05:37:06 -0700
Subject: [PATCH] [clang-reorder-fields] Support designated initializers

Initializer lists with designators, missing elements or omitted braces can now 
be rewritten. Any missing designators are added and they get sorted according 
to the new order.

```
struct Foo {
  int a;
  int b;
  int c;
};
struct Foo foo = { .a = 1, 2, 3 }
```

when reordering elements to "b,a,c" becomes:

```
struct Foo {
  int b;
  int a;
  int c;
};
struct Foo foo = { .b = 2, .a = 1, .c = 3 }
```
---
 .../clang-reorder-fields/CMakeLists.txt       |   1 +
 .../ReorderFieldsAction.cpp                   | 301 +++++++++++++++---
 .../clang-reorder-fields/utils/Designator.cpp | 256 +++++++++++++++
 .../clang-reorder-fields/utils/Designator.h   | 118 +++++++
 .../DesignatedInitializerList.c               |  31 ++
 5 files changed, 659 insertions(+), 48 deletions(-)
 create mode 100644 clang-tools-extra/clang-reorder-fields/utils/Designator.cpp
 create mode 100644 clang-tools-extra/clang-reorder-fields/utils/Designator.h
 create mode 100644 
clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c

diff --git a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt 
b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt
index 2fdeb65d89767..dfb28234fd548 100644
--- a/clang-tools-extra/clang-reorder-fields/CMakeLists.txt
+++ b/clang-tools-extra/clang-reorder-fields/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
 )
 
 add_clang_library(clangReorderFields STATIC
+  utils/Designator.cpp
   ReorderFieldsAction.cpp
 
   DEPENDS
diff --git a/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp 
b/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
index ea0207619fb2b..f5961a7dab0c9 100644
--- a/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
+++ b/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp
@@ -13,6 +13,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "ReorderFieldsAction.h"
+#include "utils/Designator.h"
 #include "clang/AST/AST.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
@@ -23,6 +24,7 @@
 #include "clang/Tooling/Refactoring.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/Support/ErrorHandling.h"
 #include <string>
 
 namespace clang {
@@ -81,7 +83,44 @@ getNewFieldsOrder(const RecordDecl *Definition,
   return NewFieldsOrder;
 }
 
+struct ReorderedStruct {
+public:
+  ReorderedStruct(const RecordDecl *Decl, ArrayRef<unsigned> NewFieldsOrder)
+      : Definition(Decl), NewFieldsOrder(NewFieldsOrder),
+        NewFieldsPositions(NewFieldsOrder.size()) {
+    for (unsigned I = 0; I < NewFieldsPositions.size(); ++I)
+      NewFieldsPositions[NewFieldsOrder[I]] = I;
+  }
+
+  const RecordDecl *Definition;
+  ArrayRef<unsigned> NewFieldsOrder;
+  SmallVector<unsigned, 4> NewFieldsPositions;
+};
+
 // FIXME: error-handling
+/// Replaces a range of source code by the specified text.
+static void
+addReplacement(SourceRange Old, StringRef New, const ASTContext &Context,
+               std::map<std::string, tooling::Replacements> &Replacements) {
+  tooling::Replacement R(Context.getSourceManager(),
+                         CharSourceRange::getTokenRange(Old), New,
+                         Context.getLangOpts());
+  consumeError(Replacements[std::string(R.getFilePath())].add(R));
+}
+
+/// Replaces one range of source code by another and adds a prefix.
+static void
+addReplacement(SourceRange Old, SourceRange New, StringRef Prefix,
+               const ASTContext &Context,
+               std::map<std::string, tooling::Replacements> &Replacements) {
+  std::string NewText =
+      (Prefix + Lexer::getSourceText(CharSourceRange::getTokenRange(New),
+                                     Context.getSourceManager(),
+                                     Context.getLangOpts()))
+          .str();
+  addReplacement(Old, NewText, Context, Replacements);
+}
+
 /// Replaces one range of source code by another.
 static void
 addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
@@ -89,10 +128,7 @@ addReplacement(SourceRange Old, SourceRange New, const 
ASTContext &Context,
   StringRef NewText =
       Lexer::getSourceText(CharSourceRange::getTokenRange(New),
                            Context.getSourceManager(), Context.getLangOpts());
-  tooling::Replacement R(Context.getSourceManager(),
-                         CharSourceRange::getTokenRange(Old), NewText,
-                         Context.getLangOpts());
-  consumeError(Replacements[std::string(R.getFilePath())].add(R));
+  addReplacement(Old, NewText.str(), Context, Replacements);
 }
 
 /// Find all member fields used in the given init-list initializer expr
@@ -194,33 +230,33 @@ static SourceRange getFullFieldSourceRange(const 
FieldDecl &Field,
 /// different accesses (public/protected/private) is not supported.
 /// \returns true on success.
 static bool reorderFieldsInDefinition(
-    const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
-    const ASTContext &Context,
+    const ReorderedStruct &RS, const ASTContext &Context,
     std::map<std::string, tooling::Replacements> &Replacements) {
-  assert(Definition && "Definition is null");
+  assert(RS.Definition && "Definition is null");
 
   SmallVector<const FieldDecl *, 10> Fields;
-  for (const auto *Field : Definition->fields())
+  for (const auto *Field : RS.Definition->fields())
     Fields.push_back(Field);
 
   // Check that the permutation of the fields doesn't change the accesses
-  for (const auto *Field : Definition->fields()) {
+  for (const auto *Field : RS.Definition->fields()) {
     const auto FieldIndex = Field->getFieldIndex();
-    if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) 
{
+    if (Field->getAccess() !=
+        Fields[RS.NewFieldsOrder[FieldIndex]]->getAccess()) {
       llvm::errs() << "Currently reordering of fields with different accesses "
                       "is not supported\n";
       return false;
     }
   }
 
-  for (const auto *Field : Definition->fields()) {
+  for (const auto *Field : RS.Definition->fields()) {
     const auto FieldIndex = Field->getFieldIndex();
-    if (FieldIndex == NewFieldsOrder[FieldIndex])
+    if (FieldIndex == RS.NewFieldsOrder[FieldIndex])
       continue;
-    addReplacement(
-        getFullFieldSourceRange(*Field, Context),
-        getFullFieldSourceRange(*Fields[NewFieldsOrder[FieldIndex]], Context),
-        Context, Replacements);
+    addReplacement(getFullFieldSourceRange(*Field, Context),
+                   getFullFieldSourceRange(
+                       *Fields[RS.NewFieldsOrder[FieldIndex]], Context),
+                   Context, Replacements);
   }
   return true;
 }
@@ -231,7 +267,7 @@ static bool reorderFieldsInDefinition(
 /// 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 CXXConstructorDecl *CtorDecl, const ReorderedStruct &RS,
     ASTContext &Context,
     std::map<std::string, tooling::Replacements> &Replacements) {
   assert(CtorDecl && "Constructor declaration is null");
@@ -243,10 +279,6 @@ static void reorderFieldsInConstructor(
   // Thus this assert needs to be after the previous checks.
   assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
 
-  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()) {
@@ -257,8 +289,8 @@ static void reorderFieldsInConstructor(
     const FieldDecl *ThisM = Initializer->getMember();
     const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context);
     for (const FieldDecl *UM : UsedMembers) {
-      if (NewFieldsPositions[UM->getFieldIndex()] >
-          NewFieldsPositions[ThisM->getFieldIndex()]) {
+      if (RS.NewFieldsPositions[UM->getFieldIndex()] >
+          RS.NewFieldsPositions[ThisM->getFieldIndex()]) {
         DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
         auto Description = ("reordering field " + UM->getName() + " after " +
                             ThisM->getName() + " makes " + UM->getName() +
@@ -276,8 +308,8 @@ static void reorderFieldsInConstructor(
   auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
                                 const CXXCtorInitializer *RHS) {
     assert(LHS && RHS);
-    return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
-           NewFieldsPositions[RHS->getMember()->getFieldIndex()];
+    return RS.NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
+           RS.NewFieldsPositions[RHS->getMember()->getFieldIndex()];
   };
   llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition);
   assert(OldWrittenInitializersOrder.size() ==
@@ -289,35 +321,205 @@ static void reorderFieldsInConstructor(
                      Replacements);
 }
 
+/// Replacement for broken InitListExpr::isExplicit function.
+/// TODO: Remove when InitListExpr::isExplicit is fixed.
+static bool isImplicitILE(const InitListExpr *ILE, const ASTContext &Context) {
+  // The ILE is implicit if either:
+  // - The left brace loc of the ILE matches the start of first init expression
+  //   (for non designated decls)
+  // - The right brace loc of the ILE matches the end of first init expression
+  //   (for designated decls)
+  // The first init expression should be taken from the syntactic form, but
+  // since the ILE could be implicit, there might not be a syntactic form.
+  // For that reason we have to check against all init expressions.
+  for (const Expr *Init : ILE->inits()) {
+    if (ILE->getLBraceLoc() == Init->getBeginLoc() ||
+        ILE->getRBraceLoc() == Init->getEndLoc())
+      return true;
+  }
+  return false;
+}
+
+/// Compares compatible designators according to the new struct order.
+/// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0 if
+/// they are equal.
+static int cmpDesignators(const DesignatorIter &Lhs, const DesignatorIter &Rhs,
+                          const ReorderedStruct &Struct) {
+  assert(Lhs.getTag() == Rhs.getTag() && "Incompatible designators");
+  switch (Lhs.getTag()) {
+  case DesignatorIter::STRUCT: {
+    assert(Lhs.getStructDecl() == Rhs.getStructDecl() &&
+           "Incompatible structs");
+    // Use the new layout for reordered struct.
+    if (Struct.Definition == Lhs.getStructDecl()) {
+      return Struct.NewFieldsPositions[Lhs.getStructIter()->getFieldIndex()] -
+             Struct.NewFieldsPositions[Rhs.getStructIter()->getFieldIndex()];
+    }
+    return Lhs.getStructIter()->getFieldIndex() -
+           Rhs.getStructIter()->getFieldIndex();
+  }
+  case DesignatorIter::ARRAY:
+    return Lhs.getArrayIndex() - Rhs.getArrayIndex();
+  case DesignatorIter::ARRAY_RANGE:
+    return Lhs.getArrayRangeEnd() - Rhs.getArrayRangeEnd();
+  }
+  llvm_unreachable("Invalid designator tag");
+}
+
+/// Compares compatible designator lists according to the new struct order.
+/// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0 if
+/// they are equal.
+static int cmpDesignatorLists(const Designators &Lhs, const Designators &Rhs,
+                              const ReorderedStruct &Reorders) {
+  for (unsigned Idx = 0, Size = std::min(Lhs.size(), Rhs.size()); Idx < Size;
+       ++Idx) {
+    int DesignatorComp = cmpDesignators(Lhs[Idx], Rhs[Idx], Reorders);
+    // If the current designators are not equal, return the result
+    if (DesignatorComp != 0)
+      return DesignatorComp;
+    // Otherwise, continue to the next pair.
+  }
+  //
+  return Lhs.size() - Rhs.size();
+}
+
+/// Finds the semantic form of the first explicit ancestor of the given
+/// initializer list including itself.
+static const InitListExpr *getExplicitILE(const InitListExpr *ILE,
+                                          ASTContext &Context) {
+  if (!isImplicitILE(ILE, Context))
+    return ILE;
+  const InitListExpr *TopLevelILE = ILE;
+  DynTypedNodeList Parents = Context.getParents(*TopLevelILE);
+  while (!Parents.empty() && Parents.begin()->get<InitListExpr>()) {
+    TopLevelILE = Parents.begin()->get<InitListExpr>();
+    Parents = Context.getParents(*TopLevelILE);
+    if (!isImplicitILE(TopLevelILE, Context))
+      break;
+  }
+  if (!TopLevelILE->isSemanticForm()) {
+    return TopLevelILE->getSemanticForm();
+  }
+  return TopLevelILE;
+}
+
 /// 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,
+    const InitListExpr *InitListEx, const ReorderedStruct &RS,
+    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())
+  // Only process semantic forms of initializer lists.
+  if (!InitListEx->isSemanticForm()) {
     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;
+
+  // We care only about InitListExprs which originate from source code.
+  // Implicit InitListExprs are created by the semantic analyzer.
+  // We find the first parent InitListExpr that exists in source code and
+  // process it. This is necessary because of designated initializer lists and
+  // possible omitted braces.
+  InitListEx = getExplicitILE(InitListEx, Context);
+
+  // Find if there are any designated initializations or implicit values. If 
all
+  // initializers are present and none have designators then just reorder them
+  // normally. Otherwise, designators are added to all initializers and they 
are
+  // sorted in the new order.
+  bool ShouldAddDesignators = false;
+  // The method InitListExpr::getSyntacticForm may return nullptr indicating
+  // that the current initializer list also serves as its syntactic form.
+  const InitListExpr *SyntacticInitListEx = InitListEx;
+  if (const InitListExpr *SynILE = InitListEx->getSyntacticForm()) {
+    // Do not rewrite zero initializers. This check is only valid for syntactic
+    // forms.
+    if (SynILE->isIdiomaticZeroInitializer(Context.getLangOpts()))
+      return true;
+
+    ShouldAddDesignators = InitListEx->getNumInits() != SynILE->getNumInits() 
||
+                           llvm::any_of(SynILE->inits(), [](const Expr *Init) {
+                             return isa<DesignatedInitExpr>(Init);
+                           });
+
+    SyntacticInitListEx = SynILE;
+  } else {
+    // If there is no syntactic form, there can be no designators. Instead,
+    // there might be implicit values.
+    ShouldAddDesignators =
+        (RS.NewFieldsOrder.size() != InitListEx->getNumInits()) ||
+        llvm::any_of(InitListEx->inits(), [&Context](const Expr *Init) {
+          return isa<ImplicitValueInitExpr>(Init) ||
+                 (isa<InitListExpr>(Init) &&
+                  isImplicitILE(dyn_cast<InitListExpr>(Init), Context));
+        });
+  }
+
+  if (ShouldAddDesignators) {
+    // Designators are only supported from C++20.
+    if (Context.getLangOpts().CPlusPlus && !Context.getLangOpts().CPlusPlus20) 
{
+      llvm::errs()
+          << "Currently only full initialization is supported for C++\n";
+      return false;
+    }
+
+    // Handle case when some fields are designated. Some fields can be
+    // missing. Insert any missing designators and reorder the expressions
+    // according to the new order.
+    Designators CurrentDesignators{};
+    // Remember each initializer expression along with its designators. They 
are
+    // sorted later to determine the correct order.
+    std::vector<std::pair<Designators, const Expr *>> Rewrites;
+    for (const Expr *Init : SyntacticInitListEx->inits()) {
+      if (const auto *DIE = dyn_cast_or_null<DesignatedInitExpr>(Init)) {
+        CurrentDesignators = {DIE, SyntacticInitListEx, Context};
+
+        // Use the child of the DesignatedInitExpr. This way designators are
+        // always replaced.
+        Rewrites.push_back({CurrentDesignators, DIE->getInit()});
+      } else {
+        // Find the next field.
+        if (!CurrentDesignators.increment(SyntacticInitListEx, Init, Context)) 
{
+          llvm::errs() << "Unsupported initializer list\n";
+          return false;
+        }
+
+        // Do not rewrite implicit values. They just had to be processed to
+        // find the correct designator.
+        if (!isa<ImplicitValueInitExpr>(Init))
+          Rewrites.push_back({CurrentDesignators, Init});
+      }
+    }
+
+    // Sort the designators according to the new order.
+    llvm::sort(Rewrites, [&RS](const auto &Lhs, const auto &Rhs) {
+      return cmpDesignatorLists(Lhs.first, Rhs.first, RS) < 0;
+    });
+
+    for (unsigned i = 0, e = Rewrites.size(); i < e; ++i) {
+      addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),
+                     Rewrites[i].second->getSourceRange(),
+                     Rewrites[i].first.toString(), Context, Replacements);
+    }
+  } else {
+    // Handle excess initializers by leaving them unchanged.
+    assert(SyntacticInitListEx->getNumInits() >= InitListEx->getNumInits());
+
+    // All field initializers are present and none have designators. They can 
be
+    // reordered normally.
+    for (unsigned i = 0, e = RS.NewFieldsOrder.size(); i < e; ++i) {
+      if (i != RS.NewFieldsOrder[i])
+        addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),
+                       SyntacticInitListEx->getInit(RS.NewFieldsOrder[i])
+                           ->getSourceRange(),
+                       Context, Replacements);
+    }
   }
-  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;
 }
 
@@ -345,7 +547,9 @@ class ReorderingConsumer : public ASTConsumer {
         getNewFieldsOrder(RD, DesiredFieldsOrder);
     if (NewFieldsOrder.empty())
       return;
-    if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
+    ReorderedStruct RS{RD, NewFieldsOrder};
+
+    if (!reorderFieldsInDefinition(RS, Context, Replacements))
       return;
 
     // CXXRD will be nullptr if C code (not C++) is being processed.
@@ -353,24 +557,25 @@ class ReorderingConsumer : public ASTConsumer {
     if (CXXRD)
       for (const auto *C : CXXRD->ctors())
         if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
-          reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
-                                     NewFieldsOrder, Context, Replacements);
+          reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D), RS,
+                                     Context, Replacements);
 
     // We only need to reorder init list expressions for
     // plain C structs or C++ 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 (!CXXRD || CXXRD->isAggregate())
+    if (!CXXRD || CXXRD->isAggregate()) {
       for (auto Result :
            match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
                  Context))
         if (!reorderFieldsInInitListExpr(
-                Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
-                Context, Replacements)) {
+                Result.getNodeAs<InitListExpr>("initListExpr"), RS, Context,
+                Replacements)) {
           Replacements.clear();
           return;
         }
+    }
   }
 };
 } // end anonymous namespace
diff --git a/clang-tools-extra/clang-reorder-fields/utils/Designator.cpp 
b/clang-tools-extra/clang-reorder-fields/utils/Designator.cpp
new file mode 100644
index 0000000000000..9ad60a3fc5db6
--- /dev/null
+++ b/clang-tools-extra/clang-reorder-fields/utils/Designator.cpp
@@ -0,0 +1,256 @@
+//===-- 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 DesignatorIter and Designators
+/// utility classes.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Designator.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+
+namespace clang {
+namespace reorder_fields {
+
+DesignatorIter::DesignatorIter(const QualType Type,
+                               RecordDecl::field_iterator Field,
+                               const RecordDecl *RD)
+    : Tag(STRUCT), Type(Type), StructIt({Field, RD}) {}
+
+DesignatorIter::DesignatorIter(const QualType Type, uint64_t Idx, uint64_t 
Size)
+    : Tag(ARRAY), Type(Type), ArrayIt({Idx, Size}) {}
+
+DesignatorIter::DesignatorIter(const QualType Type, uint64_t Start,
+                               uint64_t End, uint64_t Size)
+    : Tag(ARRAY_RANGE), Type(Type), ArrayRangeIt({Start, End, Size}) {}
+
+DesignatorIter &DesignatorIter::operator++() {
+  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;
+  }
+  return *this;
+}
+
+bool DesignatorIter::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;
+}
+
+DesignatorIter::Kind DesignatorIter::getTag() const { return Tag; }
+
+QualType DesignatorIter::getType() const { return Type; }
+
+RecordDecl::field_iterator &DesignatorIter::getStructIter() {
+  assert(Tag == STRUCT && "Must be a field designator");
+  return StructIt.Field;
+}
+
+RecordDecl::field_iterator DesignatorIter::getStructIter() const {
+  assert(Tag == STRUCT && "Must be a field designator");
+  return StructIt.Field;
+}
+
+const RecordDecl *DesignatorIter::getStructDecl() const {
+  assert(Tag == STRUCT && "Must be a field designator");
+  return StructIt.Record;
+}
+
+uint64_t &DesignatorIter::getArrayIndex() {
+  assert(Tag == ARRAY && "Must be an array designator");
+  return ArrayIt.Index;
+}
+
+uint64_t DesignatorIter::getArrayIndex() const {
+  assert(Tag == ARRAY && "Must be an array designator");
+  return ArrayIt.Index;
+}
+
+uint64_t DesignatorIter::getArrayRangeStart() const {
+  assert(Tag == ARRAY_RANGE && "Must be an array range designator");
+  return ArrayRangeIt.Start;
+}
+
+uint64_t DesignatorIter::getArrayRangeEnd() const {
+  assert(Tag == ARRAY_RANGE && "Must be an array range designator");
+  return ArrayRangeIt.End;
+}
+
+uint64_t DesignatorIter::getArraySize() const {
+  assert((Tag == ARRAY || Tag == ARRAY_RANGE) &&
+         "Must be an array or range designator");
+  return ArrayIt.Size;
+}
+
+Designators::Designators(const DesignatedInitExpr *DIE, const InitListExpr 
*ILE,
+                         const ASTContext &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::increment(const InitListExpr *ILE, const Expr *Init,
+                            const ASTContext &Context) {
+  if (DesignatorList.empty()) {
+    // First field is not designated. Initialize to the first field or
+    // array index.
+    if (ILE->getType()->isArrayType()) {
+      const ConstantArrayType *CAT =
+          Context.getAsConstantArrayType(ILE->getType());
+      // Only constant size arrays are supported.
+      if (!CAT) {
+        DesignatorList.clear();
+        return false;
+      }
+      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});
+    }
+  } else {
+    while (!DesignatorList.empty()) {
+      auto &CurrentDesignator = DesignatorList.back();
+      ++CurrentDesignator;
+      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;
+
+  // Check for missing braces. If the types don't match then there are
+  // missing braces.
+  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 = "";
+  for (auto &I : DesignatorList) {
+    switch (I.getTag()) {
+    case DesignatorIter::STRUCT:
+      Designator += "." + I.getStructIter()->getName().str();
+      break;
+    case DesignatorIter::ARRAY:
+      Designator += "[" + std::to_string(I.getArrayIndex()) + "]";
+      break;
+    case DesignatorIter::ARRAY_RANGE:
+      Designator += "[" + std::to_string(I.getArrayRangeStart()) + "..." +
+                    std::to_string(I.getArrayRangeEnd()) + "]";
+    }
+  }
+  Designator += " = ";
+  return Designator;
+}
+
+} // namespace reorder_fields
+} // namespace clang
diff --git a/clang-tools-extra/clang-reorder-fields/utils/Designator.h 
b/clang-tools-extra/clang-reorder-fields/utils/Designator.h
new file mode 100644
index 0000000000000..173b699ad3e9a
--- /dev/null
+++ b/clang-tools-extra/clang-reorder-fields/utils/Designator.h
@@ -0,0 +1,118 @@
+//===-- 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 DesignatorIter and Designators
+/// utility class.
+///
+//===----------------------------------------------------------------------===//
+
+#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 {
+
+class DesignatorIter {
+public:
+  enum Kind { STRUCT, ARRAY, ARRAY_RANGE };
+
+  DesignatorIter(const QualType Type, RecordDecl::field_iterator Field,
+                 const RecordDecl *RD);
+
+  DesignatorIter(const QualType Type, uint64_t Idx, uint64_t Size);
+
+  DesignatorIter(const QualType Type, uint64_t Start, uint64_t End,
+                 uint64_t Size);
+
+  /// Moves the iterator to the next element.
+  DesignatorIter &operator++();
+
+  /// Checks if the iterator has iterated through all elements.
+  bool isFinished();
+
+  Kind getTag() const;
+  QualType getType() const;
+
+  RecordDecl::field_iterator &getStructIter();
+  RecordDecl::field_iterator getStructIter() const;
+  const RecordDecl *getStructDecl() const;
+  uint64_t &getArrayIndex();
+  uint64_t getArrayIndex() const;
+  uint64_t getArrayRangeStart() const;
+  uint64_t getArrayRangeEnd() const;
+  uint64_t getArraySize() const;
+
+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;
+  };
+};
+
+class Designators {
+public:
+  Designators() = default;
+  Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE,
+              const ASTContext &Context);
+
+  /// 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 increment(const InitListExpr *ILE, const Expr *Init,
+                 const ASTContext &Context);
+
+  /// 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(); }
+
+  const DesignatorIter &operator[](unsigned Idx) const {
+    return DesignatorList[Idx];
+  }
+
+private:
+  SmallVector<DesignatorIter, 1> DesignatorList;
+};
+
+} // namespace reorder_fields
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H
diff --git 
a/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c 
b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c
new file mode 100644
index 0000000000000..c05c296d1b47b
--- /dev/null
+++ b/clang-tools-extra/test/clang-reorder-fields/DesignatedInitializerList.c
@@ -0,0 +1,31 @@
+// RUN: clang-reorder-fields -record-name Foo -fields-order z,w,y,x %s -- | 
FileCheck %s
+
+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}}
+};
+
+struct Bar {
+  char a;
+  struct Foo b;
+  char c;
+};
+
+int main() {
+  const int x = 13;
+  struct Foo foo1 = { .x=&x, .y=0, .z=1.29, .w=17 }; // CHECK: {{^ struct Foo 
foo1 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}}
+  struct Foo foo2 = { .x=&x, 0, 1.29, 17 };          // CHECK: {{^ struct Foo 
foo2 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}}
+  struct Foo foo3 = { .y=0, .z=1.29, 17, .x=&x };    // CHECK: {{^ struct Foo 
foo3 = { .z = 1.29, .w = 17, .y = 0, .x = &x };}}
+  struct Foo foo4 = { .y=0, .z=1.29, 17 };           // CHECK: {{^ struct Foo 
foo4 = { .z = 1.29, .w = 17, .y = 0 };}}
+
+  struct Foo foos1[1] = { [0] = {.x=&x, 0, 1.29, 17} };              // CHECK: 
{{^ struct Foo foos1\[1] = { \[0] = {.z = 1.29, .w = 17, .y = 0, .x = &x} };}}
+  struct Foo foos2[1] = { [0].x=&x, [0].y=0, [0].z=1.29, [0].w=17 }; // CHECK: 
{{^ struct Foo foos2\[1] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = 
&x };}}
+  struct Foo foos3[1] = { &x, 0, 1.29, 17 };                         // CHECK: 
{{^ struct Foo foos3\[1] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = 
&x };}}
+  struct Foo foos4[2] = { &x, 0, 1.29, 17, &x, 0, 1.29, 17 };        // CHECK: 
{{^ struct Foo foos4\[2] = { \[0].z = 1.29, \[0].w = 17, \[0].y = 0, \[0].x = 
&x, \[1].z = 1.29, \[1].w = 17, \[1].y = 0, \[1].x = &x };}}
+
+  struct Bar bar1 = { .a='a', &x, 0, 1.29, 17, 'c' }; // CHECK: {{^ struct Bar 
bar1 = { .a = 'a', .b.z = 1.29, .b.w = 17, .b.y = 0, .b.x = &x, .c = 'c' };}}
+
+  return 0;
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to