[clang] Fix clang to recognize new C23 modifiers %w and %wf when printing and scanning (PR #71771)

via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 7 16:33:38 PST 2024


https://github.com/ZijunZhaoCCK updated https://github.com/llvm/llvm-project/pull/71771

>From 06c4cf02dfb4b20c8349c5f3c7209276f6d56edf Mon Sep 17 00:00:00 2001
From: zijunzhao <zijunzhao at google.com>
Date: Thu, 9 Nov 2023 02:21:46 +0000
Subject: [PATCH 1/5] Fix clang to recognize new C23 modifiers %w and %wf when
 printing

---
 clang/include/clang/AST/FormatString.h | 16 +++++++-
 clang/lib/AST/FormatString.cpp         | 52 +++++++++++++++++++++++++-
 clang/lib/AST/PrintfFormatString.cpp   | 19 ++++++++++
 clang/test/Sema/format-strings-ms.c    | 28 ++++++++++++++
 4 files changed, 112 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index 5c4ad9baaef608..6a886854650f1d 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -81,8 +81,10 @@ class LengthModifier {
     AsLongDouble, // 'L'
     AsAllocate,   // for '%as', GNU extension to C90 scanf
     AsMAllocate,  // for '%ms', GNU extension to scanf
-    AsWide,       // 'w' (MSVCRT, like l but only for c, C, s, S, or Z
-    AsWideChar = AsLong // for '%ls', only makes sense for printf
+    AsWide,       // 'w' (1. MSVCRT, like l but only for c, C, s, S, or Z on windows
+                  // 2. for b, d, i, o, u, x, or X when a size followed(like 8, 16, 32 or 64)
+    AsWideFast,   // 'wf' (for b, d, i, o, u, x, or X)
+    AsWideChar = AsLong, // for '%ls', only makes sense for printf
   };
 
   LengthModifier()
@@ -417,6 +419,7 @@ class FormatSpecifier {
   ///  http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
   bool UsesPositionalArg;
   unsigned argIndex;
+  unsigned size;
 public:
   FormatSpecifier(bool isPrintf)
     : CS(isPrintf), VectorNumElts(false),
@@ -460,6 +463,15 @@ class FormatSpecifier {
     FieldWidth = Amt;
   }
 
+  void setSize(unsigned s) {
+    size = s;
+  }
+
+  unsigned getSize() const {
+    return size;
+  }
+
+
   bool usesPositionalArg() const { return UsesPositionalArg; }
 
   bool hasValidLengthModifier(const TargetInfo &Target,
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index e0c9e18cfe3a24..ebc136e780717e 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -286,7 +286,33 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
       lmKind = LengthModifier::AsInt3264;
       break;
     case 'w':
-      lmKind = LengthModifier::AsWide; ++I; break;
+      ++I;
+      if (I == E) return false;
+      if (*I == 'f') {
+        lmKind = LengthModifier::AsWideFast;
+        ++I;
+      } else {
+        lmKind = LengthModifier::AsWide;
+      }
+
+      if (I == E) return false;
+      int s = 0;
+      while (unsigned(*I - '0') <= 9) {
+        s = 10 * s + unsigned(*I - '0');
+        ++I;
+      }
+
+      // s == 0 is MSVCRT case, like l but only for c, C, s, S, or Z on windows
+      // s != 0 for b, d, i, o, u, x, or X when a size followed(like 8, 16, 32 or 64)
+      if (s != 0) {
+        std::set<int> supported_list {8, 16, 32, 64};
+        if (supported_list.count(s) == 0) {
+          return false;
+        }
+        FS.setSize(s);
+      }
+
+      break;
   }
   LengthModifier lm(lmPosition, lmKind);
   FS.setLengthModifier(lm);
@@ -703,6 +729,8 @@ analyze_format_string::LengthModifier::toString() const {
     return "m";
   case AsWide:
     return "w";
+  case AsWideFast:
+    return "wf";
   case None:
     return "";
   }
@@ -970,6 +998,27 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target,
         case ConversionSpecifier::SArg:
         case ConversionSpecifier::ZArg:
           return Target.getTriple().isOSMSVCRT();
+        case ConversionSpecifier::bArg:
+        case ConversionSpecifier::dArg:
+        case ConversionSpecifier::iArg:
+        case ConversionSpecifier::oArg:
+        case ConversionSpecifier::uArg:
+        case ConversionSpecifier::xArg:
+        case ConversionSpecifier::XArg:
+          return true;
+        default:
+          return false;
+      }
+    case LengthModifier::AsWideFast:
+      switch (CS.getKind()) {
+        case ConversionSpecifier::bArg:
+        case ConversionSpecifier::dArg:
+        case ConversionSpecifier::iArg:
+        case ConversionSpecifier::oArg:
+        case ConversionSpecifier::uArg:
+        case ConversionSpecifier::xArg:
+        case ConversionSpecifier::XArg:
+          return true;
         default:
           return false;
       }
@@ -996,6 +1045,7 @@ bool FormatSpecifier::hasStandardLengthModifier() const {
     case LengthModifier::AsInt3264:
     case LengthModifier::AsInt64:
     case LengthModifier::AsWide:
+    case LengthModifier::AsWideFast:
     case LengthModifier::AsShortLong: // ???
       return false;
   }
diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp
index f0b9d0ecaf2346..4b9111e8bcf509 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -537,7 +537,16 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
             ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
       case LengthModifier::AsAllocate:
       case LengthModifier::AsMAllocate:
+        return ArgType::Invalid();
       case LengthModifier::AsWide:
+      case LengthModifier::AsWideFast:
+        int s = getSize();
+        bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+        ArgType fastType = Ctx.getTargetInfo().getTriple().isArch64Bit() ? Ctx.LongLongTy : Ctx.IntTy;
+        if (s == 8) return Ctx.CharTy;
+        if (s == 16) return fast? fastType : Ctx.ShortTy;
+        if (s == 32) return fast? fastType : Ctx.IntTy;
+        if (s == 64) return Ctx.LongLongTy;
         return ArgType::Invalid();
     }
 
