Skip to content

[Clang] Consider preferred_type in bitfield warnings (#116760) #116785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Apr 20, 2025
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
8 changes: 8 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ Improvements to Clang's diagnostics
- Now correctly diagnose a tentative definition of an array with static
storage duration in pedantic mode in C. (#GH50661)

- Improved bit-field diagnostics to consider the type specified by the
``preferred_type`` attribute. These diagnostics are controlled by the flags
``-Wpreferred-type-bitfield-enum-conversion`` and
``-Wpreferred-type-bitfield-width``. These warnings are on by default as they
they're only triggered if the authors are already making the choice to use
``preferred_type`` attribute.


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

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ def SingleBitBitFieldConstantConversion :
DiagGroup<"single-bit-bitfield-constant-conversion">;
def BitFieldConstantConversion : DiagGroup<"bitfield-constant-conversion",
[SingleBitBitFieldConstantConversion]>;
def PreferredTypeBitFieldEnumConversion
: DiagGroup<"preferred-type-bitfield-enum-conversion">;
def PreferredTypeBitFieldWidth : DiagGroup<"preferred-type-bitfield-width">;
def BitFieldEnumConversion : DiagGroup<"bitfield-enum-conversion">;
def BitFieldWidth : DiagGroup<"bitfield-width">;
def CompoundTokenSplitByMacro : DiagGroup<"compound-token-split-by-macro">;
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6466,8 +6466,25 @@ def warn_signed_bitfield_enum_conversion : Warning<
"signed bit-field %0 needs an extra bit to represent the largest positive "
"enumerators of %1">,
InGroup<BitFieldEnumConversion>, DefaultIgnore;
def warn_preferred_type_bitfield_too_small_for_enum
: Warning<"bit-field %0 is not wide enough to store all enumerators of "
"preferred type %1">,
InGroup<PreferredTypeBitFieldEnumConversion>;
def warn_preferred_type_unsigned_bitfield_assigned_signed_enum
: Warning<"assigning value of preferred signed enum type %1 to unsigned "
"bit-field %0; "
"negative enumerators of enum %1 will be converted to positive "
"values">,
InGroup<PreferredTypeBitFieldEnumConversion>;
def warn_preferred_type_signed_bitfield_enum_conversion
: Warning<"signed bit-field %0 needs an extra bit to represent the largest "
"positive "
"enumerators of preferred type %1">,
InGroup<PreferredTypeBitFieldEnumConversion>;
def note_change_bitfield_sign : Note<
"consider making the bit-field type %select{unsigned|signed}0">;
def note_bitfield_preferred_type
: Note<"preferred type for bit-field %0 specified here">;

def warn_missing_braces : Warning<
"suggest braces around initialization of subobject">,
Expand Down
36 changes: 29 additions & 7 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11234,9 +11234,16 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
// The RHS is not constant. If the RHS has an enum type, make sure the
// bitfield is wide enough to hold all the values of the enum without
// truncation.
if (const auto *EnumTy = OriginalInit->getType()->getAs<EnumType>()) {
const auto *EnumTy = OriginalInit->getType()->getAs<EnumType>();
const PreferredTypeAttr *PTAttr = nullptr;
if (!EnumTy) {
PTAttr = Bitfield->getAttr<PreferredTypeAttr>();
if (PTAttr)
EnumTy = PTAttr->getType()->getAs<EnumType>();
}
if (EnumTy) {
EnumDecl *ED = EnumTy->getDecl();
bool SignedBitfield = BitfieldType->isSignedIntegerType();
bool SignedBitfield = BitfieldType->isSignedIntegerOrEnumerationType();

// Enum types are implicitly signed on Windows, so check if there are any
// negative enumerators to see if the enum was intended to be signed or
Expand All @@ -11250,19 +11257,28 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
// on Windows where unfixed enums always use an underlying type of 'int'.
unsigned DiagID = 0;
if (SignedEnum && !SignedBitfield) {
DiagID = diag::warn_unsigned_bitfield_assigned_signed_enum;
DiagID =
PTAttr == nullptr
? diag::warn_unsigned_bitfield_assigned_signed_enum
: diag::
warn_preferred_type_unsigned_bitfield_assigned_signed_enum;
} else if (SignedBitfield && !SignedEnum &&
ED->getNumPositiveBits() == FieldWidth) {
DiagID = diag::warn_signed_bitfield_enum_conversion;
DiagID =
PTAttr == nullptr
? diag::warn_signed_bitfield_enum_conversion
: diag::warn_preferred_type_signed_bitfield_enum_conversion;
Comment on lines +11260 to +11270
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AaronBallman @cor3ntin @erichkeane I was tempted to do this as a set of defs like:

unsigned assigned_signed_enum_diag[] = {diag::warn_unsigned_bitfield_assigned_signed_enum, 
                                                                          diag::warn_preferred_type_unsigned_bitfield_assigned_signed_enum};
...
DiagID = assigned_signed_enum_diag[PTAttr != nullptr];

But that seemed like while being more concise it might be confusing, and potentially error prone?

}

if (DiagID) {
S.Diag(InitLoc, DiagID) << Bitfield << ED;
TypeSourceInfo *TSI = Bitfield->getTypeSourceInfo();
SourceRange TypeRange =
TSI ? TSI->getTypeLoc().getSourceRange() : SourceRange();
S.Diag(Bitfield->getTypeSpecStartLoc(), diag::note_change_bitfield_sign)
<< SignedEnum << TypeRange;
if (PTAttr)
S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
<< ED;
}

// Compute the required bitwidth. If the enum has negative values, we need
Expand All @@ -11275,10 +11291,16 @@ static bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
// Check the bitwidth.
if (BitsNeeded > FieldWidth) {
Expr *WidthExpr = Bitfield->getBitWidth();
S.Diag(InitLoc, diag::warn_bitfield_too_small_for_enum)
<< Bitfield << ED;
auto DiagID =
PTAttr == nullptr
? diag::warn_bitfield_too_small_for_enum
: diag::warn_preferred_type_bitfield_too_small_for_enum;
S.Diag(InitLoc, DiagID) << Bitfield << ED;
S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
<< BitsNeeded << ED << WidthExpr->getSourceRange();
if (PTAttr)
S.Diag(PTAttr->getLocation(), diag::note_bitfield_preferred_type)
<< ED;
}
}

Expand Down
Loading