Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,9 @@ Improvements to Clang's diagnostics
carries messages like 'In file included from ...' or 'In module ...'.
Now the include/import locations are written into `sarif.run.result.relatedLocations`.

- Clang now generates a fix-it for C++20 designated initializers when the
initializers do not match the declaration order in the structure.

Improvements to Clang's time-trace
----------------------------------

Expand Down
68 changes: 65 additions & 3 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaObjC.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -3092,6 +3094,65 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
PrevField = *FI;
}

const auto GenerateDesignatedInitReorderingFixit =
[&](SemaBase::SemaDiagnosticBuilder &Diags) {
struct ReorderInfo {
int Pos{};
const Expr *InitExpr{};
};

llvm::SmallDenseMap<IdentifierInfo *, int> MemberNameInx{};
llvm::SmallVector<ReorderInfo, 16> ReorderedInitExprs{};

const auto *CxxRecord =
IList->getSemanticForm()->getType()->getAsCXXRecordDecl();

for (const auto &Field : CxxRecord->fields()) {
MemberNameInx[Field->getIdentifier()] = Field->getFieldIndex();
}

for (const auto *Init : IList->inits()) {
if (const auto *DI =
dyn_cast_if_present<DesignatedInitExpr>(Init)) {
// We expect only one Designator
if (DI->size() != 1)
return;

const auto *const FieldName =
DI->getDesignator(0)->getFieldName();
// In case we have an unknown initializer in the source, not in
// the record
if (MemberNameInx.contains(FieldName))
ReorderedInitExprs.emplace_back(
ReorderInfo{MemberNameInx.at(FieldName), Init});
}
}

llvm::sort(ReorderedInitExprs, [](const auto &A, const auto &B) {
return A.Pos < B.Pos;
});

llvm::SmallString<128> FixedInitList{};
SourceManager &SM = SemaRef.getSourceManager();
const LangOptions &LangOpts = SemaRef.getLangOpts();

// In a derived Record, first n base-classes are initialized first.
// They do not use designated init, so skip them
const auto IListInits =
IList->inits().drop_front(CxxRecord->getNumBases());
// loop over each existing expressions and apply replacement
for (const auto &[OrigExpr, Repl] :
llvm::zip(IListInits, ReorderedInitExprs)) {
CharSourceRange CharRange = CharSourceRange::getTokenRange(
Repl.InitExpr->getSourceRange());
const auto InitText =
Lexer::getSourceText(CharRange, SM, LangOpts);

Diags << FixItHint::CreateReplacement(OrigExpr->getSourceRange(),
InitText.str());
}
};

if (PrevField &&
PrevField->getFieldIndex() > KnownField->getFieldIndex()) {
SemaRef.Diag(DIE->getInit()->getBeginLoc(),
Expand All @@ -3101,9 +3162,10 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity,
unsigned OldIndex = StructuredIndex - 1;
if (StructuredList && OldIndex <= StructuredList->getNumInits()) {
if (Expr *PrevInit = StructuredList->getInit(OldIndex)) {
SemaRef.Diag(PrevInit->getBeginLoc(),
diag::note_previous_field_init)
<< PrevField << PrevInit->getSourceRange();
auto Diags = SemaRef.Diag(PrevInit->getBeginLoc(),
diag::note_previous_field_init)
<< PrevField << PrevInit->getSourceRange();
GenerateDesignatedInitReorderingFixit(Diags);
}
}
}
Expand Down
105 changes: 105 additions & 0 deletions clang/test/SemaCXX/cxx20-designated-initializer-fixits.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// RUN: %clang_cc1 -std=c++20 %s -Wreorder-init-list -fdiagnostics-parseable-fixits 2>&1 | FileCheck %s

// These tests are from clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp
struct C { int :0, x, :0, y, :0; };
C c = {
.x = 1,
.x = 1,
.y = 1,
.y = 1,
.x = 1,
.x = 1,
};
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".x = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".x = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".x = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".x = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".y = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:9}:".y = 1"

namespace GH63605 {
struct A {
unsigned x;
unsigned y;
unsigned z;
};

struct B {
unsigned a;
unsigned b;
};

struct : public A, public B {
unsigned : 2;
unsigned a : 6;
unsigned : 1;
unsigned b : 6;
unsigned : 2;
unsigned c : 6;
unsigned d : 1;
unsigned e : 2;
} data = {
{.z=0,


.y=1,

.x=2},
{.b=3,
.a=4},
.e = 1,

.d = 1,

.c = 1,
.b = 1,
.a = 1,
};
}
// CHECK: fix-it:"{{.*}}":{[[@LINE-17]]:4-[[@LINE-17]]:8}:".x=2"
// CHECK: fix-it:"{{.*}}":{[[@LINE-15]]:4-[[@LINE-15]]:8}:".y=1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-14]]:4-[[@LINE-14]]:8}:".z=0"
// CHECK: fix-it:"{{.*}}":{[[@LINE-14]]:4-[[@LINE-14]]:8}:".a=4"
// CHECK: fix-it:"{{.*}}":{[[@LINE-14]]:4-[[@LINE-14]]:8}:".b=3"
// CHECK: fix-it:"{{.*}}":{[[@LINE-14]]:5-[[@LINE-14]]:11}:".a = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-13]]:5-[[@LINE-13]]:11}:".b = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-12]]:5-[[@LINE-12]]:11}:".c = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-12]]:5-[[@LINE-12]]:11}:".d = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-12]]:5-[[@LINE-12]]:11}:".e = 1"
// END tests from clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp

namespace reorder_derived {
struct col {
int r;
int g;
int b;
};

struct point {
float x;
float y;
float z;
};

struct derived : public col, public point {
int z2;
int z1;
};

void test() {
derived a {
{.b = 1, .g = 2, .r = 3},
{ .z = 1, .y=2, .x = 3 },
.z1 = 1,
.z2 = 2,
};
}
// CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:12}:".r = 3"
// CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:14-[[@LINE-7]]:20}:".g = 2"
// CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:22-[[@LINE-8]]:28}:".b = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:15-[[@LINE-8]]:19}:".y=2"
// CHECK: fix-it:"{{.*}}":{[[@LINE-9]]:21-[[@LINE-9]]:28}:".z = 1"
// CHECK: fix-it:"{{.*}}":{[[@LINE-10]]:7-[[@LINE-10]]:13}:".x = 3"
// CHECK: fix-it:"{{.*}}":{[[@LINE-10]]:5-[[@LINE-10]]:12}:".z2 = 2"
// CHECK: fix-it:"{{.*}}":{[[@LINE-10]]:5-[[@LINE-10]]:12}:".z1 = 1"
} // namespace reorder_derived