@@ -572,7 +581,16 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
             ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"));
       case LengthModifier::AsAllocate:
       case LengthModifier::AsMAllocate:
+        return ArgType::Invalid();
       case LengthModifier::AsWide:
+      case LengthModifier::AsWideFast:
+        int s = getSize();
+        bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+        ArgType fastType = Ctx.getTargetInfo().getTriple().isArch64Bit() ? Ctx.UnsignedLongLongTy : Ctx.UnsignedIntTy;
+        if (s == 8) return Ctx.UnsignedCharTy;
+        if (s == 16) return fast? fastType : Ctx.UnsignedShortTy;
+        if (s == 32) return fast? fastType : Ctx.UnsignedIntTy;
+        if (s == 64) return Ctx.UnsignedLongLongTy;
         return ArgType::Invalid();
     }
 
@@ -621,6 +639,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
       case LengthModifier::AsInt3264:
       case LengthModifier::AsInt64:
       case LengthModifier::AsWide:
+      case LengthModifier::AsWideFast:
         return ArgType::Invalid();
       case LengthModifier::AsShortLong:
         llvm_unreachable("only used for OpenCL which doesn not handle nArg");
diff --git a/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c
index 697032673d4e77..59ae930bb734eb 100644
--- a/clang/test/Sema/format-strings-ms.c
+++ b/clang/test/Sema/format-strings-ms.c
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 -Wformat-non-iso -DNON_ISO_WARNING %s
 
+#include <stdint.h>
 int printf(const char *format, ...) __attribute__((format(printf, 1, 2)));
 int scanf(const char * restrict, ...) ;
 typedef unsigned short wchar_t;
@@ -85,4 +86,31 @@ void z_test(void *p) {
   scanf("%Z", p); // expected-warning{{invalid conversion specifier 'Z'}}
 }
 
