Author: Oleksandr Tarasiuk
Date: 2026-06-06T10:08:47+03:00
New Revision: cff8815ebb23354ac506e6b299cf74cd60227163

URL: 
https://github.com/llvm/llvm-project/commit/cff8815ebb23354ac506e6b299cf74cd60227163
DIFF: 
https://github.com/llvm/llvm-project/commit/cff8815ebb23354ac506e6b299cf74cd60227163.diff

LOG: [Clang] support C23 printf width length modifiers (#199991)

This patch adds `-Wformat` support for the C23 `wN` and `wfN` length
modifiers in `printf`/`scanf` format strings. #116962

Added: 
    clang/test/Sema/format-strings-c23.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/ASTContext.h
    clang/include/clang/AST/FormatString.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/FormatString.cpp
    clang/lib/AST/PrintfFormatString.cpp
    clang/lib/AST/ScanfFormatString.cpp
    clang/lib/Sema/SemaChecking.cpp
    clang/test/Sema/format-strings.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf4826f50e5a5..bf917b3f642bc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -235,6 +235,7 @@ C2y Feature Support
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
 - Clang now allows C23 ``constexpr`` struct member access through the dot 
operator in constant expressions. (#GH178349)
+- Clang now supports the C23 ``wN`` and ``wfN`` length modifiers. (#GH116962)
 
 Objective-C Language Changes
 -----------------------------

diff  --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 54c046f5fab4a..6c53d6dbe9d2a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -948,6 +948,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType getIntTypeForBitwidth(unsigned DestWidth,
                                  unsigned Signed) const;
 
+  QualType getLeastIntTypeForBitwidth(unsigned DestWidth,
+                                      unsigned Signed) const;
+
   /// getRealTypeForBitwidth -
   /// sets floating point QualTy according to specified bitwidth.
   /// Returns empty type if there is no appropriate target types.

diff  --git a/clang/include/clang/AST/FormatString.h 
b/clang/include/clang/AST/FormatString.h
index a3382e1a1d007..75273651d3548 100644
--- a/clang/include/clang/AST/FormatString.h
+++ b/clang/include/clang/AST/FormatString.h
@@ -19,6 +19,7 @@
 #define LLVM_CLANG_AST_FORMATSTRING_H
 
 #include "clang/AST/CanonicalType.h"
+#include "llvm/ADT/StringRef.h"
 #include <optional>
 
 namespace clang {
@@ -80,6 +81,8 @@ class LengthModifier {
     AsInt3264,          // 'I'   (MSVCRT, like __int3264 from MIDL)
     AsInt64,            // 'I64' (MSVCRT, like __int64)
     AsLongDouble,       // 'L'
+    AsIntN,             // 'wN'
+    AsFastIntN,         // 'wfN'
     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
@@ -88,6 +91,8 @@ class LengthModifier {
 
   LengthModifier() : Position(nullptr), kind(None) {}
   LengthModifier(const char *pos, Kind k) : Position(pos), kind(k) {}
+  LengthModifier(const char *pos, Kind k, unsigned bitWidth, unsigned length)
+      : Position(pos), kind(k), BitWidth(bitWidth), ModifierLength(length) {}
 
   const char *getStart() const { return Position; }
 
@@ -98,6 +103,11 @@ class LengthModifier {
     case AsLongLong:
     case AsChar:
       return 2;
+    case AsIntN:
+    case AsFastIntN:
+      assert(ModifierLength != 0 &&
+             "C23 wN/wfN length modifiers must have a nonzero length");
+      return ModifierLength;
     case AsInt32:
     case AsInt64:
       return 3;
@@ -109,11 +119,15 @@ class LengthModifier {
   Kind getKind() const { return kind; }
   void setKind(Kind k) { kind = k; }
 
-  const char *toString() const;
+  unsigned getBitWidth() const { return BitWidth; }
+
+  StringRef toString() const;
 
 private:
   const char *Position;
   Kind kind;
+  unsigned BitWidth = 0;
+  unsigned ModifierLength = 0;
 };
 
 class ConversionSpecifier {
@@ -301,10 +315,18 @@ class ArgType {
   const char *Name = nullptr;
   bool Ptr = false;
 
-  /// The TypeKind identifies certain well-known types like size_t and
-  /// ptr
diff _t.
-  enum class TypeKind { DontCare, SizeT, Ptr
diff T };
+  /// The TypeKind identifies certain well-known types.
+  enum class TypeKind {
+    DontCare,
+    SizeT,
+    Ptr
diff T,
+    IntN,
+    UIntN,
+    FastIntN,
+    FastUIntN,
+  };
   TypeKind TK = TypeKind::DontCare;
+  unsigned BitWidth = 0;
 
 public:
   ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {}
@@ -341,6 +363,9 @@ class ArgType {
     return Res;
   }
 
+  static ArgType makeIntNType(ASTContext &Ctx, const LengthModifier &LengthMod,
+                              bool Signed);
+
   MatchKind matchesType(ASTContext &C, QualType argTy) const;
   MatchKind matchesArgType(ASTContext &C, const ArgType &other) const;
 

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index d7e2a0f9c4803..ae0a8167b31ad 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -13539,6 +13539,12 @@ QualType ASTContext::getIntTypeForBitwidth(unsigned 
DestWidth,
   return QualTy;
 }
 
+QualType ASTContext::getLeastIntTypeForBitwidth(unsigned DestWidth,
+                                                unsigned Signed) const {
+  return getFromTargetType(
+      getTargetInfo().getLeastIntTypeByWidth(DestWidth, Signed));
+}
+
 /// getRealTypeForBitwidth -
 /// sets floating point QualTy according to specified bitwidth.
 /// Returns empty type if there is no appropriate target types.

diff  --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp
index 7e1ac0de6dcaf..2163eacbe2ad7 100644
--- a/clang/lib/AST/FormatString.cpp
+++ b/clang/lib/AST/FormatString.cpp
@@ -14,6 +14,7 @@
 #include "FormatStringParsing.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/ConvertUTF.h"
 #include <optional>
 
@@ -59,6 +60,21 @@ OptionalAmount 
clang::analyze_format_string::ParseAmount(const char *&Beg,
   return OptionalAmount();
 }
 
+static bool ParseWidthModifier(const char *&I, const char *E,
+                               unsigned &BitWidth, unsigned &ModifierLength) {
+  StringRef W = StringRef(I, E - I).take_while(llvm::isDigit);
+  if (W.empty() || W.front() == '0')
+    return false;
+
+  if (W.getAsInteger(10, BitWidth))
+    return false;
+
+  I = W.end();
+  ModifierLength += W.size();
+
+  return true;
+}
+
 OptionalAmount clang::analyze_format_string::ParseNonPositionAmount(
     const char *&Beg, const char *E, unsigned &argIndex) {
   if (*Beg == '*') {
@@ -287,6 +303,25 @@ bool 
clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
     lmKind = LengthModifier::AsInt3264;
     break;
   case 'w':
+    if (LO.C23) {
+      const char *WidthModifier = I + 1;
+      unsigned BitWidth = 0;
+      unsigned ModifierLength = 1;
+
+      LengthModifier::Kind WidthKind = LengthModifier::AsIntN;
+      if (WidthModifier != E && *WidthModifier == 'f') {
+        WidthModifier = I + 2;
+        ModifierLength = 2;
+        WidthKind = LengthModifier::AsFastIntN;
+      }
+
+      if (ParseWidthModifier(WidthModifier, E, BitWidth, ModifierLength)) {
+        I = WidthModifier;
+        FS.setLengthModifier(
+            LengthModifier(lmPosition, WidthKind, BitWidth, ModifierLength));
+        return true;
+      }
+    }
     lmKind = LengthModifier::AsWide;
     ++I;
     break;
@@ -464,7 +499,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
   }
 
   case SpecificTy: {
-    if (TK != TypeKind::DontCare) {
+    if (TK == TypeKind::SizeT || TK == TypeKind::Ptr
diff T) {
       return matchesSizeTPtr
diff T(C, argTy, T);
     }
 
@@ -774,6 +809,22 @@ ArgType ArgType::makeVectorType(ASTContext &C, unsigned 
NumElts) const {
   return ArgType(Vec, Name);
 }
 
+ArgType ArgType::makeIntNType(ASTContext &Ctx, const LengthModifier &LengthMod,
+                              bool Signed) {
+  bool IsFast = LengthMod.getKind() == LengthModifier::AsFastIntN;
+  QualType Ty =
+      IsFast ? Ctx.getLeastIntTypeForBitwidth(LengthMod.getBitWidth(), Signed)
+             : Ctx.getIntTypeForBitwidth(LengthMod.getBitWidth(), Signed);
+  if (Ty.isNull())
+    return ArgType::Invalid();
+
+  ArgType Res(Ty);
+  Res.TK = IsFast ? (Signed ? TypeKind::FastIntN : TypeKind::FastUIntN)
+                  : (Signed ? TypeKind::IntN : TypeKind::UIntN);
+  Res.BitWidth = LengthMod.getBitWidth();
+  return Res;
+}
+
 QualType ArgType::getRepresentativeType(ASTContext &C) const {
   QualType Res;
   switch (K) {
@@ -820,6 +871,33 @@ std::string ArgType::getRepresentativeTypeName(ASTContext 
&C) const {
   if (Name) {
     // Use a specific name for this type, e.g. "size_t".
     Alias = Name;
+  } else {
+    const char *Prefix = nullptr;
+    switch (TK) {
+    case TypeKind::IntN:
+      Prefix = "int";
+      break;
+    case TypeKind::UIntN:
+      Prefix = "uint";
+      break;
+    case TypeKind::FastIntN:
+      Prefix = "int_fast";
+      break;
+    case TypeKind::FastUIntN:
+      Prefix = "uint_fast";
+      break;
+    case TypeKind::DontCare:
+    case TypeKind::SizeT:
+    case TypeKind::Ptr
diff T:
+      break;
+    }
+    if (Prefix) {
+      Alias = Prefix;
+      Alias += std::to_string(BitWidth);
+      Alias += "_t";
+    }
+  }
+  if (!Alias.empty()) {
     if (Ptr) {
       // If ArgType is actually a pointer to T, append an asterisk.
       Alias += (Alias[Alias.size() - 1] == '*') ? "*" : " *";
@@ -847,7 +925,7 @@ 
analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const {
 // Methods on LengthModifier.
 
//===----------------------------------------------------------------------===//
 
-const char *analyze_format_string::LengthModifier::toString() const {
+StringRef analyze_format_string::LengthModifier::toString() const {
   switch (kind) {
   case AsChar:
     return "hh";
@@ -875,6 +953,9 @@ const char 
*analyze_format_string::LengthModifier::toString() const {
     return "I64";
   case AsLongDouble:
     return "L";
+  case AsIntN:
+  case AsFastIntN:
+    return StringRef(Position, getLength());
   case AsAllocate:
     return "a";
   case AsMAllocate:
@@ -884,7 +965,7 @@ const char 
*analyze_format_string::LengthModifier::toString() const {
   case None:
     return "";
   }
-  return nullptr;
+  llvm_unreachable("Invalid LengthModifier Kind!");
 }
 
 
//===----------------------------------------------------------------------===//
@@ -1156,6 +1237,35 @@ bool FormatSpecifier::hasValidLengthModifier(const 
TargetInfo &Target,
       return false;
     }
 
+  case LengthModifier::AsIntN:
+  case LengthModifier::AsFastIntN: {
+    if (!LO.C23)
+      return false;
+
+    TargetInfo::IntType TargetType =
+        LM.getKind() == LengthModifier::AsIntN
+            ? Target.getIntTypeByWidth(LM.getBitWidth(), /*IsSigned=*/true)
+            : Target.getLeastIntTypeByWidth(LM.getBitWidth(),
+                                            /*IsSigned=*/true);
+    if (TargetType == TargetInfo::NoInt)
+      return false;
+
+    switch (CS.getKind()) {
+    case ConversionSpecifier::bArg:
+    case ConversionSpecifier::BArg:
+    case ConversionSpecifier::dArg:
+    case ConversionSpecifier::iArg:
+    case ConversionSpecifier::oArg:
+    case ConversionSpecifier::uArg:
+    case ConversionSpecifier::xArg:
+    case ConversionSpecifier::XArg:
+    case ConversionSpecifier::nArg:
+      return true;
+    default:
+      return false;
+    }
+  }
+
   case LengthModifier::AsAllocate:
     switch (CS.getKind()) {
     case ConversionSpecifier::sArg:
@@ -1217,6 +1327,8 @@ bool FormatSpecifier::hasStandardLengthModifier() const {
   case LengthModifier::AsSizeT:
   case LengthModifier::AsPtrDiff:
   case LengthModifier::AsLongDouble:
+  case LengthModifier::AsIntN:
+  case LengthModifier::AsFastIntN:
     return true;
   case LengthModifier::AsAllocate:
   case LengthModifier::AsMAllocate:

diff  --git a/clang/lib/AST/PrintfFormatString.cpp 
b/clang/lib/AST/PrintfFormatString.cpp
index 6610a2de9e083..6c325eba2a3f7 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -601,6 +601,12 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
     case LengthModifier::AsPtrDiff:
       return ArgType::makePtr
diff T(
           ArgType(Ctx.getPointerDiffType(), "ptr
diff _t"));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::makeIntNType(Ctx, LM,
+                                   CS.getKind() != ConversionSpecifier::bArg &&
+                                       CS.getKind() !=
+                                           ConversionSpecifier::BArg);
     case LengthModifier::AsAllocate:
     case LengthModifier::AsMAllocate:
     case LengthModifier::AsWide:
@@ -639,6 +645,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
     case LengthModifier::AsPtrDiff:
       return ArgType::makePtr
diff T(
           ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptr
diff _t"));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::makeIntNType(Ctx, LM, /*Signed=*/false);
     case LengthModifier::AsAllocate:
     case LengthModifier::AsMAllocate:
     case LengthModifier::AsWide:
@@ -684,6 +693,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx,
     case LengthModifier::AsPtrDiff:
       return ArgType::PtrTo(ArgType::makePtr
diff T(
           ArgType(Ctx.getPointerDiffType(), "ptr
diff _t")));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::PtrTo(ArgType::makeIntNType(Ctx, LM, /*Signed=*/true));
     case LengthModifier::AsLongDouble:
       return ArgType(); // FIXME: Is this a known extension?
     case LengthModifier::AsAllocate:

diff  --git a/clang/lib/AST/ScanfFormatString.cpp 
b/clang/lib/AST/ScanfFormatString.cpp
index 90cbbd60bbcf5..0af0c1458c76e 100644
--- a/clang/lib/AST/ScanfFormatString.cpp
+++ b/clang/lib/AST/ScanfFormatString.cpp
@@ -300,6 +300,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
     case LengthModifier::AsPtrDiff:
       return ArgType::PtrTo(ArgType::makePtr
diff T(
           ArgType(Ctx.getPointerDiffType(), "ptr
diff _t")));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::PtrTo(ArgType::makeIntNType(Ctx, LM, /*Signed=*/true));
     case LengthModifier::AsLongDouble:
       // GNU extension.
       return ArgType::PtrTo(Ctx.LongLongTy);
@@ -344,6 +347,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
     case LengthModifier::AsPtrDiff:
       return ArgType::PtrTo(ArgType::makePtr
diff T(
           ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptr
diff _t")));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::PtrTo(ArgType::makeIntNType(Ctx, LM, /*Signed=*/false));
     case LengthModifier::AsLongDouble:
       // GNU extension.
       return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
@@ -443,6 +449,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
     case LengthModifier::AsPtrDiff:
       return ArgType::PtrTo(ArgType::makePtr
diff T(
           ArgType(Ctx.getPointerDiffType(), "ptr
diff _t")));
+    case LengthModifier::AsIntN:
+    case LengthModifier::AsFastIntN:
+      return ArgType::PtrTo(ArgType::makeIntNType(Ctx, LM, /*Signed=*/true));
     case LengthModifier::AsLongDouble:
       return ArgType(); // FIXME: Is this a known extension?
     case LengthModifier::AsAllocate:

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 8a8c9cc9d2c23..324cd46556767 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8391,7 +8391,7 @@ class EquatableFormatArgument {
 
 private:
   analyze_format_string::ArgType ArgType;
-  analyze_format_string::LengthModifier::Kind LengthMod;
+  analyze_format_string::LengthModifier LengthMod;
   StringRef SpecifierLetter;
   CharSourceRange Range;
   SourceLocation ElementLoc;
@@ -8405,7 +8405,7 @@ class EquatableFormatArgument {
 
 public:
   EquatableFormatArgument(CharSourceRange Range, SourceLocation ElementLoc,
-                          analyze_format_string::LengthModifier::Kind 
LengthMod,
+                          analyze_format_string::LengthModifier LengthMod,
                           StringRef SpecifierLetter,
                           analyze_format_string::ArgType ArgType,
                           FormatArgumentRole Role,
@@ -8420,7 +8420,7 @@ class EquatableFormatArgument {
   SourceLocation getSourceLocation() const { return ElementLoc; }
   CharSourceRange getSourceRange() const { return Range; }
   analyze_format_string::LengthModifier getLengthModifier() const {
-    return analyze_format_string::LengthModifier(nullptr, LengthMod);
+    return LengthMod;
   }
   void setModifierFor(unsigned V) { ModifierFor = V; }
 
@@ -8768,7 +8768,7 @@ bool DecomposePrintfHandler::HandlePrintfSpecifier(
     Specs.emplace_back(
         getSpecifierRange(startSpecifier, specifierLen),
         getLocationOfByte(FieldWidth.getStart()),
-        analyze_format_string::LengthModifier::None, 
FieldWidth.getCharacters(),
+        analyze_format_string::LengthModifier(), FieldWidth.getCharacters(),
         FieldWidth.getArgType(S.Context),
         EquatableFormatArgument::FAR_FieldWidth,
         EquatableFormatArgument::SS_None,
@@ -8783,7 +8783,7 @@ bool DecomposePrintfHandler::HandlePrintfSpecifier(
     Specs.emplace_back(
         getSpecifierRange(startSpecifier, specifierLen),
         getLocationOfByte(Precision.getStart()),
-        analyze_format_string::LengthModifier::None, Precision.getCharacters(),
+        analyze_format_string::LengthModifier(), Precision.getCharacters(),
         Precision.getArgType(S.Context), 
EquatableFormatArgument::FAR_Precision,
         EquatableFormatArgument::SS_None,
         Precision.usesPositionalArg() ? Precision.getPositionalArgIndex() - 1
@@ -8811,7 +8811,7 @@ bool DecomposePrintfHandler::HandlePrintfSpecifier(
 
   Specs.emplace_back(
       getSpecifierRange(startSpecifier, specifierLen),
-      getLocationOfByte(CS.getStart()), FS.getLengthModifier().getKind(),
+      getLocationOfByte(CS.getStart()), FS.getLengthModifier(),
       CS.getCharacters(), FS.getArgType(S.Context, isObjCContext()),
       EquatableFormatArgument::FAR_Data, Sensitivity, SpecIndex, 0);
 
@@ -8820,7 +8820,7 @@ bool DecomposePrintfHandler::HandlePrintfSpecifier(
       CS.getKind() == analyze_format_string::ConversionSpecifier::FreeBSDDArg) 
{
     Specs.emplace_back(getSpecifierRange(startSpecifier, specifierLen),
                        getLocationOfByte(CS.getStart()),
-                       analyze_format_string::LengthModifier::None,
+                       analyze_format_string::LengthModifier(),
                        CS.getCharacters(),
                        analyze_format_string::ArgType::CStrTy,
                        EquatableFormatArgument::FAR_Auxiliary, Sensitivity,

diff  --git a/clang/test/Sema/format-strings-c23.c 
b/clang/test/Sema/format-strings-c23.c
new file mode 100644
index 0000000000000..86ddcaa67a828
--- /dev/null
+++ b/clang/test/Sema/format-strings-c23.c
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -ffreestanding 
-fsyntax-only -verify %s
+
+#include <stdint.h>
+
+int printf(const char *restrict, ...) __attribute__((format(printf, 1, 2)));
+int scanf(const char *restrict, ...) __attribute__((format(scanf, 1, 2)));
+
+void t1(int i, int8_t i8, uint8_t u8, int16_t i16, uint16_t u16, int32_t i32,
+        uint32_t u32, int64_t i64, uint64_t u64, int_fast8_t if8,
+        uint_fast8_t uf8, int_fast16_t if16, uint_fast16_t uf16,
+        int_fast32_t if32, uint_fast32_t uf32, int_fast64_t if64,
+        uint_fast64_t uf64) {
+  printf("%w8d", i8);
+  printf("%w8u", u8);
+  printf("%w16d", i16);
+  printf("%w16u", u16);
+  printf("%w32d", i32);
+  printf("%w32i", i32);
+  printf("%w32u", u32);
+  printf("%w32x", u32);
+  printf("%w32b", u32);
+  printf("%w64d", i64);
+  printf("%w64u", u64);
+  printf("%wf8d", if8);
+  printf("%wf8u", uf8);
+  printf("%wf16d", if16);
+  printf("%wf16u", uf16);
+  printf("%wf32d", if32);
+  printf("%wf32u", uf32);
+  printf("%wf32B", uf32);
+  printf("%wf64d", if64);
+  printf("%wf64u", uf64);
+
+  printf("%w32d", 1);
+  printf("%w32d", i);
+  printf("%w16d", 1);
+  printf("%w16d", 65536);
+  printf("%w16d", i);
+
+  printf("%w32d", 1.0);  // expected-warning{{format specifies type 'int32_t' 
(aka 'int') but the argument has type 'double'}}
+  printf("%w32u", 1.0);  // expected-warning{{format specifies type 'uint32_t' 
(aka 'unsigned int') but the argument has type 'double'}}
+  printf("%wf32d", 1.0); // expected-warning{{format specifies type 
'int_fast32_t' (aka 'int') but the argument has type 'double'}}
+  printf("%wf32u", 1.0); // expected-warning{{format specifies type 
'uint_fast32_t' (aka 'unsigned int') but the argument has type 'double'}}
+  printf("%w18446744073709551616d", i32); // expected-warning{{invalid 
conversion specifier '1'}}
+}
+
+void t2(int8_t *i8_ptr, int16_t *i16_ptr, int32_t *i32_ptr,
+        int64_t *i64_ptr, int_fast8_t *if8_ptr, int_fast16_t *if16_ptr,
+        int_fast32_t *if32_ptr, int_fast64_t *if64_ptr, double *d_ptr) {
+  printf("%w8n", i8_ptr);
+  printf("%w16n", i16_ptr);
+  printf("%w32n", i32_ptr);
+  printf("%w64n", i64_ptr);
+  printf("%wf8n", if8_ptr);
+  printf("%wf16n", if16_ptr);
+  printf("%wf32n", if32_ptr);
+  printf("%wf64n", if64_ptr);
+  printf("%w32n", d_ptr);  // expected-warning{{format specifies type 'int32_t 
*' (aka 'int *') but the argument has type 'double *'}}
+  printf("%wf32n", d_ptr); // expected-warning{{format specifies type 
'int_fast32_t *' (aka 'int *') but the argument has type 'double *'}}
+}
+
+void t3(int *int_ptr, int8_t *i8_ptr, uint8_t *u8_ptr, int16_t *i16_ptr,
+        uint16_t *u16_ptr, int32_t *i32_ptr, uint32_t *u32_ptr,
+        int64_t *i64_ptr, uint64_t *u64_ptr, int_fast8_t *if8_ptr,
+        uint_fast8_t *uf8_ptr, int_fast16_t *if16_ptr,
+        uint_fast16_t *uf16_ptr, int_fast32_t *if32_ptr,
+        uint_fast32_t *uf32_ptr, int_fast64_t *if64_ptr,
+        uint_fast64_t *uf64_ptr, double *d_ptr) {
+  scanf("%w32d", int_ptr);
+
+  scanf("%w8d", i8_ptr);
+  scanf("%w8u", u8_ptr);
+  scanf("%w16d", i16_ptr);
+  scanf("%w16u", u16_ptr);
+  scanf("%w32d", i32_ptr);
+  scanf("%w32i", i32_ptr);
+  scanf("%w32u", u32_ptr);
+  scanf("%w32x", u32_ptr);
+  scanf("%w32b", u32_ptr);
+  scanf("%w64d", i64_ptr);
+  scanf("%w64u", u64_ptr);
+  scanf("%wf8d", if8_ptr);
+  scanf("%wf8u", uf8_ptr);
+  scanf("%wf16d", if16_ptr);
+  scanf("%wf16u", uf16_ptr);
+  scanf("%wf32d", if32_ptr);
+  scanf("%wf32u", uf32_ptr);
+  scanf("%wf64d", if64_ptr);
+  scanf("%wf64u", uf64_ptr);
+
+  scanf("%w32d", d_ptr);   // expected-warning{{format specifies type 'int32_t 
*' (aka 'int *') but the argument has type 'double *'}}
+  scanf("%w32u", d_ptr);   // expected-warning{{format specifies type 
'uint32_t *' (aka 'unsigned int *') but the argument has type 'double *'}}
+  scanf("%wf32d", d_ptr);  // expected-warning{{format specifies type 
'int_fast32_t *' (aka 'int *') but the argument has type 'double *'}}
+  scanf("%wf32u", d_ptr);  // expected-warning{{format specifies type 
'uint_fast32_t *' (aka 'unsigned int *') but the argument has type 'double *'}}
+  scanf("%w16d", i32_ptr); // expected-warning{{format specifies type 'int16_t 
*' (aka 'short *') but the argument has type 'int32_t *' (aka 'int *')}}
+  scanf("%w32d", i16_ptr); // expected-warning{{format specifies type 'int32_t 
*' (aka 'int *') but the argument has type 'int16_t *' (aka 'short *')}}
+}
+
+void t4(const char *fmt) __attribute__((format_matches(printf, 1, "%w32d"))); 
// expected-note{{comparing with this specifier}}
+void t5(void) {
+  t4("%w32d");
+  t4("%w64d"); // expected-warning{{format specifier 'w64d' is incompatible 
with 'w32d'}}
+}

diff  --git a/clang/test/Sema/format-strings.c 
b/clang/test/Sema/format-strings.c
index bdb4466dc6ae8..59cf5562e91ef 100644
--- a/clang/test/Sema/format-strings.c
+++ b/clang/test/Sema/format-strings.c
@@ -790,6 +790,11 @@ void test_opencl_vector_format(int x) {
   printf("%hld", x); // expected-warning{{invalid conversion specifier 'l'}}
 }
 
+void test_int_width_modifiers(int x) {
+  printf("%w32d", x);    // expected-warning {{invalid conversion specifier 
'3'}}
+  printf("%wf32d", 1.0); // expected-warning {{length modifier 'w' results in 
undefined behavior or no effect with 'f' conversion specifier}}
+}
+
 // Test that we correctly merge the format in both orders.
 extern void test14_foo(const char *, const char *, ...)
      __attribute__((__format__(__printf__, 1, 3)));


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to