[clang-tools-extra] [llvm] [clang-tidy] modernize-use-std-format: Correct replacement types when signed stdint types are used, and when enums are printed in hex. #150343 (PR #155200)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 1 14:04:19 PDT 2025
https://github.com/DaveBrantonCTCT updated https://github.com/llvm/llvm-project/pull/155200
>From 19b4ddf6a856fdbebac19d1779c6ee83f2d682b0 Mon Sep 17 00:00:00 2001
From: Dave Branton <david_branton at trimble.com>
Date: Tue, 2 Sep 2025 08:59:08 +1200
Subject: [PATCH 1/2] Correct replacement code when signed stdint types are
used, and when enums are printed in hex.
---
.../utils/FormatStringConverter.cpp | 76 +++++++++++++++----
1 file changed, 63 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
index 0df8e913100fc..104ce5eeecab7 100644
--- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
+++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
@@ -42,7 +42,7 @@ static bool isRealCharType(const clang::QualType &Ty) {
/// If possible, return the text name of the signed type that corresponds to the
/// passed integer type. If the passed type is already signed then its name is
-/// just returned. Only supports BuiltinTypes.
+/// just returned. Supports BuiltinTypes and types from <cstdint>
static std::optional<std::string>
getCorrespondingSignedTypeName(const clang::QualType &QT) {
using namespace clang;
@@ -80,6 +80,10 @@ getCorrespondingSignedTypeName(const clang::QualType &QT) {
const bool InStd = SimplifiedTypeName.consume_front("std::");
const StringRef Prefix = InStd ? "std::" : "";
+ if (SimplifiedTypeName.starts_with("int") &&
+ SimplifiedTypeName.ends_with("_t"))
+ return (Twine(Prefix) + SimplifiedTypeName).str();
+
if (SimplifiedTypeName.starts_with("uint") &&
SimplifiedTypeName.ends_with("_t"))
return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
@@ -453,8 +457,36 @@ bool FormatStringConverter::emitIntegerArgument(
// std::format will print bool as either "true" or "false" by default,
// but printf prints them as "0" or "1". Be compatible with printf by
// requesting decimal output.
- FormatSpec.push_back('d');
+
+ // In cases where `x` or `X` was specified in the format string
+ // these will technically have no effect, since the bool can only be zero or
+ // one. However, it seems best to leave them as-is anyway.
+ switch (ArgKind) {
+ case ConversionSpecifier::Kind::xArg:
+ FormatSpec.push_back('x'); // Not strictly needed
+ break;
+ case ConversionSpecifier::Kind::XArg:
+ FormatSpec.push_back('X');
+ break;
+ default:
+ FormatSpec.push_back('d');
+ }
+
} else if (ArgType->isEnumeralType()) {
+
+ // If the format string contained `x` or `X`, then use these
+ // format modifiers. Otherwise the default will work.
+ switch (ArgKind) {
+ case ConversionSpecifier::Kind::xArg:
+ FormatSpec.push_back('x');
+ break;
+ case ConversionSpecifier::Kind::XArg:
+ FormatSpec.push_back('X');
+ break;
+ default:
+ break;
+ }
+
// std::format will try to find a specialization to print the enum
// (and probably fail), whereas printf would have just expected it to
// be passed as its underlying type. However, printf will have forced
@@ -477,10 +509,21 @@ bool FormatStringConverter::emitIntegerArgument(
// Even -Wformat doesn't warn for this. std::format will format as
// unsigned unless we cast it.
if (const std::optional<std::string> MaybeCastType =
- castTypeForArgument(ArgKind, ArgType))
+ castTypeForArgument(ArgKind, ArgType)) {
+ switch (ArgKind) {
+ case ConversionSpecifier::Kind::xArg:
+ FormatSpec.push_back('x');
+ break;
+ case ConversionSpecifier::Kind::XArg:
+ FormatSpec.push_back('X');
+ break;
+ default:
+ break;
+ }
+
ArgFixes.emplace_back(
ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());
- else
+ } else
return conversionNotPossible(
(Twine("argument ") + Twine(ArgIndex) + " cannot be cast to " +
Twine(ArgKind == ConversionSpecifier::Kind::uArg ? "unsigned"
@@ -488,9 +531,20 @@ bool FormatStringConverter::emitIntegerArgument(
" integer type to match format"
" specifier and StrictMode is enabled")
.str());
- } else if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {
- // Only specify integer if the argument is of a different type
- FormatSpec.push_back('d');
+ } else {
+ switch (ArgKind) {
+ case ConversionSpecifier::Kind::xArg:
+ FormatSpec.push_back('x');
+ break;
+ case ConversionSpecifier::Kind::XArg:
+ FormatSpec.push_back('X');
+ break;
+ default:
+ if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {
+ // Only specify integer if the argument is of a different type
+ FormatSpec.push_back('d');
+ }
+ }
}
return true;
}
@@ -514,6 +568,8 @@ bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,
case ConversionSpecifier::Kind::dArg:
case ConversionSpecifier::Kind::iArg:
case ConversionSpecifier::Kind::uArg:
+ case ConversionSpecifier::Kind::xArg:
+ case ConversionSpecifier::Kind::XArg:
if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
FormatSpec))
return false;
@@ -526,12 +582,6 @@ bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,
"static_cast<const void *>(");
break;
}
- case ConversionSpecifier::Kind::xArg:
- FormatSpec.push_back('x');
- break;
- case ConversionSpecifier::Kind::XArg:
- FormatSpec.push_back('X');
- break;
case ConversionSpecifier::Kind::oArg:
FormatSpec.push_back('o');
break;
>From a85cce78221760bb5b10e7c2bd9beb19d62b61b6 Mon Sep 17 00:00:00 2001
From: Dave Branton <david_branton at trimble.com>
Date: Tue, 2 Sep 2025 09:04:05 +1200
Subject: [PATCH 2/2] Added release notes
---
llvm/docs/ReleaseNotes.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 3b90c964ac53d..b714054a79b5e 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -150,6 +150,9 @@ Changes to the Debug Info
Changes to the LLVM tools
---------------------------------
+* modernize-use-std-format now correctly replaces signed types, and correctly
+ adds a static_cast to the underlying type when being printed in hex.
+
Changes to LLDB
---------------------------------
More information about the llvm-commits
mailing list