@@ -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>
4646static std::optional<std::string>
4747getCorrespondingSignedTypeName (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