Skip to content

Commit 19b4ddf

Browse files
Correct replacement code when signed stdint types are used, and when enums are printed in hex.
1 parent af0f85c commit 19b4ddf

File tree

1 file changed

+63
-13
lines changed

1 file changed

+63
-13
lines changed

clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static bool isRealCharType(const clang::QualType &Ty) {
4242

4343
/// If possible, return the text name of the signed type that corresponds to the
4444
/// passed integer type. If the passed type is already signed then its name is
45-
/// just returned. Only supports BuiltinTypes.
45+
/// just returned. Supports BuiltinTypes and types from <cstdint>
4646
static std::optional<std::string>
4747
getCorrespondingSignedTypeName(const clang::QualType &QT) {
4848
using namespace clang;
@@ -80,6 +80,10 @@ getCorrespondingSignedTypeName(const clang::QualType &QT) {
8080
const bool InStd = SimplifiedTypeName.consume_front("std::");
8181
const StringRef Prefix = InStd ? "std::" : "";
8282

83+
if (SimplifiedTypeName.starts_with("int") &&
84+
SimplifiedTypeName.ends_with("_t"))
85+
return (Twine(Prefix) + SimplifiedTypeName).str();
86+
8387
if (SimplifiedTypeName.starts_with("uint") &&
8488
SimplifiedTypeName.ends_with("_t"))
8589
return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
@@ -453,8 +457,36 @@ bool FormatStringConverter::emitIntegerArgument(
453457
// std::format will print bool as either "true" or "false" by default,
454458
// but printf prints them as "0" or "1". Be compatible with printf by
455459
// requesting decimal output.
456-
FormatSpec.push_back('d');
460+
461+
// In cases where `x` or `X` was specified in the format string
462+
// these will technically have no effect, since the bool can only be zero or
463+
// one. However, it seems best to leave them as-is anyway.
464+
switch (ArgKind) {
465+
case ConversionSpecifier::Kind::xArg:
466+
FormatSpec.push_back('x'); // Not strictly needed
467+
break;
468+
case ConversionSpecifier::Kind::XArg:
469+
FormatSpec.push_back('X');
470+
break;
471+
default:
472+
FormatSpec.push_back('d');
473+
}
474+
457475
} else if (ArgType->isEnumeralType()) {
476+
477+
// If the format string contained `x` or `X`, then use these
478+
// format modifiers. Otherwise the default will work.
479+
switch (ArgKind) {
480+
case ConversionSpecifier::Kind::xArg:
481+
FormatSpec.push_back('x');
482+
break;
483+
case ConversionSpecifier::Kind::XArg:
484+
FormatSpec.push_back('X');
485+
break;
486+
default:
487+
break;
488+
}
489+
458490
// std::format will try to find a specialization to print the enum
459491
// (and probably fail), whereas printf would have just expected it to
460492
// be passed as its underlying type. However, printf will have forced
@@ -477,20 +509,42 @@ bool FormatStringConverter::emitIntegerArgument(
477509
// Even -Wformat doesn't warn for this. std::format will format as
478510
// unsigned unless we cast it.
479511
if (const std::optional<std::string> MaybeCastType =
480-
castTypeForArgument(ArgKind, ArgType))
512+
castTypeForArgument(ArgKind, ArgType)) {
513+
switch (ArgKind) {
514+
case ConversionSpecifier::Kind::xArg:
515+
FormatSpec.push_back('x');
516+
break;
517+
case ConversionSpecifier::Kind::XArg:
518+
FormatSpec.push_back('X');
519+
break;
520+
default:
521+
break;
522+
}
523+
481524
ArgFixes.emplace_back(
482525
ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());
483-
else
526+
} else
484527
return conversionNotPossible(
485528
(Twine("argument ") + Twine(ArgIndex) + " cannot be cast to " +
486529
Twine(ArgKind == ConversionSpecifier::Kind::uArg ? "unsigned"
487530
: "signed") +
488531
" integer type to match format"
489532
" specifier and StrictMode is enabled")
490533
.str());
491-
} else if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {
492-
// Only specify integer if the argument is of a different type
493-
FormatSpec.push_back('d');
534+
} else {
535+
switch (ArgKind) {
536+
case ConversionSpecifier::Kind::xArg:
537+
FormatSpec.push_back('x');
538+
break;
539+
case ConversionSpecifier::Kind::XArg:
540+
FormatSpec.push_back('X');
541+
break;
542+
default:
543+
if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {
544+
// Only specify integer if the argument is of a different type
545+
FormatSpec.push_back('d');
546+
}
547+
}
494548
}
495549
return true;
496550
}
@@ -514,6 +568,8 @@ bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,
514568
case ConversionSpecifier::Kind::dArg:
515569
case ConversionSpecifier::Kind::iArg:
516570
case ConversionSpecifier::Kind::uArg:
571+
case ConversionSpecifier::Kind::xArg:
572+
case ConversionSpecifier::Kind::XArg:
517573
if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
518574
FormatSpec))
519575
return false;
@@ -526,12 +582,6 @@ bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,
526582
"static_cast<const void *>(");
527583
break;
528584
}
529-
case ConversionSpecifier::Kind::xArg:
530-
FormatSpec.push_back('x');
531-
break;
532-
case ConversionSpecifier::Kind::XArg:
533-
FormatSpec.push_back('X');
534-
break;
535585
case ConversionSpecifier::Kind::oArg:
536586
FormatSpec.push_back('o');
537587
break;

0 commit comments

Comments
 (0)