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