+void w_int_test(void) {
+  int8_t a = 0b101;
+  int16_t b = 2;
+  uint32_t c = 123;
+  int64_t d = 0x3b;
+
+  // for %w
+  printf("%w8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int8_t' (aka 'signed char')}}
+  printf("%w16i", b);
+  printf("%w32u", c);
+  printf("%w64x", d);
+
+}
+
+void wf_test(void) {
+  int_fast8_t a = 0b101;
+  uint_fast16_t b = 2;
+  int_fast32_t c = 021;
+  int_fast64_t d = 0x3a;
+
+  // for %wf
+  printf("%wf8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int_fast8_t' (aka 'signed char')}}
+  printf("%wf16u", b);
+  printf("%wf32o", c);
+  printf("%wf64X", d);
+}
+
 #endif

>From 2ec84a88a9ba6e4576a855b419dd6bafa9f3d721 Mon Sep 17 00:00:00 2001
From: zijunzhao <zijunzhao at google.com>
Date: Tue, 14 Nov 2023 00:38:41 +0000
Subject: [PATCH 2/5] Update code and tests. %w and %wf are available in
 printf() and scanf()

---
 clang/include/clang/AST/FormatString.h |  4 ++-
 clang/lib/AST/PrintfFormatString.cpp   | 34 +++++++++++++++++---------
 clang/lib/AST/ScanfFormatString.cpp    | 14 +++++++++--
 clang/test/Sema/format-strings-ms.c    | 25 ++++++++++++-------
 4 files changed, 53 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index 6a886854650f1d..2e48a8ddfde4d3 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -471,7 +471,6 @@ class FormatSpecifier {
     return size;
   }
 
-
   bool usesPositionalArg() const { return UsesPositionalArg; }
 
   bool hasValidLengthModifier(const TargetInfo &Target,
@@ -792,6 +791,9 @@ bool parseFormatStringHasFormattingSpecifiers(const char *Begin,
                                               const LangOptions &LO,
                                               const TargetInfo &Target);
 
+ArgType wToArgType(int size, bool fast, ASTContext &C);
+ArgType wToArgTypeUnsigned(int size, bool fast, ASTContext &C);
+
 } // end analyze_format_string namespace
 } // end clang namespace
 #endif
diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp
index 4b9111e8bcf509..e6e47403198af7 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -484,6 +484,26 @@ bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers(
   return false;
 }
 
+ArgType clang::analyze_format_string::wToArgType(
+    int size, bool fast, ASTContext &C) {
+  ArgType fastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.LongLongTy : C.IntTy;
+  if (size == 8) return C.CharTy;
+  if (size == 16) return fast? fastType : C.ShortTy;
+  if (size == 32) return fast? fastType : C.IntTy;
+  if (size == 64) return C.LongLongTy;
+  return ArgType::Invalid();
+}
+
+ArgType clang::analyze_format_string::wToArgTypeUnsigned(
+    int size, bool fast, ASTContext &C) {
+  ArgType fastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.UnsignedLongLongTy : C.UnsignedIntTy;
+  if (size == 8) return C.UnsignedCharTy;
+  if (size == 16) return fast? fastType : C.UnsignedShortTy;
+  if (size == 32) return fast? fastType : C.UnsignedIntTy;
+  if (size == 64) return C.UnsignedLongLongTy;
+  return ArgType::Invalid();
+}
+
 //===----------------------------------------------------------------------===//
 // Methods on PrintfSpecifier.
 //===----------------------------------------------------------------------===//
@@ -542,12 +562,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
       case LengthModifier::AsWideFast:
         int s = getSize();
         bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-        ArgType fastType = Ctx.getTargetInfo().getTriple().isArch64Bit() ? Ctx.LongLongTy : Ctx.IntTy;
-        if (s == 8) return Ctx.CharTy;
-        if (s == 16) return fast? fastType : Ctx.ShortTy;
-        if (s == 32) return fast? fastType : Ctx.IntTy;
-        if (s == 64) return Ctx.LongLongTy;
-        return ArgType::Invalid();
+        return clang::analyze_format_string::wToArgType(s, fast, Ctx);
     }
 
   if (CS.isUIntArg())
@@ -586,12 +601,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
       case LengthModifier::AsWideFast:
         int s = getSize();
         bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-        ArgType fastType = Ctx.getTargetInfo().getTriple().isArch64Bit() ? Ctx.UnsignedLongLongTy : Ctx.UnsignedIntTy;
-        if (s == 8) return Ctx.UnsignedCharTy;
-        if (s == 16) return fast? fastType : Ctx.UnsignedShortTy;
-        if (s == 32) return fast? fastType : Ctx.UnsignedIntTy;
-        if (s == 64) return Ctx.UnsignedLongLongTy;
-        return ArgType::Invalid();
+        return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);
     }
 
   if (CS.isDoubleArg()) {
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index 64c430e623b577..e640bc2de4d259 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -261,9 +261,13 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
         case LengthModifier::AsMAllocate:
         case LengthModifier::AsInt32:
         case LengthModifier::AsInt3264:
-        case LengthModifier::AsWide:
         case LengthModifier::AsShortLong:
           return ArgType::Invalid();
+        case LengthModifier::AsWide:
+        case LengthModifier::AsWideFast:
+          int s = getSize();
+          bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+          return clang::analyze_format_string::wToArgType(s, fast, Ctx);
       }
       llvm_unreachable("Unsupported LengthModifier Type");
 
@@ -303,9 +307,15 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
         case LengthModifier::AsMAllocate:
         case LengthModifier::AsInt32:
         case LengthModifier::AsInt3264:
-        case LengthModifier::AsWide:
         case LengthModifier::AsShortLong:
           return ArgType::Invalid();
+        case LengthModifier::AsWide:
+        case LengthModifier::AsWideFast:
+          int s = getSize();
+          bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+          if (CS.getKind() == ConversionSpecifier::uArg or CS.getKind() == ConversionSpecifier::UArg)
+            return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);
+          return clang::analyze_format_string::wToArgType(s, fast, Ctx);
       }
       llvm_unreachable("Unsupported LengthModifier Type");
 
