summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaChecking.cpp
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2017-03-14 18:01:02 +0000
committerReid Kleckner <rnk@google.com>2017-03-14 18:01:02 +0000
commit329f24d6f6e733fcadfd1be7cd3b430d63047c2e (patch)
tree9bde4962de281b4d5a74e2b509d27849a94ad56e /clang/lib/Sema/SemaChecking.cpp
parentca1fab5d6664e46add1ae4e6139f0efa5f54d9c7 (diff)
downloadbcm5719-llvm-329f24d6f6e733fcadfd1be7cd3b430d63047c2e.tar.gz
bcm5719-llvm-329f24d6f6e733fcadfd1be7cd3b430d63047c2e.zip
Warn on enum assignment to bitfields that can't fit all values
This adds -Wbitfield-enum-conversion, which warns on implicit conversions that happen on bitfield assignment that change the value of some enumerators. Values of enum type typically take on a very small range of values, so they are frequently stored in bitfields. Unfortunately, there is no convenient way to calculate the minimum number of bits necessary to store all possible values at compile time, so users usually hard code a bitwidth that works today and widen it as necessary to pass basic testing and validation. This is very error-prone, and leads to stale widths as enums grow. This warning aims to catch such bugs. This would have found two real bugs in clang and two instances of questionable code. See r297680 and r297654 for the full description of the issues. This warning is currently disabled by default while we investigate its usefulness outside of LLVM. The major cause of false positives with this warning is this kind of enum: enum E { W, X, Y, Z, SENTINEL_LAST }; The last enumerator is an invalid value used to validate inputs or size an array. Depending on the prevalance of this style of enum across a codebase, this warning may be more or less feasible to deploy. It also has trouble on sentinel values such as ~0U. Reviewers: rsmith, rtrieu, thakis Reviewed By: thakis Subscribers: hfinkel, voskresensky.vladimir, sashab, cfe-commits Differential Revision: https://reviews.llvm.org/D30923 llvm-svn: 297761
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r--clang/lib/Sema/SemaChecking.cpp57
1 files changed, 55 insertions, 2 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 9858244d569..0e5d519c8f8 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8729,13 +8729,66 @@ bool AnalyzeBitFieldAssignment(Sema &S, FieldDecl *Bitfield, Expr *Init,
return false;
Expr *OriginalInit = Init->IgnoreParenImpCasts();
+ unsigned FieldWidth = Bitfield->getBitWidthValue(S.Context);
llvm::APSInt Value;
- if (!OriginalInit->EvaluateAsInt(Value, S.Context, Expr::SE_AllowSideEffects))
+ if (!OriginalInit->EvaluateAsInt(Value, S.Context,
+ Expr::SE_AllowSideEffects)) {
+ // 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>()) {
+ EnumDecl *ED = EnumTy->getDecl();
+ bool SignedBitfield = BitfieldType->isSignedIntegerType();
+
+ // 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
+ // not.
+ bool SignedEnum = ED->getNumNegativeBits() > 0;
+
+ // Check for surprising sign changes when assigning enum values to a
+ // bitfield of different signedness. If the bitfield is signed and we
+ // have exactly the right number of bits to store this unsigned enum,
+ // suggest changing the enum to an unsigned type. This typically happens
+ // 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;
+ } else if (SignedBitfield && !SignedEnum &&
+ ED->getNumPositiveBits() == FieldWidth) {
+ DiagID = diag::warn_signed_bitfield_enum_conversion;
+ }
+
+ 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;
+ }
+
+ // Compute the required bitwidth. If the enum has negative values, we need
+ // one more bit than the normal number of positive bits to represent the
+ // sign bit.
+ unsigned BitsNeeded = SignedEnum ? std::max(ED->getNumPositiveBits() + 1,
+ ED->getNumNegativeBits())
+ : ED->getNumPositiveBits();
+
+ // Check the bitwidth.
+ if (BitsNeeded > FieldWidth) {
+ Expr *WidthExpr = Bitfield->getBitWidth();
+ S.Diag(InitLoc, diag::warn_bitfield_too_small_for_enum)
+ << Bitfield << ED;
+ S.Diag(WidthExpr->getExprLoc(), diag::note_widen_bitfield)
+ << BitsNeeded << ED << WidthExpr->getSourceRange();
+ }
+ }
+
return false;
+ }
unsigned OriginalWidth = Value.getBitWidth();
- unsigned FieldWidth = Bitfield->getBitWidthValue(S.Context);
if (!Value.isSigned() || Value.isNegative())
if (UnaryOperator *UO = dyn_cast<UnaryOperator>(OriginalInit))
OpenPOWER on IntegriCloud