@@ -11478,12 +11478,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11478
11478
ImplicitMatch == ArgType::NoMatchTypeConfusion)
11479
11479
Match = ImplicitMatch;
11480
11480
assert(Match != ArgType::MatchPromotion);
11481
+
11481
11482
// Look through unscoped enums to their underlying type.
11482
11483
bool IsEnum = false;
11483
11484
bool IsScopedEnum = false;
11485
+ QualType IntendedTy = ExprTy;
11484
11486
if (auto EnumTy = ExprTy->getAs<EnumType>()) {
11487
+ IntendedTy = EnumTy->getDecl()->getIntegerType();
11485
11488
if (EnumTy->isUnscopedEnumerationType()) {
11486
- ExprTy = EnumTy->getDecl()->getIntegerType() ;
11489
+ ExprTy = IntendedTy ;
11487
11490
// This controls whether we're talking about the underlying type or not,
11488
11491
// which we only want to do when it's an unscoped enum.
11489
11492
IsEnum = true;
@@ -11495,7 +11498,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11495
11498
// %C in an Objective-C context prints a unichar, not a wchar_t.
11496
11499
// If the argument is an integer of some kind, believe the %C and suggest
11497
11500
// a cast instead of changing the conversion specifier.
11498
- QualType IntendedTy = ExprTy;
11499
11501
if (isObjCContext() &&
11500
11502
FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
11501
11503
if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
@@ -11531,8 +11533,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11531
11533
std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);
11532
11534
if (!CastTy.isNull()) {
11533
11535
// %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int
11534
- // (long in ASTContext). Only complain to pedants.
11535
- if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11536
+ // (long in ASTContext). Only complain to pedants or when they're the
11537
+ // underlying type of a scoped enum (which always needs a cast).
11538
+ if (!IsScopedEnum &&
11539
+ (CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11536
11540
(AT.isSizeT() || AT.isPtrdiffT()) &&
11537
11541
AT.matchesType(S.Context, CastTy))
11538
11542
Match = ArgType::NoMatchPedantic;
@@ -11587,20 +11591,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11587
11591
// should be printed as 'long' for 64-bit compatibility.)
11588
11592
// Rather than emitting a normal format/argument mismatch, we want to
11589
11593
// add a cast to the recommended type (and correct the format string
11590
- // if necessary).
11594
+ // if necessary). We should also do so for scoped enumerations.
11591
11595
SmallString<16> CastBuf;
11592
11596
llvm::raw_svector_ostream CastFix(CastBuf);
11593
11597
CastFix << (S.LangOpts.CPlusPlus ? "static_cast<" : "(");
11594
- if (IsScopedEnum) {
11595
- CastFix << AT.getRepresentativeType(S.Context).getAsString(
11596
- S.Context.getPrintingPolicy());
11597
- } else {
11598
- IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11599
- }
11598
+ IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11600
11599
CastFix << (S.LangOpts.CPlusPlus ? ">" : ")");
11601
11600
11602
11601
SmallVector<FixItHint,4> Hints;
11603
- if ((! AT.matchesType(S.Context, IntendedTy) && !IsScopedEnum) ||
11602
+ if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match ||
11604
11603
ShouldNotPrintDirectly)
11605
11604
Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str()));
11606
11605
@@ -11628,7 +11627,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11628
11627
Hints.push_back(FixItHint::CreateInsertion(After, ")"));
11629
11628
}
11630
11629
11631
- if (ShouldNotPrintDirectly) {
11630
+ if (ShouldNotPrintDirectly && !IsScopedEnum ) {
11632
11631
// The expression has a type that should not be printed directly.
11633
11632
// We extract the name from the typedef because we don't want to show
11634
11633
// the underlying type in the diagnostic.
0 commit comments