diff --git a/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c
index 59ae930bb734eb..bca824533d128a 100644
--- a/clang/test/Sema/format-strings-ms.c
+++ b/clang/test/Sema/format-strings-ms.c
@@ -87,30 +87,37 @@ void z_test(void *p) {
 }
 
 void w_int_test(void) {
-  int8_t a = 0b101;
-  int16_t b = 2;
-  uint32_t c = 123;
-  int64_t d = 0x3b;
+  int8_t a;
+  int16_t b;
+  uint32_t c;
+  int64_t d;
 
   // for %w
   printf("%w8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int8_t' (aka 'signed char')}}
   printf("%w16i", b);
   printf("%w32u", c);
   printf("%w64x", d);
-
+  scanf("%w8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int'}}
+  scanf("%w16i", b); // expected-warning{{format specifies type 'short' but the argument has type 'int'}}
+  scanf("%w32u", c);
+  scanf("%w64x", d);
 }
 
 void wf_test(void) {
-  int_fast8_t a = 0b101;
-  uint_fast16_t b = 2;
-  int_fast32_t c = 021;
-  int_fast64_t d = 0x3a;
+  int_fast8_t a;
+  uint_fast16_t b;
+  int_fast32_t c;
+  int_fast64_t d;
 
   // for %wf
   printf("%wf8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int_fast8_t' (aka 'signed char')}}
   printf("%wf16u", b);
   printf("%wf32o", c);
   printf("%wf64X", d);
+  scanf("%wf8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int'}}
+  scanf("%wf16u", b);
+  scanf("%wf32o", c);
+  scanf("%wf64X", d);
 }
 
 #endif

>From 1431133f1e48bb58fa407e4a6d985fca24e9410b Mon Sep 17 00:00:00 2001
From: zijunzhao <zijunzhao at google.com>
Date: Tue, 14 Nov 2023 00:38:41 +0000
Subject: [PATCH 3/5] Update code and tests. %w and %wf are available in
 printf() and scanf()

---
 clang/include/clang/AST/FormatString.h | 14 ++++++-------
 clang/lib/AST/FormatString.cpp         |  2 +-
 clang/lib/AST/PrintfFormatString.cpp   | 28 +++++++++++++-------------
 clang/lib/AST/ScanfFormatString.cpp    |  4 ++--
 4 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index 2e48a8ddfde4d3..dba973ffcaa4ef 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -419,7 +419,7 @@ class FormatSpecifier {
   ///  http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
   bool UsesPositionalArg;
   unsigned argIndex;
-  unsigned size;
+  unsigned ExplicitlyFixedSize;
 public:
   FormatSpecifier(bool isPrintf)
     : CS(isPrintf), VectorNumElts(false),
@@ -463,12 +463,12 @@ class FormatSpecifier {
     FieldWidth = Amt;
   }
 
-  void setSize(unsigned s) {
-    size = s;
+  void setExplicitlyFixedSize(unsigned s) {
+    ExplicitlyFixedSize = s;
   }
 
-  unsigned getSize() const {
-    return size;
+  unsigned getExplicitlyFixedSize() const {
+    return ExplicitlyFixedSize;
   }
 
   bool usesPositionalArg() const { return UsesPositionalArg; }
@@ -791,8 +791,8 @@ bool parseFormatStringHasFormattingSpecifiers(const char *Begin,
                                               const LangOptions &LO,
                                               const TargetInfo &Target);
 
-ArgType wToArgType(int size, bool fast, ASTContext &C);
-ArgType wToArgTypeUnsigned(int size, bool fast, ASTContext &C);
+ArgType wToArgType(int Size, bool Fast, ASTContext &C);
+ArgType wToArgTypeUnsigned(int Size, bool Fast, ASTContext &C);
 
 } // end analyze_format_string namespace
 } // end clang namespace
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index ebc136e780717e..88d0f1c98ed0d2 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -309,7 +309,7 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
         if (supported_list.count(s) == 0) {
           return false;
         }
-        FS.setSize(s);
+        FS.setExplicitlyFixedSize(s);
       }
 
       break;
diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp
index e6e47403198af7..ad92fb3aab97d8 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -485,22 +485,22 @@ bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers(
 }
 
 ArgType clang::analyze_format_string::wToArgType(
-    int size, bool fast, ASTContext &C) {
-  ArgType fastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.LongLongTy : C.IntTy;
-  if (size == 8) return C.CharTy;
-  if (size == 16) return fast? fastType : C.ShortTy;
-  if (size == 32) return fast? fastType : C.IntTy;
-  if (size == 64) return C.LongLongTy;
+    int Size, bool Fast, ASTContext &C) {
+  ArgType FastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.LongLongTy : C.IntTy;
+  if (Size == 8) return C.CharTy;
+  if (Size == 16) return Fast? FastType : C.ShortTy;
+  if (Size == 32) return Fast? FastType : C.IntTy;
+  if (Size == 64) return C.LongLongTy;
   return ArgType::Invalid();
 }
 
 ArgType clang::analyze_format_string::wToArgTypeUnsigned(
-    int size, bool fast, ASTContext &C) {
-  ArgType fastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.UnsignedLongLongTy : C.UnsignedIntTy;
-  if (size == 8) return C.UnsignedCharTy;
-  if (size == 16) return fast? fastType : C.UnsignedShortTy;
-  if (size == 32) return fast? fastType : C.UnsignedIntTy;
-  if (size == 64) return C.UnsignedLongLongTy;
+    int Size, bool Fast, ASTContext &C) {
+  ArgType FastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.UnsignedLongLongTy : C.UnsignedIntTy;
+  if (Size == 8) return C.UnsignedCharTy;
+  if (Size == 16) return Fast? FastType : C.UnsignedShortTy;
+  if (Size == 32) return Fast? FastType : C.UnsignedIntTy;
+  if (Size == 64) return C.UnsignedLongLongTy;
   return ArgType::Invalid();
 }
 
@@ -560,7 +560,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
         return ArgType::Invalid();
       case LengthModifier::AsWide:
       case LengthModifier::AsWideFast:
-        int s = getSize();
+        int s = getExplicitlyFixedSize();
         bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
         return clang::analyze_format_string::wToArgType(s, fast, Ctx);
     }
@@ -599,7 +599,7 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
         return ArgType::Invalid();
       case LengthModifier::AsWide:
       case LengthModifier::AsWideFast:
-        int s = getSize();
+        int s = getExplicitlyFixedSize();
         bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
         return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);
     }
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index e640bc2de4d259..6f856f1b0bedbd 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -265,7 +265,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
           return ArgType::Invalid();
         case LengthModifier::AsWide:
         case LengthModifier::AsWideFast:
-          int s = getSize();
+          int s = getExplicitlyFixedSize();
           bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
           return clang::analyze_format_string::wToArgType(s, fast, Ctx);
       }
