@@ -11166,12 +11166,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11166
11166
ImplicitMatch == ArgType::NoMatchTypeConfusion)
11167
11167
Match = ImplicitMatch;
11168
11168
assert(Match != ArgType::MatchPromotion);
11169
+
11169
11170
// Look through unscoped enums to their underlying type.
11170
11171
bool IsEnum = false;
11171
11172
bool IsScopedEnum = false;
11173
+ QualType IntendedTy = ExprTy;
11172
11174
if (auto EnumTy = ExprTy->getAs<EnumType>()) {
11175
+ IntendedTy = EnumTy->getDecl()->getIntegerType();
11173
11176
if (EnumTy->isUnscopedEnumerationType()) {
11174
- ExprTy = EnumTy->getDecl()->getIntegerType() ;
11177
+ ExprTy = IntendedTy ;
11175
11178
// This controls whether we're talking about the underlying type or not,
11176
11179
// which we only want to do when it's an unscoped enum.
11177
11180
IsEnum = true;
@@ -11183,7 +11186,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11183
11186
// %C in an Objective-C context prints a unichar, not a wchar_t.
11184
11187
// If the argument is an integer of some kind, believe the %C and suggest
11185
11188
// a cast instead of changing the conversion specifier.
11186
- QualType IntendedTy = ExprTy;
11187
11189
if (isObjCContext() &&
11188
11190
FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
11189
11191
if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
@@ -11219,8 +11221,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11219
11221
std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E);
11220
11222
if (!CastTy.isNull()) {
11221
11223
// %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int
11222
- // (long in ASTContext). Only complain to pedants.
11223
- if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11224
+ // (long in ASTContext). Only complain to pedants or when they're the
11225
+ // underlying type of a scoped enum (which always needs a cast).
11226
+ if (!IsScopedEnum &&
11227
+ (CastTyName == "NSInteger" || CastTyName == "NSUInteger") &&
11224
11228
(AT.isSizeT() || AT.isPtrdiffT()) &&
11225
11229
AT.matchesType(S.Context, CastTy))
11226
11230
Match = ArgType::NoMatchPedantic;
@@ -11275,20 +11279,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11275
11279
// should be printed as 'long' for 64-bit compatibility.)
11276
11280
// Rather than emitting a normal format/argument mismatch, we want to
11277
11281
// add a cast to the recommended type (and correct the format string
11278
- // if necessary).
11282
+ // if necessary). We should also do so for scoped enumerations.
11279
11283
SmallString<16> CastBuf;
11280
11284
llvm::raw_svector_ostream CastFix(CastBuf);
11281
11285
CastFix << (S.LangOpts.CPlusPlus ? "static_cast<" : "(");
11282
- if (IsScopedEnum) {
11283
- CastFix << AT.getRepresentativeType(S.Context).getAsString(
11284
- S.Context.getPrintingPolicy());
11285
- } else {
11286
- IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11287
- }
11286
+ IntendedTy.print(CastFix, S.Context.getPrintingPolicy());
11288
11287
CastFix << (S.LangOpts.CPlusPlus ? ">" : ")");
11289
11288
11290
11289
SmallVector<FixItHint,4> Hints;
11291
- if ((! AT.matchesType(S.Context, IntendedTy) && !IsScopedEnum) ||
11290
+ if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match ||
11292
11291
ShouldNotPrintDirectly)
11293
11292
Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str()));
11294
11293
@@ -11316,7 +11315,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
11316
11315
Hints.push_back(FixItHint::CreateInsertion(After, ")"));
11317
11316
}
11318
11317
11319
- if (ShouldNotPrintDirectly) {
11318
+ if (ShouldNotPrintDirectly && !IsScopedEnum ) {
11320
11319
// The expression has a type that should not be printed directly.
11321
11320
// We extract the name from the typedef because we don't want to show
11322
11321
// the underlying type in the diagnostic.
0 commit comments