[clang] cff8815 - [Clang] support C23 printf width length modifiers (#199991)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jun 6 00:08:52 PDT 2026
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)));
More information about the cfe-commits
mailing list