@@ -11356,12 +11356,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11356
11356
ImplicitMatch == ArgType::NoMatchTypeConfusion)
11357
11357
Match = ImplicitMatch;
11358
11358
assert(Match != ArgType::MatchPromotion);
11359
+
11359
11360
// Look through unscoped enums to their underlying type.
11360
11361
bool IsEnum = false;
11361
11362
bool IsScopedEnum = false;
11363
+ QualType IntendedTy = ExprTy;
11362
11364
if (auto EnumTy = ExprTy->getAs<EnumType>()) {
11365
+ IntendedTy = EnumTy->getDecl()->getIntegerType();
11363
11366
if (EnumTy->isUnscopedEnumerationType()) {
11364
- ExprTy = EnumTy->getDecl()->getIntegerType() ;
11367
+ ExprTy = IntendedTy ;
11365
11368
// This controls whether we're talking about the underlying type or not,
11366
11369
// which we only want to do when it's an unscoped enum.
11367
11370
IsEnum = true;
@@ -11373,7 +11376,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11373
11376
// %C in an Objective-C context prints a unichar, not a wchar_t.
11374
11377
// If the argument is an integer of some kind, believe the %C and suggest
11375
11378
// a cast instead of changing the conversion specifier.
11376
- QualType IntendedTy = ExprTy;
11377
11379
if (isObjCContext() &&
11378
11380
FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
11379
11381
if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
@@ -11409,8 +11411,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11409
11411
std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);
11410
11412
if (!CastTy.isNull()) {
11411
11413
// %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int
11412
- // (long in ASTContext). Only complain to pedants.
11413
- if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11414
+ // (long in ASTContext). Only complain to pedants or when they're the
11415
+ // underlying type of a scoped enum (which always needs a cast).
11416
+ if (!IsScopedEnum &&
11417
+ (CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11414
11418
(AT.isSizeT() || AT.isPtrdiffT()) &&
11415
11419
AT.matchesType(S.Context, CastTy))
11416
11420
Match = ArgType::NoMatchPedantic;
@@ -11465,20 +11469,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11465
11469
// should be printed as 'long' for 64-bit compatibility.)
11466
11470
// Rather than emitting a normal format/argument mismatch, we want to
11467
11471
// add a cast to the recommended type (and correct the format string
11468
- // if necessary).
11472
+ // if necessary). We should also do so for scoped enumerations.
11469
11473
SmallString<16> CastBuf;
11470
11474
llvm::raw_svector_ostream CastFix(CastBuf);
11471
11475
CastFix << (S.LangOpts.CPlusPlus ? "static_cast<" : "(");
11472
- if (IsScopedEnum) {
11473
- CastFix << AT.getRepresentativeType(S.Context).getAsString(
11474
- S.Context.getPrintingPolicy());
11475
- } else {
11476
- IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11477
- }
11476
+ IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11478
11477
CastFix << (S.LangOpts.CPlusPlus ? ">" : ")");
11479
11478
11480
11479
SmallVector<FixItHint,4> Hints;
11481
- if ((! AT.matchesType(S.Context, IntendedTy) && !IsScopedEnum) ||
11480
+ if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match ||
11482
11481
ShouldNotPrintDirectly)
11483
11482
Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str()));
11484
11483
@@ -11506,7 +11505,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11506
11505
Hints.push_back(FixItHint::CreateInsertion(After, ")"));
11507
11506
}
11508
11507
11509
- if (ShouldNotPrintDirectly) {
11508
+ if (ShouldNotPrintDirectly && !IsScopedEnum ) {
11510
11509
// The expression has a type that should not be printed directly.
11511
11510
// We extract the name from the typedef because we don't want to show
11512
11511
// the underlying type in the diagnostic.
0 commit comments