@@ -311,7 +311,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
           return ArgType::Invalid();
         case LengthModifier::AsWide:
         case LengthModifier::AsWideFast:
-          int s = getSize();
+          int s = getExplicitlyFixedSize();
           bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
           if (CS.getKind() == ConversionSpecifier::uArg or CS.getKind() == ConversionSpecifier::UArg)
             return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);

>From b4ff31c11d64079456cda63bed1a734958754b10 Mon Sep 17 00:00:00 2001
From: zijunzhao <zijunzhao at google.com>
Date: Sat, 2 Dec 2023 01:35:09 +0000
Subject: [PATCH 4/5] 1. add one more warning about unsupported sizes 2.
 reformat

---
 clang/include/clang/AST/FormatString.h        | 16 +++++++++++---
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 ++++
 clang/lib/AST/FormatString.cpp                | 13 ++++++-----
 clang/lib/AST/PrintfFormatString.cpp          |  6 ++---
 clang/lib/AST/ScanfFormatString.cpp           |  6 ++---
 clang/lib/Sema/SemaChecking.cpp               | 22 +++++++++++++++++++
 clang/test/Sema/format-strings-ms.c           |  8 +++++++
 7 files changed, 60 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index dba973ffcaa4ef..c3f761ed03d9f4 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -420,10 +420,12 @@ class FormatSpecifier {
   bool UsesPositionalArg;
   unsigned argIndex;
   unsigned ExplicitlyFixedSize;
+  bool ExplicitlyFixedSizeValid;
+
 public:
   FormatSpecifier(bool isPrintf)
     : CS(isPrintf), VectorNumElts(false),
-      UsesPositionalArg(false), argIndex(0) {}
+      UsesPositionalArg(false), argIndex(0), ExplicitlyFixedSizeValid(true) {}
 
   void setLengthModifier(LengthModifier lm) {
     LM = lm;
@@ -463,8 +465,8 @@ class FormatSpecifier {
     FieldWidth = Amt;
   }
 
-  void setExplicitlyFixedSize(unsigned s) {
-    ExplicitlyFixedSize = s;
+  void setExplicitlyFixedSize(unsigned S) {
+    ExplicitlyFixedSize = S;
   }
 
   unsigned getExplicitlyFixedSize() const {
@@ -478,6 +480,14 @@ class FormatSpecifier {
 
   bool hasStandardLengthModifier() const;
 
+  void setExplicitlyFixedSizeValid(bool valid) {
+    ExplicitlyFixedSizeValid = valid;
+  }
+
+  bool isExplicitlyFixedSizeSupported() const {
+    return ExplicitlyFixedSizeValid;
+  }
+
   std::optional<LengthModifier> getCorrectedLengthModifier() const;
 
   bool hasStandardConversionSpecifier(const LangOptions &LangOpt) const;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6dfb2d7195203a..5d1dcb66d29903 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9707,6 +9707,10 @@ def warn_missing_format_string : Warning<
 def warn_scanf_nonzero_width : Warning<
   "zero field width in scanf format string is unused">,
   InGroup<Format>;
+def warn_format_conversion_size_unsupported: Warning<
+  "format specifies %select{an exact-|a fastest-}0 width integer type with "
+  "invalid bit-width %1">,
+  InGroup<Format>;
 def warn_format_conversion_argument_type_mismatch : Warning<
   "format specifies type %0 but the argument has "
   "%select{type|underlying type}2 %1">,
diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index 88d0f1c98ed0d2..39359c2b18fb49 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -297,18 +297,19 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
 
       if (I == E) return false;
       int s = 0;
+      bool MSVCRT = true;
       while (unsigned(*I - '0') <= 9) {
+        MSVCRT = false;
         s = 10 * s + unsigned(*I - '0');
         ++I;
       }
 
-      // s == 0 is MSVCRT case, like l but only for c, C, s, S, or Z on windows
-      // s != 0 for b, d, i, o, u, x, or X when a size followed(like 8, 16, 32 or 64)
-      if (s != 0) {
+      // MSVCRT == true is MSVCRT case, like l but only for c, C, s, S, or Z on windows
+      // MSVCRT == false for b, d, i, o, u, x, or X when a size followed(like 8, 16, 32 or 64)
+      if (!MSVCRT) {
         std::set<int> supported_list {8, 16, 32, 64};
-        if (supported_list.count(s) == 0) {
-          return false;
-        }
+        if (supported_list.count(s) == 0)
+          FS.setExplicitlyFixedSizeValid(false);
         FS.setExplicitlyFixedSize(s);
       }
 
diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp
index ad92fb3aab97d8..d4d9a0596620af 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -560,9 +560,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
         return ArgType::Invalid();
       case LengthModifier::AsWide:
       case LengthModifier::AsWideFast:
-        int s = getExplicitlyFixedSize();
-        bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-        return clang::analyze_format_string::wToArgType(s, fast, Ctx);
+        int S = getExplicitlyFixedSize();
+        bool FAST = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+        return clang::analyze_format_string::wToArgType(S, FAST, Ctx);
     }
 
   if (CS.isUIntArg())
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index 6f856f1b0bedbd..de261a303f322a 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -265,9 +265,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
           return ArgType::Invalid();
         case LengthModifier::AsWide:
         case LengthModifier::AsWideFast:
-          int s = getExplicitlyFixedSize();
-          bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-          return clang::analyze_format_string::wToArgType(s, fast, Ctx);
+          int S = getExplicitlyFixedSize();
+          bool FAST = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+          return clang::analyze_format_string::wToArgType(S, FAST, Ctx);
       }
       llvm_unreachable("Unsupported LengthModifier Type");
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 77c8334f3ca25d..7e2f5dc6708c4d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11680,6 +11680,17 @@ bool CheckPrintfHandler::HandlePrintfSpecifier(
   if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
     HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
 
+  // Check the explicitly fixed size is supported
+  if (!FS.isExplicitlyFixedSizeSupported()){
+    EmitFormatDiagnostic(S.PDiag(
+                         diag::warn_format_conversion_size_unsupported) 
+                         << FS.getLengthModifier().toString()
+                         << FS.getExplicitlyFixedSize(),
+                         getLocationOfByte(startSpecifier),
+                         /*IsStringLocation*/true,
+                         getSpecifierRange(startSpecifier, specifierLen));
+  }
+
   // The remaining checks depend on the data arguments.
   if (ArgPassingKind == Sema::FAPK_VAList)
     return true;
@@ -12289,6 +12300,17 @@ bool CheckScanfHandler::HandleScanfSpecifier(
   else if (!FS.hasStandardLengthConversionCombination())
     HandleInvalidLengthModifier(FS, CS, startSpecifier, specifierLen,
                                 diag::warn_format_non_standard_conversion_spec);
+  
+  // Check the explicitly fixed size is supported
+  if (!FS.isExplicitlyFixedSizeSupported()){
+    EmitFormatDiagnostic(S.PDiag(
+                         diag::warn_format_conversion_size_unsupported) 
+                         << FS.getLengthModifier().toString()
+                         << FS.getExplicitlyFixedSize(),
+                         getLocationOfByte(startSpecifier),
+                         /*IsStringLocation*/true,
+                         getSpecifierRange(startSpecifier, specifierLen));
+  }
 
   if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
     HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
diff --git a/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c
index bca824533d128a..e6172ee436114e 100644
--- a/clang/test/Sema/format-strings-ms.c
+++ b/clang/test/Sema/format-strings-ms.c
@@ -101,6 +101,10 @@ void w_int_test(void) {
   scanf("%w16i", b); // expected-warning{{format specifies type 'short' but the argument has type 'int'}}
   scanf("%w32u", c);
   scanf("%w64x", d);
+
+  // unsupported size
+  printf("%w92d", a); // expected-warning{{format specifies w width integer type with invalid bit-width 92}}
+  scanf("%w0i", b); // expected-warning{{format specifies w width integer type with invalid bit-width 0}}
 }
 
 void wf_test(void) {
@@ -118,6 +122,10 @@ void wf_test(void) {
   scanf("%wf16u", b);
   scanf("%wf32o", c);
   scanf("%wf64X", d);
+
+  // unsupported size
+  printf("%wf0d", a); // expected-warning{{format specifies wf width integer type with invalid bit-width 0}}
+  scanf("%wf35u", b); // expected-warning{{format specifies wf width integer type with invalid bit-width 35}}
 }
 
 #endif

>From a5b2d771fc51df5018765896207b76cfee88e39b Mon Sep 17 00:00:00 2001
From: zijunzhao <zijunzhao at google.com>
Date: Tue, 5 Dec 2023 07:48:50 +0000
Subject: [PATCH 5/5] Add getFastIntTypeByWidth() function

---
 clang/include/clang/AST/FormatString.h |  3 +-
 clang/include/clang/Basic/TargetInfo.h |  4 +++
 clang/lib/AST/PrintfFormatString.cpp   | 48 ++++++++++++++------------
 clang/lib/AST/ScanfFormatString.cpp    | 10 +++---
 clang/lib/Basic/TargetInfo.cpp         | 25 ++++++++++++++
 clang/test/Sema/format-strings-ms.c    |  8 ++---
 6 files changed, 65 insertions(+), 33 deletions(-)

diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h
index c3f761ed03d9f4..7add829ee1cca9 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -801,8 +801,7 @@ bool parseFormatStringHasFormattingSpecifiers(const char *Begin,
                                               const LangOptions &LO,
                                               const TargetInfo &Target);
 
-ArgType wToArgType(int Size, bool Fast, ASTContext &C);
-ArgType wToArgTypeUnsigned(int Size, bool Fast, ASTContext &C);
+ArgType wToArgType(int Size, bool IsSigned, bool Fast, ASTContext &C);
 
 } // end analyze_format_string namespace
 } // end clang namespace
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 41f3c2e403cbef..c1e79b5d759ef3 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -442,6 +442,10 @@ class TargetInfo : public TransferrableTargetInfo,
   virtual IntType getLeastIntTypeByWidth(unsigned BitWidth,
                                          bool IsSigned) const;
 
+  /// Return the fastest integer type with at least the specified width.
+  virtual IntType getFastIntTypeByWidth(unsigned BitWidth,
+                                        bool IsSigned, bool Fast) const;
+
   /// Return floating point type with specified width. On PPC, there are
   /// three possible types for 128-bit floating point: "PPC double-double",
   /// IEEE 754R quad precision, and "long double" (which under the covers
diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp
index d4d9a0596620af..f41fba78b5d058 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -485,23 +485,27 @@ bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers(
 }
 
 ArgType clang::analyze_format_string::wToArgType(
-    int Size, bool Fast, ASTContext &C) {
-  ArgType FastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.LongLongTy : C.IntTy;
-  if (Size == 8) return C.CharTy;
-  if (Size == 16) return Fast? FastType : C.ShortTy;
-  if (Size == 32) return Fast? FastType : C.IntTy;
-  if (Size == 64) return C.LongLongTy;
-  return ArgType::Invalid();
-}
-
-ArgType clang::analyze_format_string::wToArgTypeUnsigned(
-    int Size, bool Fast, ASTContext &C) {
-  ArgType FastType = C.getTargetInfo().getTriple().isArch64Bit() ? C.UnsignedLongLongTy : C.UnsignedIntTy;
-  if (Size == 8) return C.UnsignedCharTy;
-  if (Size == 16) return Fast? FastType : C.UnsignedShortTy;
-  if (Size == 32) return Fast? FastType : C.UnsignedIntTy;
-  if (Size == 64) return C.UnsignedLongLongTy;
-  return ArgType::Invalid();
+    int Size, bool IsSigned, bool Fast, ASTContext &C) {
+  switch (C.getTargetInfo().getFastIntTypeByWidth(Size, IsSigned, Fast)) {
+    case TargetInfo::SignedChar:
+      return C.SignedCharTy;
+    case TargetInfo::UnsignedChar:
+      return C.UnsignedCharTy;
+    case TargetInfo::SignedShort:
+      return C.ShortTy;
+    case TargetInfo::UnsignedShort:
+      return C.UnsignedShortTy;
+    case TargetInfo::SignedInt:
+      return C.IntTy;
+    case TargetInfo::UnsignedInt:
+      return C.UnsignedIntTy;
+    case TargetInfo::SignedLongLong:
+      return C.LongLongTy;
+    case TargetInfo::UnsignedLongLong:
+      return C.UnsignedLongLongTy;
+    default:
+      return ArgType::Invalid();
+  }
 }
 
 //===----------------------------------------------------------------------===//
@@ -561,8 +565,8 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
       case LengthModifier::AsWide:
       case LengthModifier::AsWideFast:
         int S = getExplicitlyFixedSize();
-        bool FAST = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-        return clang::analyze_format_string::wToArgType(S, FAST, Ctx);
+        bool Fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+        return clang::analyze_format_string::wToArgType(S, true, Fast, Ctx);
     }
 
   if (CS.isUIntArg())
@@ -599,9 +603,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
         return ArgType::Invalid();
       case LengthModifier::AsWide:
       case LengthModifier::AsWideFast:
-        int s = getExplicitlyFixedSize();
-        bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-        return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);
+        int S = getExplicitlyFixedSize();
+        bool Fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+        return clang::analyze_format_string::wToArgType(S, false, Fast, Ctx);
     }
 
   if (CS.isDoubleArg()) {
diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp
index de261a303f322a..52bb1de117fe15 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -267,7 +267,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
         case LengthModifier::AsWideFast:
           int S = getExplicitlyFixedSize();
           bool FAST = LM.getKind() == LengthModifier::AsWideFast ? true : false;
-          return clang::analyze_format_string::wToArgType(S, FAST, Ctx);
+          return clang::analyze_format_string::wToArgType(S, true, FAST, Ctx);
       }
       llvm_unreachable("Unsupported LengthModifier Type");
 
@@ -311,11 +311,11 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
           return ArgType::Invalid();
         case LengthModifier::AsWide:
         case LengthModifier::AsWideFast:
-          int s = getExplicitlyFixedSize();
-          bool fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
+          int S = getExplicitlyFixedSize();
+          bool Fast = LM.getKind() == LengthModifier::AsWideFast ? true : false;
           if (CS.getKind() == ConversionSpecifier::uArg or CS.getKind() == ConversionSpecifier::UArg)
-            return clang::analyze_format_string::wToArgTypeUnsigned(s, fast, Ctx);
-          return clang::analyze_format_string::wToArgType(s, fast, Ctx);
+            return clang::analyze_format_string::wToArgType(S, false, Fast, Ctx);
+          return clang::analyze_format_string::wToArgType(S, true, Fast, Ctx);
       }
       llvm_unreachable("Unsupported LengthModifier Type");
 
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 6cd5d618a4acaa..fbc842d492ec38 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -312,6 +312,31 @@ TargetInfo::IntType TargetInfo::getLeastIntTypeByWidth(unsigned BitWidth,
   return NoInt;
 }
 
+TargetInfo::IntType TargetInfo::getFastIntTypeByWidth(unsigned BitWidth,
+                                                      bool IsSigned, bool Fast)
+                                                      const {
+  IntType SignedFastType = getTriple().isArch64Bit() ? SignedLongLong : SignedInt;
+  IntType UnSignedFastType = getTriple().isArch64Bit() ?
+                             UnsignedLongLong : UnsignedInt;
+  if (getCharWidth() == BitWidth)
+    return IsSigned ? SignedChar : UnsignedChar;
+  if (getShortWidth() == BitWidth) {
+    if (Fast)
+      return IsSigned ? SignedFastType : UnSignedFastType;
+    else
+      return IsSigned ? SignedShort : UnsignedShort;
+  }
+  if (getIntWidth() == BitWidth) {
+    if (Fast)
+      return IsSigned ? SignedFastType : UnSignedFastType;
+    else
+      return IsSigned ? SignedInt : UnsignedInt;
+  }
+  if (getLongLongWidth() == BitWidth)
+    return IsSigned ? SignedLongLong : UnsignedLongLong;
+  return NoInt;
+}
+
 FloatModeKind TargetInfo::getRealTypeByWidth(unsigned BitWidth,
                                              FloatModeKind ExplicitType) const {
   if (getHalfWidth() == BitWidth)
diff --git a/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c
index e6172ee436114e..89f267a0d4fcdf 100644
--- a/clang/test/Sema/format-strings-ms.c
+++ b/clang/test/Sema/format-strings-ms.c
@@ -93,11 +93,11 @@ void w_int_test(void) {
   int64_t d;
 
   // for %w
-  printf("%w8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int8_t' (aka 'signed char')}}
+  printf("%w8b", a);
   printf("%w16i", b);
   printf("%w32u", c);
   printf("%w64x", d);
-  scanf("%w8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int'}}
+  scanf("%w8b", a); // expected-warning{{format specifies type 'signed char' but the argument has type 'int'}}
   scanf("%w16i", b); // expected-warning{{format specifies type 'short' but the argument has type 'int'}}
   scanf("%w32u", c);
   scanf("%w64x", d);
@@ -114,11 +114,11 @@ void wf_test(void) {
   int_fast64_t d;
 
   // for %wf
-  printf("%wf8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int_fast8_t' (aka 'signed char')}}
+  printf("%wf8b", a);
   printf("%wf16u", b);
   printf("%wf32o", c);
   printf("%wf64X", d);
-  scanf("%wf8b", a); // expected-warning{{format specifies type 'char' but the argument has type 'int'}}
+  scanf("%wf8b", a); // expected-warning{{format specifies type 'signed char' but the argument has type 'int'}}
   scanf("%wf16u", b);
   scanf("%wf32o", c);
   scanf("%wf64X", d);



More information about the cfe-commits mailing list