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_bran...@trimble.com>
Date: Tue, 2 Sep 2025 08:59:08 +1200
Subject: [PATCH] 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;

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to