From f90aec49c43b405b76b60d9ff499e093b2feb86b Mon Sep 17 00:00:00 2001 From: Abhina Sreeskantharajan Date: Fri, 8 May 2026 12:19:11 -0400 Subject: [PATCH 1/5] Add format string handling --- .../utils/FormatStringConverter.cpp | 3 +- .../clang-tidy/utils/FormatStringConverter.h | 3 +- clang/include/clang/AST/FormatString.h | 15 ++- clang/include/clang/Lex/TextEncoding.h | 3 - clang/lib/AST/FormatString.cpp | 86 +++++++------ clang/lib/AST/FormatStringParsing.h | 36 ++++-- clang/lib/AST/OSLog.cpp | 3 +- clang/lib/AST/PrintfFormatString.cpp | 121 ++++++++++++------ clang/lib/AST/ScanfFormatString.cpp | 42 ++++-- clang/lib/Analysis/UnsafeBufferUsage.cpp | 3 +- clang/lib/Basic/TargetInfo.cpp | 1 + clang/lib/Sema/SemaChecking.cpp | 65 ++++++---- llvm/include/llvm/Support/TextEncoding.h | 10 ++ llvm/lib/Support/TextEncoding.cpp | 19 +++ 14 files changed, 269 insertions(+), 141 deletions(-) diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index a710c6ab728c2..73fc53d395266 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -622,7 +622,8 @@ bool FormatStringConverter::convertArgument(const PrintfSpecifier &FS, bool FormatStringConverter::HandlePrintfSpecifier(const PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, - const TargetInfo &Target) { + const TargetInfo &Target, + const llvm::TextEncodingConverter &) { const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data(); assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size()); diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h index e413b9f4c79f0..8c799e45cbd2b 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.h @@ -106,7 +106,8 @@ class FormatStringConverter bool HandlePrintfSpecifier(const PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, - const TargetInfo &Target) override; + const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) override; void appendFormatText(StringRef Text); void finalizeFormatText(); diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index 2c91e18491b1e..aa3ca93b3bcca 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -20,6 +20,7 @@ #include "clang/AST/CanonicalType.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/TextEncoding.h" #include namespace clang { @@ -764,14 +765,16 @@ class FormatStringHandler { virtual bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen) { + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) { return true; } virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const TargetInfo &Target) { + const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) { return true; } @@ -780,10 +783,10 @@ class FormatStringHandler { // Scanf-specific handlers. - virtual bool - HandleInvalidScanfConversionSpecifier(const analyze_scanf::ScanfSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) { + virtual bool HandleInvalidScanfConversionSpecifier( + const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) { return true; } diff --git a/clang/include/clang/Lex/TextEncoding.h b/clang/include/clang/Lex/TextEncoding.h index 1d4ac51bb764f..c1e6b7dad3948 100644 --- a/clang/include/clang/Lex/TextEncoding.h +++ b/clang/include/clang/Lex/TextEncoding.h @@ -30,9 +30,6 @@ class TextEncoding { std::unique_ptr FromLiteralEncodingConverter; // Only non-null on z/OS, where the system default encoding is IBM-1047. - // This converts UTF-8 to IBM-1047 for asm string literals so that - // octal/hex escape sequences are interpreted as IBM-1047 code points, - // regardless of -fexec-charset. std::unique_ptr ToIBM1047Converter; std::unique_ptr FromIBM1047Converter; diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index a7569701352b3..54c3ff18bdfe5 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -34,8 +34,9 @@ FormatStringHandler::~FormatStringHandler() {} // scanf format strings. //===----------------------------------------------------------------------===// -OptionalAmount clang::analyze_format_string::ParseAmount(const char *&Beg, - const char *E) { +OptionalAmount clang::analyze_format_string::ParseAmount( + const char *&Beg, const char *E, + const llvm::TextEncodingConverter &FormatStrConverter) { const char *I = Beg; UpdateOnReturn UpdateBeg(Beg, I); @@ -43,7 +44,7 @@ OptionalAmount clang::analyze_format_string::ParseAmount(const char *&Beg, bool hasDigits = false; for (; I != E; ++I) { - char c = *I; + char c = FormatStrConverter.convert(*I); if (c >= '0' && c <= '9') { hasDigits = true; accumulator = (accumulator * 10) + (c - '0'); @@ -76,21 +77,22 @@ static bool ParseWidthModifier(const char *&I, const char *E, } OptionalAmount clang::analyze_format_string::ParseNonPositionAmount( - const char *&Beg, const char *E, unsigned &argIndex) { - if (*Beg == '*') { + const char *&Beg, const char *E, unsigned &argIndex, + const llvm::TextEncodingConverter &FormatStrConverter) { + if (FormatStrConverter.convert(*Beg) == '*') { ++Beg; return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); } - return ParseAmount(Beg, E); + return ParseAmount(Beg, E, FormatStrConverter); } OptionalAmount clang::analyze_format_string::ParsePositionAmount( FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, - PositionContext p) { - if (*Beg == '*') { + PositionContext p, const llvm::TextEncodingConverter &FormatStrConverter) { + if (FormatStrConverter.convert(*Beg) == '*') { const char *I = Beg + 1; - const OptionalAmount &Amt = ParseAmount(I, E); + const OptionalAmount &Amt = ParseAmount(I, E, FormatStrConverter); if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { H.HandleInvalidPosition(Beg, I - Beg, p); @@ -105,7 +107,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( assert(Amt.getHowSpecified() == OptionalAmount::Constant); - if (*I == '$') { + if (FormatStrConverter.convert(*I) == '$') { // Handle positional arguments // Special case: '*0$', since this is an easy mistake. @@ -125,18 +127,21 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( return OptionalAmount(false); } - return ParseAmount(Beg, E); + return ParseAmount(Beg, E, FormatStrConverter); } bool clang::analyze_format_string::ParseFieldWidth( FormatStringHandler &H, FormatSpecifier &CS, const char *Start, - const char *&Beg, const char *E, unsigned *argIndex) { + const char *&Beg, const char *E, unsigned *argIndex, + const llvm::TextEncodingConverter &FormatStrConverter) { // FIXME: Support negative field widths. if (argIndex) { - CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); + CS.setFieldWidth( + ParseNonPositionAmount(Beg, E, *argIndex, FormatStrConverter)); } else { const OptionalAmount Amt = ParsePositionAmount( - H, Start, Beg, E, analyze_format_string::FieldWidthPos); + H, Start, Beg, E, analyze_format_string::FieldWidthPos, + FormatStrConverter); if (Amt.isInvalid()) return true; @@ -145,14 +150,13 @@ bool clang::analyze_format_string::ParseFieldWidth( return false; } -bool clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, - FormatSpecifier &FS, - const char *Start, - const char *&Beg, - const char *E) { +bool clang::analyze_format_string::ParseArgPosition( + FormatStringHandler &H, FormatSpecifier &FS, const char *Start, + const char *&Beg, const char *E, + const llvm::TextEncodingConverter &FormatStrConverter) { const char *I = Beg; - const OptionalAmount &Amt = ParseAmount(I, E); + const OptionalAmount &Amt = ParseAmount(I, E, FormatStrConverter); if (I == E) { // No more characters left? @@ -160,7 +164,8 @@ bool clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, return true; } - if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { + if (Amt.getHowSpecified() == OptionalAmount::Constant && + FormatStrConverter.convert(*(I++)) == '$') { // Warn that positional arguments are non-standard. H.HandlePosition(Start, I - Start); @@ -181,16 +186,15 @@ bool clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, return false; } -bool clang::analyze_format_string::ParseVectorModifier(FormatStringHandler &H, - FormatSpecifier &FS, - const char *&I, - const char *E, - const LangOptions &LO) { +bool clang::analyze_format_string::ParseVectorModifier( + FormatStringHandler &H, FormatSpecifier &FS, const char *&I, const char *E, + const LangOptions &LO, + const llvm::TextEncodingConverter &FormatStrConverter) { if (!LO.OpenCL) return false; const char *Start = I; - if (*I == 'v') { + if (FormatStrConverter.convert(*I) == 'v') { ++I; if (I == E) { @@ -198,7 +202,7 @@ bool clang::analyze_format_string::ParseVectorModifier(FormatStringHandler &H, return true; } - OptionalAmount NumElts = ParseAmount(I, E); + OptionalAmount NumElts = ParseAmount(I, E, FormatStrConverter); if (NumElts.getHowSpecified() != OptionalAmount::Constant) { H.HandleIncompleteSpecifier(Start, E - Start); return true; @@ -210,22 +214,20 @@ bool clang::analyze_format_string::ParseVectorModifier(FormatStringHandler &H, return false; } -bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, - const char *&I, - const char *E, - const LangOptions &LO, - bool IsScanf) { +bool clang::analyze_format_string::ParseLengthModifier( + FormatSpecifier &FS, const char *&I, const char *E, const LangOptions &LO, + const llvm::TextEncodingConverter &FormatStrConverter, bool IsScanf) { LengthModifier::Kind lmKind = LengthModifier::None; const char *lmPosition = I; - switch (*I) { + switch (FormatStrConverter.convert(*I)) { default: return false; case 'h': ++I; - if (I != E && *I == 'h') { + if (I != E && FormatStrConverter.convert(*I) == 'h') { ++I; lmKind = LengthModifier::AsChar; - } else if (I != E && *I == 'l' && LO.OpenCL) { + } else if (I != E && FormatStrConverter.convert(*I) == 'l' && LO.OpenCL) { ++I; lmKind = LengthModifier::AsShortLong; } else { @@ -234,7 +236,7 @@ bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, break; case 'l': ++I; - if (I != E && *I == 'l') { + if (I != E && FormatStrConverter.convert(*I) == 'l') { ++I; lmKind = LengthModifier::AsLongLong; } else { @@ -267,7 +269,9 @@ bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, // be parsed as the GNU extension 'a' length modifier. If not, this // will be parsed as a conversion specifier. ++I; - if (I != E && (*I == 's' || *I == 'S' || *I == '[')) { + if (I != E && (FormatStrConverter.convert(*I) == 's' || + FormatStrConverter.convert(*I) == 'S' || + FormatStrConverter.convert(*I) == '[')) { lmKind = LengthModifier::AsAllocate; break; } @@ -285,7 +289,8 @@ bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, // scanf: AsInt64 case 'I': if (I + 1 != E && I + 2 != E) { - if (I[1] == '6' && I[2] == '4') { + if (FormatStrConverter.convert(I[1]) == '6' && + FormatStrConverter.convert(I[2]) == '4') { I += 3; lmKind = LengthModifier::AsInt64; break; @@ -293,7 +298,8 @@ bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, if (IsScanf) return false; - if (I[1] == '3' && I[2] == '2') { + if (FormatStrConverter.convert(I[1]) == '3' && + FormatStrConverter.convert(I[2]) == '2') { I += 3; lmKind = LengthModifier::AsInt32; break; diff --git a/clang/lib/AST/FormatStringParsing.h b/clang/lib/AST/FormatStringParsing.h index 401528481a9d6..531bc291e0b5b 100644 --- a/clang/lib/AST/FormatStringParsing.h +++ b/clang/lib/AST/FormatStringParsing.h @@ -35,29 +35,43 @@ template class UpdateOnReturn { namespace analyze_format_string { -OptionalAmount ParseAmount(const char *&Beg, const char *E); -OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E, - unsigned &argIndex); +OptionalAmount +ParseAmount(const char *&Beg, const char *E, + const llvm::TextEncodingConverter &FormatStrConverter); -OptionalAmount ParsePositionAmount(FormatStringHandler &H, const char *Start, - const char *&Beg, const char *E, - PositionContext p); +OptionalAmount +ParseNonPositionAmount(const char *&Beg, const char *E, unsigned &argIndex, + const llvm::TextEncodingConverter &FormatStrConverter); + +OptionalAmount +ParsePositionAmount(FormatStringHandler &H, const char *Start, const char *&Beg, + const char *E, PositionContext p, + const llvm::TextEncodingConverter &FormatStrConverter); + +OptionalAmount +ParsePositionAmount(FormatStringHandler &H, const char *Start, const char *&Beg, + const char *E, PositionContext p, + const llvm::TextEncodingConverter &FormatStrConverter); bool ParseFieldWidth(FormatStringHandler &H, FormatSpecifier &CS, const char *Start, const char *&Beg, const char *E, - unsigned *argIndex); + unsigned *argIndex, + const llvm::TextEncodingConverter &FormatStrConverter); bool ParseArgPosition(FormatStringHandler &H, FormatSpecifier &CS, - const char *Start, const char *&Beg, const char *E); + const char *Start, const char *&Beg, const char *E, + const llvm::TextEncodingConverter &FormatStrConverter); bool ParseVectorModifier(FormatStringHandler &H, FormatSpecifier &FS, - const char *&Beg, const char *E, - const LangOptions &LO); + const char *&Beg, const char *E, const LangOptions &LO, + const llvm::TextEncodingConverter &FormatStrConverter); /// Returns true if a LengthModifier was parsed and installed in the /// FormatSpecifier& argument, and false otherwise. bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, - const LangOptions &LO, bool IsScanf = false); + const LangOptions &LO, + const llvm::TextEncodingConverter &FormatStrConverter, + bool IsScanf = false); /// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8 /// string; check that it won't go further than \p FmtStrEnd and write diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp index 91f8410e89e86..f112403add118 100644 --- a/clang/lib/AST/OSLog.cpp +++ b/clang/lib/AST/OSLog.cpp @@ -69,7 +69,8 @@ class OSLogFormatStringHandler bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, - const TargetInfo &) override { + const TargetInfo &, + const llvm::TextEncodingConverter &) override { if (!FS.consumesDataArgument() && FS.getConversionSpecifier().getKind() != clang::analyze_format_string::ConversionSpecifier::PrintErrno) diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index d09ed5e02dc02..478a2dc4acc69 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -15,6 +15,7 @@ #include "clang/AST/FormatString.h" #include "clang/AST/OSLog.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/Support/TextEncoding.h" #include "llvm/Support/Regex.h" using clang::analyze_format_string::ArgType; @@ -35,14 +36,17 @@ typedef clang::analyze_format_string::SpecifierResult using analyze_format_string::ParseNonPositionAmount; -static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, - const char *Start, const char *&Beg, const char *E, - unsigned *argIndex) { +static bool +ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, const char *Start, + const char *&Beg, const char *E, unsigned *argIndex, + const llvm::TextEncodingConverter &FormatStrConverter) { if (argIndex) { - FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex)); + FS.setPrecision( + ParseNonPositionAmount(Beg, E, *argIndex, FormatStrConverter)); } else { const OptionalAmount Amt = ParsePositionAmount( - H, Start, Beg, E, analyze_format_string::PrecisionPos); + H, Start, Beg, E, analyze_format_string::PrecisionPos, + FormatStrConverter); if (Amt.isInvalid()) return true; FS.setPrecision(Amt); @@ -50,11 +54,14 @@ static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, return false; } -static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, - const char *FlagBeg, const char *E, bool Warn) { +static bool +ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, const char *FlagBeg, + const char *E, bool Warn, + const llvm::TextEncodingConverter &FormatStrConverter) { StringRef Flag(FlagBeg, E - FlagBeg); // Currently there is only one flag. - if (Flag == "tt") { + if (Flag.size() == 2 && FormatStrConverter.convert(FlagBeg[0]) == 't' && + FormatStrConverter.convert(FlagBeg[1]) == 't') { FS.setHasObjCTechnicalTerm(FlagBeg); return false; } @@ -71,8 +78,9 @@ static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex, const LangOptions &LO, - const TargetInfo &Target, bool Warn, - bool isFreeBSDKPrintf) { + const TargetInfo &Target, + const llvm::TextEncodingConverter &FormatStrConverter, + bool Warn, bool isFreeBSDKPrintf) { using namespace clang::analyze_format_string; using namespace clang::analyze_printf; @@ -89,7 +97,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, H.HandleNullChar(I); return true; } - if (c == '%') { + if (FormatStrConverter.convert(c) == '%') { Start = I++; // Record the start of the format specifier. break; } @@ -107,7 +115,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } PrintfSpecifier FS; - if (ParseArgPosition(H, FS, Start, I, E)) + if (ParseArgPosition(H, FS, Start, I, E, FormatStrConverter)) return true; if (I == E) { @@ -117,13 +125,17 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } - if (*I == '{') { + if (FormatStrConverter.convert(*I) == '{') { ++I; unsigned char PrivacyFlags = 0; StringRef MatchedStr; do { - StringRef Str(I, E - I); + const char *II; + std::string S(I, E - I); + for (unsigned long i = 0; i < S.length(); ++i) + S[i] = FormatStrConverter.convert(S[i]); + StringRef Str(S); std::string Match = "^[[:space:]]*" "(private|public|sensitive|mask\\.[^[:space:],}]*)" "[[:space:]]*(,|})"; @@ -132,25 +144,38 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, if (R.match(Str, &Matches)) { MatchedStr = Matches[1]; + II = I; I += Matches[0].size(); + while (FormatStrConverter.convert(*II) == ' ') + ++II; + // Set the privacy flag if the privacy annotation in the // comma-delimited segment is at least as strict as the privacy // annotations in previous comma-delimited segments. if (MatchedStr.starts_with("mask")) { - StringRef MaskType = MatchedStr.substr(sizeof("mask.") - 1); + StringRef MaskType(II + sizeof("mask.") - 1, + MatchedStr.size() - sizeof("mask.") + 1); unsigned Size = MaskType.size(); + if (Warn && (Size == 0 || Size > 8)) H.handleInvalidMaskType(MaskType); FS.setMaskType(MaskType); - } else if (MatchedStr == "sensitive") + } else if (MatchedStr == "sensitive") { + StringRef ProxyMatchedStr(II, sizeof("sensitive") - 1); + MatchedStr = ProxyMatchedStr; PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsSensitive; - else if (PrivacyFlags != - clang::analyze_os_log::OSLogBufferItem::IsSensitive && - MatchedStr == "private") + } else if (PrivacyFlags != + clang::analyze_os_log::OSLogBufferItem::IsSensitive && + MatchedStr == "private") { + StringRef ProxyMatchedStr(II, sizeof("private") - 1); + MatchedStr = ProxyMatchedStr; PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPrivate; - else if (PrivacyFlags == 0 && MatchedStr == "public") + } else if (PrivacyFlags == 0 && MatchedStr == "public") { + StringRef ProxyMatchedStr(II, sizeof("public") - 1); + MatchedStr = ProxyMatchedStr; PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPublic; + } } else { size_t CommaOrBracePos = Str.find_if([](char c) { return c == ',' || c == '}'; }); @@ -165,7 +190,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, I += CommaOrBracePos + 1; } // Continue until the closing brace is found. - } while (*(I - 1) == ','); + } while (FormatStrConverter.convert(*(I - 1)) == ','); // Set the privacy flag. switch (PrivacyFlags) { @@ -188,7 +213,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // Look for flags (if any). bool hasMore = true; for (; I != E; ++I) { - switch (*I) { + switch (FormatStrConverter.convert(*I)) { default: hasMore = false; break; @@ -225,7 +250,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // Look for the field width (if any). if (ParseFieldWidth(H, FS, Start, I, E, - FS.usesPositionalArg() ? nullptr : &argIndex)) + FS.usesPositionalArg() ? nullptr : &argIndex, + FormatStrConverter)) return true; if (I == E) { @@ -236,7 +262,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } // Look for the precision (if any). - if (*I == '.') { + if (FormatStrConverter.convert(*I) == '.') { ++I; if (I == E) { if (Warn) @@ -245,7 +271,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } if (ParsePrecision(H, FS, Start, I, E, - FS.usesPositionalArg() ? nullptr : &argIndex)) + FS.usesPositionalArg() ? nullptr : &argIndex, + FormatStrConverter)) return true; if (I == E) { @@ -256,11 +283,11 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } } - if (ParseVectorModifier(H, FS, I, E, LO)) + if (ParseVectorModifier(H, FS, I, E, LO, FormatStrConverter)) return true; // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO) && I == E) { + if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter) && I == E) { // No more characters left? if (Warn) H.HandleIncompleteSpecifier(Start, E - Start); @@ -274,7 +301,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // enables better recovery, and we don't know if // these flags are applicable until later. const char *ObjCModifierFlagsStart = nullptr, *ObjCModifierFlagsEnd = nullptr; - if (*I == '[') { + if (FormatStrConverter.convert(*I) == '[') { ObjCModifierFlagsStart = I; ++I; auto flagStart = I; @@ -286,8 +313,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } // Did we find the closing ']'? - if (*I == ']') { - if (ParseObjCFlags(H, FS, flagStart, I, Warn)) + if (FormatStrConverter.convert(*I) == ']') { + if (ParseObjCFlags(H, FS, flagStart, I, Warn, FormatStrConverter)) return true; ++I; break; @@ -307,7 +334,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // Finally, look for the conversion specifier. const char *conversionPosition = I++; ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; - switch (*conversionPosition) { + switch (FormatStrConverter.convert(*conversionPosition)) { default: break; // C99: 7.19.6.1 (section 8). @@ -470,21 +497,36 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len); + return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len, + FormatStrConverter); } return PrintfSpecifierResult(Start, FS); } +// Creates a converter for format string character analysis. On z/OS format +// strings are IBM-1047 encoded, so we convert each byte to UTF-8 before +// comparing against ASCII specifier characters. On other targets a no-op +// converter is used. Created once per format string parse and passed through. +static llvm::TextEncodingConverter makeFormatStrConverter(const TargetInfo &T) { + auto MaybeConverter = + T.getTriple().isOSzOS() + ? llvm::TextEncodingConverter::create(llvm::TextEncoding::IBM1047, + llvm::TextEncoding::UTF8) + : llvm::TextEncodingConverter::createNoopConverter(); + assert(MaybeConverter && "Failed to create format string converter"); + return std::move(*MaybeConverter); +} + bool clang::analyze_format_string::ParsePrintfString( FormatStringHandler &H, const char *I, const char *E, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf) { unsigned argIndex = 0; - + llvm::TextEncodingConverter Conv = makeFormatStrConverter(Target); // Keep looking for a format specifier until we have exhausted the string. while (I != E) { const PrintfSpecifierResult &FSR = ParsePrintfSpecifier( - H, I, E, argIndex, LO, Target, true, isFreeBSDKPrintf); + H, I, E, argIndex, LO, Target, Conv, true, isFreeBSDKPrintf); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) @@ -495,7 +537,7 @@ bool clang::analyze_format_string::ParsePrintfString( continue; // We have a format specifier. Pass it to the callback. if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), - I - FSR.getStart(), Target)) + I - FSR.getStart(), Target, Conv)) return true; } assert(I == E && "Format string not exhausted"); @@ -507,12 +549,12 @@ bool clang::analyze_format_string::ParseFormatStringHasSArg( const TargetInfo &Target) { unsigned argIndex = 0; - + llvm::TextEncodingConverter Conv = makeFormatStrConverter(Target); // Keep looking for a %s format specifier until we have exhausted the string. FormatStringHandler H; while (I != E) { const PrintfSpecifierResult &FSR = - ParsePrintfSpecifier(H, I, E, argIndex, LO, Target, false, false); + ParsePrintfSpecifier(H, I, E, argIndex, LO, Target, Conv, false, false); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) @@ -534,11 +576,12 @@ bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers( const char *Begin, const char *End, const LangOptions &LO, const TargetInfo &Target) { unsigned ArgIndex = 0; + llvm::TextEncodingConverter Conv = makeFormatStrConverter(Target); // Keep looking for a formatting specifier until we have exhausted the string. FormatStringHandler H; while (Begin != End) { - const PrintfSpecifierResult &FSR = - ParsePrintfSpecifier(H, Begin, End, ArgIndex, LO, Target, false, false); + const PrintfSpecifierResult &FSR = ParsePrintfSpecifier( + H, Begin, End, ArgIndex, LO, Target, Conv, false, false); if (FSR.shouldStop()) break; if (FSR.hasValue()) diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 5e1622e95277b..76311c2113dd6 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -14,6 +14,7 @@ #include "FormatStringParsing.h" #include "clang/AST/FormatString.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/Support/TextEncoding.h" using clang::UpdateOnReturn; using clang::analyze_format_string::ArgType; @@ -71,17 +72,15 @@ static bool ParseScanList(FormatStringHandler &H, ScanfConversionSpecifier &CS, // FIXME: Much of this is copy-paste from ParsePrintfSpecifier. // We can possibly refactor. -static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, - const char *&Beg, const char *E, - unsigned &argIndex, - const LangOptions &LO, - const TargetInfo &Target) { +static ScanfSpecifierResult ParseScanfSpecifier( + FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex, + const LangOptions &LO, const TargetInfo &Target, + const llvm::TextEncodingConverter &FormatStrConverter) { using namespace clang::analyze_format_string; using namespace clang::analyze_scanf; const char *I = Beg; const char *Start = nullptr; UpdateOnReturn UpdateBeg(Beg, I); - // Look for a '%' character that indicates the start of a format specifier. for (; I != E; ++I) { char c = *I; @@ -90,7 +89,9 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, H.HandleNullChar(I); return true; } - if (c == '%') { + SmallString<1> ConvertedChar; + FormatStrConverter.convert(StringRef(&c, 1), ConvertedChar); + if (ConvertedChar[0] == '%') { Start = I++; // Record the start of the format specifier. break; } @@ -107,7 +108,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, } ScanfSpecifier FS; - if (ParseArgPosition(H, FS, Start, I, E)) + if (ParseArgPosition(H, FS, Start, I, E, FormatStrConverter)) return true; if (I == E) { @@ -117,7 +118,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, } // Look for '*' flag if it is present. - if (*I == '*') { + if (FormatStrConverter.convert(*I) == '*') { FS.setSuppressAssignment(I); if (++I == E) { H.HandleIncompleteSpecifier(Start, E - Start); @@ -127,7 +128,8 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, // Look for the field width (if any). Unlike printf, this is either // a fixed integer or isn't present. - const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E); + const OptionalAmount &Amt = + clang::analyze_format_string::ParseAmount(I, E, FormatStrConverter); if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { assert(Amt.getHowSpecified() == OptionalAmount::Constant); FS.setFieldWidth(Amt); @@ -140,7 +142,9 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, } // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO, /*IsScanf=*/true) && I == E) { + if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter, + /*IsScanf=*/true) && + I == E) { // No more characters left? H.HandleIncompleteSpecifier(Start, E - Start); return true; @@ -155,7 +159,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, // Finally, look for the conversion specifier. const char *conversionPosition = I++; ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier; - switch (*conversionPosition) { + switch (FormatStrConverter.convert(*conversionPosition)) { default: break; case '%': @@ -262,7 +266,8 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len); + return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len, + FormatStrConverter); } return ScanfSpecifierResult(Start, FS); } @@ -616,11 +621,20 @@ bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, const TargetInfo &Target) { unsigned argIndex = 0; + // On z/OS, format strings are IBM-1047 encoded. Create the converter once + // here and pass it through to avoid recreating it for every specifier. + auto MaybeConverter = + Target.getTriple().isOSzOS() + ? llvm::TextEncodingConverter::create(llvm::TextEncoding::IBM1047, + llvm::TextEncoding::UTF8) + : llvm::TextEncodingConverter::createNoopConverter(); + assert(MaybeConverter && "Failed to create format string converter"); + llvm::TextEncodingConverter Conv = std::move(*MaybeConverter); // Keep looking for a format specifier until we have exhausted the string. while (I != E) { const ScanfSpecifierResult &FSR = - ParseScanfSpecifier(H, I, E, argIndex, LO, Target); + ParseScanfSpecifier(H, I, E, argIndex, LO, Target, Conv); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 09f610358ca65..72dd33e31bd8e 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -1057,7 +1057,8 @@ hasUnsafeFormatOrSArg(ASTContext &Ctx, const CallExpr *Call, bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const TargetInfo &Target) override { + const TargetInfo &Target, + const llvm::TextEncodingConverter &) override { if (FS.getConversionSpecifier().getKind() != analyze_printf::PrintfConversionSpecifier::sArg) return true; // continue parsing diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 6e390226d2e4b..01ebd187d6848 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -201,6 +201,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { MaxOpenCLWorkGroupSize = 1024; MaxBitIntWidth.reset(); + } // Out of line virtual dtor for TargetInfo. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 406f03a17f7eb..7f94a9d0281c5 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -104,6 +104,7 @@ #include "llvm/Support/Locale.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/TextEncoding.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/RISCVTargetParser.h" #include "llvm/TargetParser/Triple.h" @@ -936,7 +937,8 @@ class EstimateSizeFormatHandler bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *, unsigned SpecifierLen, - const TargetInfo &) override { + const TargetInfo &, + const llvm::TextEncodingConverter &) override { const size_t FieldWidth = computeFieldWidth(FS); const size_t Precision = computePrecision(FS); @@ -8312,10 +8314,10 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { ArrayRef Fixit = {}); protected: - bool HandleInvalidConversionSpecifier(unsigned argIndex, SourceLocation Loc, - const char *startSpec, - unsigned specifierLen, - const char *csStart, unsigned csLen); + bool HandleInvalidConversionSpecifier( + unsigned argIndex, SourceLocation Loc, const char *startSpec, + unsigned specifierLen, const char *csStart, unsigned csLen, + const llvm::TextEncodingConverter &FormatStrConverter); void HandlePositionalNonpositionalArgs(SourceLocation Loc, const char *startSpec, @@ -8562,7 +8564,8 @@ void UncoveredArgHandler::Diagnose(Sema &S, bool IsFunctionCall, bool CheckFormatHandler::HandleInvalidConversionSpecifier( unsigned argIndex, SourceLocation Loc, const char *startSpec, - unsigned specifierLen, const char *csStart, unsigned csLen) { + unsigned specifierLen, const char *csStart, unsigned csLen, + const llvm::TextEncodingConverter &FormatStrConverter) { bool keepGoing = true; if (argIndex < NumDataArgs) { // Consider the argument coverered, even though the specifier doesn't @@ -8577,7 +8580,13 @@ bool CheckFormatHandler::HandleInvalidConversionSpecifier( keepGoing = false; } - StringRef Specifier(csStart, csLen); + // The csStart points to a character that has already been converted to the + // exec charset, so we have to reverse the conversion to allow diagnostic + // message to match an expected value when using -verify option, + std::string RS(csStart, csLen); + for (unsigned int i = 0; i < RS.size(); ++i) + RS[i] = FormatStrConverter.convert(RS[i]); + StringRef Specifier(RS); // If the specifier in non-printable, it could be the first byte of a UTF-8 // sequence. In that case, print the UTF-8 code point. If not, print the byte @@ -8731,13 +8740,15 @@ class CheckPrintfHandler : public CheckFormatHandler { bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen) override; + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) override; void handleInvalidMaskType(StringRef MaskType) override; bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const TargetInfo &Target) override; + const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) override; bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, const Expr *E); @@ -8864,20 +8875,22 @@ class DecomposePrintfHandler : public CheckPrintfHandler { virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const TargetInfo &Target) override; + const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) override; }; } // namespace bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen) { + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) { const analyze_printf::PrintfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength()); + specifierLen, CS.getStart(), CS.getLength(), FormatStrConverter); } void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) { @@ -9140,9 +9153,10 @@ bool DecomposePrintfHandler::GetSpecifiers( bool DecomposePrintfHandler::HandlePrintfSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen, const TargetInfo &Target) { + unsigned specifierLen, const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) { if (!CheckPrintfHandler::HandlePrintfSpecifier(FS, startSpecifier, - specifierLen, Target)) { + specifierLen, Target, Conv)) { HadError = true; return false; } @@ -9299,7 +9313,8 @@ bool CheckPrintfHandler::checkForCStrMembers( bool CheckPrintfHandler::HandlePrintfSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen, const TargetInfo &Target) { + unsigned specifierLen, const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) { using namespace analyze_format_string; using namespace analyze_printf; @@ -9386,14 +9401,14 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( // in a non-ObjC literal. if (!allowsObjCArg() && CS.isObjCArg()) { return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen); + specifierLen, Conv); } // %P can only be used with os_log. if (FSType != FormatStringType::OSLog && CS.getKind() == ConversionSpecifier::PArg) { return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen); + specifierLen, Conv); } // %n is not allowed with os_log. @@ -9413,7 +9428,7 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( CS.getKind() == ConversionSpecifier::sArg || CS.getKind() == ConversionSpecifier::ObjCObjArg)) { return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen); + specifierLen, Conv); } // Check for use of public/private annotation outside of os_log(). @@ -10089,10 +10104,10 @@ class CheckScanfHandler : public CheckFormatHandler { const char *startSpecifier, unsigned specifierLen) override; - bool - HandleInvalidScanfConversionSpecifier(const analyze_scanf::ScanfSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) override; + bool HandleInvalidScanfConversionSpecifier( + const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) override; void HandleIncompleteScanList(const char *start, const char *end) override; }; @@ -10108,13 +10123,15 @@ void CheckScanfHandler::HandleIncompleteScanList(const char *start, bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen) { + unsigned specifierLen, + const llvm::TextEncodingConverter &FormatStrConverter) { + const analyze_scanf::ScanfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength()); + specifierLen, CS.getStart(), CS.getLength(), FormatStrConverter); } bool CheckScanfHandler::HandleScanfSpecifier( diff --git a/llvm/include/llvm/Support/TextEncoding.h b/llvm/include/llvm/Support/TextEncoding.h index 6e7eb95464eff..734972b892ecc 100644 --- a/llvm/include/llvm/Support/TextEncoding.h +++ b/llvm/include/llvm/Support/TextEncoding.h @@ -104,6 +104,8 @@ class TextEncodingConverter { LLVM_ABI static ErrorOr create(StringRef From, StringRef To); + LLVM_ABI static ErrorOr createNoopConverter(); + TextEncodingConverter(const TextEncodingConverter &) = delete; TextEncodingConverter &operator=(const TextEncodingConverter &) = delete; @@ -134,6 +136,14 @@ class TextEncodingConverter { return std::string(Result); return EC; } + + char convert(char SingleChar) const { + SmallString<1> Result; + auto EC = Converter->convert(StringRef(&SingleChar, 1), Result); + if (!EC) + return Result[0]; + return '\0'; + } }; } // namespace llvm diff --git a/llvm/lib/Support/TextEncoding.cpp b/llvm/lib/Support/TextEncoding.cpp index c44bf78ea9fba..9672cba006a2c 100644 --- a/llvm/lib/Support/TextEncoding.cpp +++ b/llvm/lib/Support/TextEncoding.cpp @@ -357,3 +357,22 @@ ErrorOr TextEncodingConverter::create(StringRef From, return std::make_error_code(std::errc::invalid_argument); #endif } + +class TextEncodingConverterNoop final + : public details::TextEncodingConverterImplBase { + +public: + TextEncodingConverterNoop() {} + + std::error_code convertString(StringRef Source, + SmallVectorImpl &Result) override { + Result.assign(Source.begin(), Source.end()); + return std::error_code(); + } + + void reset() override {} +}; + +ErrorOr TextEncodingConverter::createNoopConverter() { + return TextEncodingConverter(std::make_unique()); +} From 6094107d460daf2d8cfce59829e954cdd8f18e3a Mon Sep 17 00:00:00 2001 From: Abhina Sreeskantharajan Date: Thu, 28 May 2026 15:16:49 -0400 Subject: [PATCH 2/5] do not convert character by character --- clang/include/clang/AST/FormatString.h | 4 +- clang/lib/AST/FormatString.cpp | 65 ++++++++++++++------------ clang/lib/AST/FormatStringParsing.h | 54 ++++++++++----------- clang/lib/AST/PrintfFormatString.cpp | 13 ++++-- clang/lib/AST/ScanfFormatString.cpp | 8 ++-- clang/lib/Sema/SemaChecking.cpp | 38 ++++++++------- 6 files changed, 96 insertions(+), 86 deletions(-) diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index aa3ca93b3bcca..ae0ccff75bb41 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -766,7 +766,7 @@ class FormatStringHandler { virtual bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { return true; } @@ -786,7 +786,7 @@ class FormatStringHandler { virtual bool HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { return true; } diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 54c3ff18bdfe5..e804aafa8a9e1 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -36,7 +36,7 @@ FormatStringHandler::~FormatStringHandler() {} OptionalAmount clang::analyze_format_string::ParseAmount( const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { const char *I = Beg; UpdateOnReturn UpdateBeg(Beg, I); @@ -44,7 +44,7 @@ OptionalAmount clang::analyze_format_string::ParseAmount( bool hasDigits = false; for (; I != E; ++I) { - char c = FormatStrConverter.convert(*I); + char c = FromSystemEncodingConverter.convert(*I); if (c >= '0' && c <= '9') { hasDigits = true; accumulator = (accumulator * 10) + (c - '0'); @@ -78,21 +78,22 @@ static bool ParseWidthModifier(const char *&I, const char *E, OptionalAmount clang::analyze_format_string::ParseNonPositionAmount( const char *&Beg, const char *E, unsigned &argIndex, - const llvm::TextEncodingConverter &FormatStrConverter) { - if (FormatStrConverter.convert(*Beg) == '*') { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + if (FromSystemEncodingConverter.convert(*Beg) == '*') { ++Beg; return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); } - return ParseAmount(Beg, E, FormatStrConverter); + return ParseAmount(Beg, E, FromSystemEncodingConverter); } OptionalAmount clang::analyze_format_string::ParsePositionAmount( FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, - PositionContext p, const llvm::TextEncodingConverter &FormatStrConverter) { - if (FormatStrConverter.convert(*Beg) == '*') { + PositionContext p, + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + if (FromSystemEncodingConverter.convert(*Beg) == '*') { const char *I = Beg + 1; - const OptionalAmount &Amt = ParseAmount(I, E, FormatStrConverter); + const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { H.HandleInvalidPosition(Beg, I - Beg, p); @@ -107,7 +108,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( assert(Amt.getHowSpecified() == OptionalAmount::Constant); - if (FormatStrConverter.convert(*I) == '$') { + if (FromSystemEncodingConverter.convert(*I) == '$') { // Handle positional arguments // Special case: '*0$', since this is an easy mistake. @@ -127,21 +128,21 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( return OptionalAmount(false); } - return ParseAmount(Beg, E, FormatStrConverter); + return ParseAmount(Beg, E, FromSystemEncodingConverter); } bool clang::analyze_format_string::ParseFieldWidth( FormatStringHandler &H, FormatSpecifier &CS, const char *Start, const char *&Beg, const char *E, unsigned *argIndex, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { // FIXME: Support negative field widths. if (argIndex) { CS.setFieldWidth( - ParseNonPositionAmount(Beg, E, *argIndex, FormatStrConverter)); + ParseNonPositionAmount(Beg, E, *argIndex, FromSystemEncodingConverter)); } else { const OptionalAmount Amt = ParsePositionAmount( H, Start, Beg, E, analyze_format_string::FieldWidthPos, - FormatStrConverter); + FromSystemEncodingConverter); if (Amt.isInvalid()) return true; @@ -153,10 +154,10 @@ bool clang::analyze_format_string::ParseFieldWidth( bool clang::analyze_format_string::ParseArgPosition( FormatStringHandler &H, FormatSpecifier &FS, const char *Start, const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { const char *I = Beg; - const OptionalAmount &Amt = ParseAmount(I, E, FormatStrConverter); + const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); if (I == E) { // No more characters left? @@ -165,7 +166,7 @@ bool clang::analyze_format_string::ParseArgPosition( } if (Amt.getHowSpecified() == OptionalAmount::Constant && - FormatStrConverter.convert(*(I++)) == '$') { + FromSystemEncodingConverter.convert(*(I++)) == '$') { // Warn that positional arguments are non-standard. H.HandlePosition(Start, I - Start); @@ -189,12 +190,12 @@ bool clang::analyze_format_string::ParseArgPosition( bool clang::analyze_format_string::ParseVectorModifier( FormatStringHandler &H, FormatSpecifier &FS, const char *&I, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { if (!LO.OpenCL) return false; const char *Start = I; - if (FormatStrConverter.convert(*I) == 'v') { + if (FromSystemEncodingConverter.convert(*I) == 'v') { ++I; if (I == E) { @@ -202,7 +203,7 @@ bool clang::analyze_format_string::ParseVectorModifier( return true; } - OptionalAmount NumElts = ParseAmount(I, E, FormatStrConverter); + OptionalAmount NumElts = ParseAmount(I, E, FromSystemEncodingConverter); if (NumElts.getHowSpecified() != OptionalAmount::Constant) { H.HandleIncompleteSpecifier(Start, E - Start); return true; @@ -216,18 +217,20 @@ bool clang::analyze_format_string::ParseVectorModifier( bool clang::analyze_format_string::ParseLengthModifier( FormatSpecifier &FS, const char *&I, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FormatStrConverter, bool IsScanf) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter, + bool IsScanf) { LengthModifier::Kind lmKind = LengthModifier::None; const char *lmPosition = I; - switch (FormatStrConverter.convert(*I)) { + switch (FromSystemEncodingConverter.convert(*I)) { default: return false; case 'h': ++I; - if (I != E && FormatStrConverter.convert(*I) == 'h') { + if (I != E && FromSystemEncodingConverter.convert(*I) == 'h') { ++I; lmKind = LengthModifier::AsChar; - } else if (I != E && FormatStrConverter.convert(*I) == 'l' && LO.OpenCL) { + } else if (I != E && FromSystemEncodingConverter.convert(*I) == 'l' && + LO.OpenCL) { ++I; lmKind = LengthModifier::AsShortLong; } else { @@ -236,7 +239,7 @@ bool clang::analyze_format_string::ParseLengthModifier( break; case 'l': ++I; - if (I != E && FormatStrConverter.convert(*I) == 'l') { + if (I != E && FromSystemEncodingConverter.convert(*I) == 'l') { ++I; lmKind = LengthModifier::AsLongLong; } else { @@ -269,9 +272,9 @@ bool clang::analyze_format_string::ParseLengthModifier( // be parsed as the GNU extension 'a' length modifier. If not, this // will be parsed as a conversion specifier. ++I; - if (I != E && (FormatStrConverter.convert(*I) == 's' || - FormatStrConverter.convert(*I) == 'S' || - FormatStrConverter.convert(*I) == '[')) { + if (I != E && (FromSystemEncodingConverter.convert(*I) == 's' || + FromSystemEncodingConverter.convert(*I) == 'S' || + FromSystemEncodingConverter.convert(*I) == '[')) { lmKind = LengthModifier::AsAllocate; break; } @@ -289,8 +292,8 @@ bool clang::analyze_format_string::ParseLengthModifier( // scanf: AsInt64 case 'I': if (I + 1 != E && I + 2 != E) { - if (FormatStrConverter.convert(I[1]) == '6' && - FormatStrConverter.convert(I[2]) == '4') { + if (FromSystemEncodingConverter.convert(I[1]) == '6' && + FromSystemEncodingConverter.convert(I[2]) == '4') { I += 3; lmKind = LengthModifier::AsInt64; break; @@ -298,8 +301,8 @@ bool clang::analyze_format_string::ParseLengthModifier( if (IsScanf) return false; - if (FormatStrConverter.convert(I[1]) == '3' && - FormatStrConverter.convert(I[2]) == '2') { + if (FromSystemEncodingConverter.convert(I[1]) == '3' && + FromSystemEncodingConverter.convert(I[2]) == '2') { I += 3; lmKind = LengthModifier::AsInt32; break; diff --git a/clang/lib/AST/FormatStringParsing.h b/clang/lib/AST/FormatStringParsing.h index 531bc291e0b5b..164efb9d847b9 100644 --- a/clang/lib/AST/FormatStringParsing.h +++ b/clang/lib/AST/FormatStringParsing.h @@ -37,41 +37,43 @@ namespace analyze_format_string { OptionalAmount ParseAmount(const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FormatStrConverter); + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -OptionalAmount -ParseNonPositionAmount(const char *&Beg, const char *E, unsigned &argIndex, - const llvm::TextEncodingConverter &FormatStrConverter); +OptionalAmount ParseNonPositionAmount( + const char *&Beg, const char *E, unsigned &argIndex, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -OptionalAmount -ParsePositionAmount(FormatStringHandler &H, const char *Start, const char *&Beg, - const char *E, PositionContext p, - const llvm::TextEncodingConverter &FormatStrConverter); +OptionalAmount ParsePositionAmount( + FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, + PositionContext p, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -OptionalAmount -ParsePositionAmount(FormatStringHandler &H, const char *Start, const char *&Beg, - const char *E, PositionContext p, - const llvm::TextEncodingConverter &FormatStrConverter); +OptionalAmount ParsePositionAmount( + FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, + PositionContext p, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -bool ParseFieldWidth(FormatStringHandler &H, FormatSpecifier &CS, - const char *Start, const char *&Beg, const char *E, - unsigned *argIndex, - const llvm::TextEncodingConverter &FormatStrConverter); +bool ParseFieldWidth( + FormatStringHandler &H, FormatSpecifier &CS, const char *Start, + const char *&Beg, const char *E, unsigned *argIndex, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -bool ParseArgPosition(FormatStringHandler &H, FormatSpecifier &CS, - const char *Start, const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FormatStrConverter); +bool ParseArgPosition( + FormatStringHandler &H, FormatSpecifier &CS, const char *Start, + const char *&Beg, const char *E, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); -bool ParseVectorModifier(FormatStringHandler &H, FormatSpecifier &FS, - const char *&Beg, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FormatStrConverter); +bool ParseVectorModifier( + FormatStringHandler &H, FormatSpecifier &FS, const char *&Beg, + const char *E, const LangOptions &LO, + const llvm::TextEncodingConverter &FromSystemEncodingConverter); /// Returns true if a LengthModifier was parsed and installed in the /// FormatSpecifier& argument, and false otherwise. -bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, - const LangOptions &LO, - const llvm::TextEncodingConverter &FormatStrConverter, - bool IsScanf = false); +bool ParseLengthModifier( + FormatSpecifier &FS, const char *&Beg, const char *E, const LangOptions &LO, + const llvm::TextEncodingConverter &FromSystemEncodingConverter, + bool IsScanf = false); /// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8 /// string; check that it won't go further than \p FmtStrEnd and write diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index 478a2dc4acc69..b37d9461f474a 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -60,7 +60,8 @@ ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, const char *FlagBeg, const llvm::TextEncodingConverter &FormatStrConverter) { StringRef Flag(FlagBeg, E - FlagBeg); // Currently there is only one flag. - if (Flag.size() == 2 && FormatStrConverter.convert(FlagBeg[0]) == 't' && + if (Flag.size() == 2 && + FormatStrConverter.convert(FlagBeg[0]) == 't' && FormatStrConverter.convert(FlagBeg[1]) == 't') { FS.setHasObjCTechnicalTerm(FlagBeg); return false; @@ -287,7 +288,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter) && I == E) { + if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter) && + I == E) { // No more characters left? if (Warn) H.HandleIncompleteSpecifier(Start, E - Start); @@ -314,7 +316,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } // Did we find the closing ']'? if (FormatStrConverter.convert(*I) == ']') { - if (ParseObjCFlags(H, FS, flagStart, I, Warn, FormatStrConverter)) + if (ParseObjCFlags(H, FS, flagStart, I, Warn, + FormatStrConverter)) return true; ++I; break; @@ -497,8 +500,8 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len, - FormatStrConverter); + return !H.HandleInvalidPrintfConversionSpecifier( + FS, Start, Len, FormatStrConverter); } return PrintfSpecifierResult(Start, FS); } diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 76311c2113dd6..18d7dabd5d6ad 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -128,8 +128,8 @@ static ScanfSpecifierResult ParseScanfSpecifier( // Look for the field width (if any). Unlike printf, this is either // a fixed integer or isn't present. - const OptionalAmount &Amt = - clang::analyze_format_string::ParseAmount(I, E, FormatStrConverter); + const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount( + I, E, FormatStrConverter); if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { assert(Amt.getHowSpecified() == OptionalAmount::Constant); FS.setFieldWidth(Amt); @@ -266,8 +266,8 @@ static ScanfSpecifierResult ParseScanfSpecifier( FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len, - FormatStrConverter); + return !H.HandleInvalidScanfConversionSpecifier( + FS, Beg, Len, FormatStrConverter); } return ScanfSpecifierResult(Start, FS); } diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 7f94a9d0281c5..4d3d458105126 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8317,7 +8317,7 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { bool HandleInvalidConversionSpecifier( unsigned argIndex, SourceLocation Loc, const char *startSpec, unsigned specifierLen, const char *csStart, unsigned csLen, - const llvm::TextEncodingConverter &FormatStrConverter); + const llvm::TextEncodingConverter &FromSystemEncodingConverter); void HandlePositionalNonpositionalArgs(SourceLocation Loc, const char *startSpec, @@ -8565,7 +8565,7 @@ void UncoveredArgHandler::Diagnose(Sema &S, bool IsFunctionCall, bool CheckFormatHandler::HandleInvalidConversionSpecifier( unsigned argIndex, SourceLocation Loc, const char *startSpec, unsigned specifierLen, const char *csStart, unsigned csLen, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { bool keepGoing = true; if (argIndex < NumDataArgs) { // Consider the argument coverered, even though the specifier doesn't @@ -8583,10 +8583,12 @@ bool CheckFormatHandler::HandleInvalidConversionSpecifier( // The csStart points to a character that has already been converted to the // exec charset, so we have to reverse the conversion to allow diagnostic // message to match an expected value when using -verify option, - std::string RS(csStart, csLen); - for (unsigned int i = 0; i < RS.size(); ++i) - RS[i] = FormatStrConverter.convert(RS[i]); - StringRef Specifier(RS); + SmallString<4> RS; + auto EC = FromSystemEncodingConverter.convert(StringRef(csStart, csLen), RS); + if (EC) { + keepGoing = false; + } + llvm::StringRef Specifier(RS); // If the specifier in non-printable, it could be the first byte of a UTF-8 // sequence. In that case, print the UTF-8 code point. If not, print the byte @@ -8741,7 +8743,7 @@ class CheckPrintfHandler : public CheckFormatHandler { bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) override; + const llvm::TextEncodingConverter &FromSystemEncodingConverter) override; void handleInvalidMaskType(StringRef MaskType) override; @@ -8884,13 +8886,13 @@ class DecomposePrintfHandler : public CheckPrintfHandler { bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { const analyze_printf::PrintfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength(), FormatStrConverter); + specifierLen, CS.getStart(), CS.getLength(), FromSystemEncodingConverter); } void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) { @@ -9400,15 +9402,15 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( // Check for using an Objective-C specific conversion specifier // in a non-ObjC literal. if (!allowsObjCArg() && CS.isObjCArg()) { - return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier( + FS, startSpecifier, specifierLen, Conv); } // %P can only be used with os_log. if (FSType != FormatStringType::OSLog && CS.getKind() == ConversionSpecifier::PArg) { - return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier( + FS, startSpecifier, specifierLen, Conv); } // %n is not allowed with os_log. @@ -9427,8 +9429,8 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( (CS.getKind() == ConversionSpecifier::PArg || CS.getKind() == ConversionSpecifier::sArg || CS.getKind() == ConversionSpecifier::ObjCObjArg)) { - return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, - specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier( + FS, startSpecifier, specifierLen, Conv); } // Check for use of public/private annotation outside of os_log(). @@ -10107,7 +10109,7 @@ class CheckScanfHandler : public CheckFormatHandler { bool HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) override; + const llvm::TextEncodingConverter &FromSystemEncodingConverter) override; void HandleIncompleteScanList(const char *start, const char *end) override; }; @@ -10124,14 +10126,14 @@ void CheckScanfHandler::HandleIncompleteScanList(const char *start, bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FormatStrConverter) { + const llvm::TextEncodingConverter &FromSystemEncodingConverter) { const analyze_scanf::ScanfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength(), FormatStrConverter); + specifierLen, CS.getStart(), CS.getLength(), FromSystemEncodingConverter); } bool CheckScanfHandler::HandleScanfSpecifier( From 396866bf137dae50271b3e787843704054868d0c Mon Sep 17 00:00:00 2001 From: Abhina Sreeskantharajan Date: Fri, 17 Jul 2026 14:23:44 -0400 Subject: [PATCH 3/5] rename char conversion function to convertBasicChar --- clang/lib/AST/FormatString.cpp | 35 ++++++++++++------------ clang/lib/AST/PrintfFormatString.cpp | 24 ++++++++-------- clang/lib/AST/ScanfFormatString.cpp | 8 ++---- llvm/include/llvm/Support/TextEncoding.h | 4 ++- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index e804aafa8a9e1..845fb09ade998 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -44,7 +44,7 @@ OptionalAmount clang::analyze_format_string::ParseAmount( bool hasDigits = false; for (; I != E; ++I) { - char c = FromSystemEncodingConverter.convert(*I); + char c = FromSystemEncodingConverter.convertBasicChar(*I); if (c >= '0' && c <= '9') { hasDigits = true; accumulator = (accumulator * 10) + (c - '0'); @@ -79,7 +79,7 @@ static bool ParseWidthModifier(const char *&I, const char *E, OptionalAmount clang::analyze_format_string::ParseNonPositionAmount( const char *&Beg, const char *E, unsigned &argIndex, const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convert(*Beg) == '*') { + if (FromSystemEncodingConverter.convertBasicChar(*Beg) == '*') { ++Beg; return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); } @@ -91,7 +91,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, PositionContext p, const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convert(*Beg) == '*') { + if (FromSystemEncodingConverter.convertBasicChar(*Beg) == '*') { const char *I = Beg + 1; const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); @@ -108,7 +108,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( assert(Amt.getHowSpecified() == OptionalAmount::Constant); - if (FromSystemEncodingConverter.convert(*I) == '$') { + if (FromSystemEncodingConverter.convertBasicChar(*I) == '$') { // Handle positional arguments // Special case: '*0$', since this is an easy mistake. @@ -166,7 +166,7 @@ bool clang::analyze_format_string::ParseArgPosition( } if (Amt.getHowSpecified() == OptionalAmount::Constant && - FromSystemEncodingConverter.convert(*(I++)) == '$') { + FromSystemEncodingConverter.convertBasicChar(*(I++)) == '$') { // Warn that positional arguments are non-standard. H.HandlePosition(Start, I - Start); @@ -195,7 +195,7 @@ bool clang::analyze_format_string::ParseVectorModifier( return false; const char *Start = I; - if (FromSystemEncodingConverter.convert(*I) == 'v') { + if (FromSystemEncodingConverter.convertBasicChar(*I) == 'v') { ++I; if (I == E) { @@ -221,15 +221,16 @@ bool clang::analyze_format_string::ParseLengthModifier( bool IsScanf) { LengthModifier::Kind lmKind = LengthModifier::None; const char *lmPosition = I; - switch (FromSystemEncodingConverter.convert(*I)) { + switch (FromSystemEncodingConverter.convertBasicChar(*I)) { default: return false; case 'h': ++I; - if (I != E && FromSystemEncodingConverter.convert(*I) == 'h') { + if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == 'h') { ++I; lmKind = LengthModifier::AsChar; - } else if (I != E && FromSystemEncodingConverter.convert(*I) == 'l' && + } else if (I != E && + FromSystemEncodingConverter.convertBasicChar(*I) == 'l' && LO.OpenCL) { ++I; lmKind = LengthModifier::AsShortLong; @@ -239,7 +240,7 @@ bool clang::analyze_format_string::ParseLengthModifier( break; case 'l': ++I; - if (I != E && FromSystemEncodingConverter.convert(*I) == 'l') { + if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == 'l') { ++I; lmKind = LengthModifier::AsLongLong; } else { @@ -272,9 +273,9 @@ bool clang::analyze_format_string::ParseLengthModifier( // be parsed as the GNU extension 'a' length modifier. If not, this // will be parsed as a conversion specifier. ++I; - if (I != E && (FromSystemEncodingConverter.convert(*I) == 's' || - FromSystemEncodingConverter.convert(*I) == 'S' || - FromSystemEncodingConverter.convert(*I) == '[')) { + if (I != E && (FromSystemEncodingConverter.convertBasicChar(*I) == 's' || + FromSystemEncodingConverter.convertBasicChar(*I) == 'S' || + FromSystemEncodingConverter.convertBasicChar(*I) == '[')) { lmKind = LengthModifier::AsAllocate; break; } @@ -292,8 +293,8 @@ bool clang::analyze_format_string::ParseLengthModifier( // scanf: AsInt64 case 'I': if (I + 1 != E && I + 2 != E) { - if (FromSystemEncodingConverter.convert(I[1]) == '6' && - FromSystemEncodingConverter.convert(I[2]) == '4') { + if (FromSystemEncodingConverter.convertBasicChar(I[1]) == '6' && + FromSystemEncodingConverter.convertBasicChar(I[2]) == '4') { I += 3; lmKind = LengthModifier::AsInt64; break; @@ -301,8 +302,8 @@ bool clang::analyze_format_string::ParseLengthModifier( if (IsScanf) return false; - if (FromSystemEncodingConverter.convert(I[1]) == '3' && - FromSystemEncodingConverter.convert(I[2]) == '2') { + if (FromSystemEncodingConverter.convertBasicChar(I[1]) == '3' && + FromSystemEncodingConverter.convertBasicChar(I[2]) == '2') { I += 3; lmKind = LengthModifier::AsInt32; break; diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index b37d9461f474a..25657c442a747 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -61,8 +61,8 @@ ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, const char *FlagBeg, StringRef Flag(FlagBeg, E - FlagBeg); // Currently there is only one flag. if (Flag.size() == 2 && - FormatStrConverter.convert(FlagBeg[0]) == 't' && - FormatStrConverter.convert(FlagBeg[1]) == 't') { + FormatStrConverter.convertBasicChar(FlagBeg[0]) == 't' && + FormatStrConverter.convertBasicChar(FlagBeg[1]) == 't') { FS.setHasObjCTechnicalTerm(FlagBeg); return false; } @@ -98,7 +98,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, H.HandleNullChar(I); return true; } - if (FormatStrConverter.convert(c) == '%') { + if (FormatStrConverter.convertBasicChar(c) == '%') { Start = I++; // Record the start of the format specifier. break; } @@ -126,7 +126,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } - if (FormatStrConverter.convert(*I) == '{') { + if (FormatStrConverter.convertBasicChar(*I) == '{') { ++I; unsigned char PrivacyFlags = 0; StringRef MatchedStr; @@ -135,7 +135,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, const char *II; std::string S(I, E - I); for (unsigned long i = 0; i < S.length(); ++i) - S[i] = FormatStrConverter.convert(S[i]); + S[i] = FormatStrConverter.convertBasicChar(S[i]); StringRef Str(S); std::string Match = "^[[:space:]]*" "(private|public|sensitive|mask\\.[^[:space:],}]*)" @@ -148,7 +148,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, II = I; I += Matches[0].size(); - while (FormatStrConverter.convert(*II) == ' ') + while (FormatStrConverter.convertBasicChar(*II) == ' ') ++II; // Set the privacy flag if the privacy annotation in the @@ -191,7 +191,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, I += CommaOrBracePos + 1; } // Continue until the closing brace is found. - } while (FormatStrConverter.convert(*(I - 1)) == ','); + } while (FormatStrConverter.convertBasicChar(*(I - 1)) == ','); // Set the privacy flag. switch (PrivacyFlags) { @@ -214,7 +214,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // Look for flags (if any). bool hasMore = true; for (; I != E; ++I) { - switch (FormatStrConverter.convert(*I)) { + switch (FormatStrConverter.convertBasicChar(*I)) { default: hasMore = false; break; @@ -263,7 +263,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } // Look for the precision (if any). - if (FormatStrConverter.convert(*I) == '.') { + if (FormatStrConverter.convertBasicChar(*I) == '.') { ++I; if (I == E) { if (Warn) @@ -303,7 +303,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // enables better recovery, and we don't know if // these flags are applicable until later. const char *ObjCModifierFlagsStart = nullptr, *ObjCModifierFlagsEnd = nullptr; - if (FormatStrConverter.convert(*I) == '[') { + if (FormatStrConverter.convertBasicChar(*I) == '[') { ObjCModifierFlagsStart = I; ++I; auto flagStart = I; @@ -315,7 +315,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } // Did we find the closing ']'? - if (FormatStrConverter.convert(*I) == ']') { + if (FormatStrConverter.convertBasicChar(*I) == ']') { if (ParseObjCFlags(H, FS, flagStart, I, Warn, FormatStrConverter)) return true; @@ -337,7 +337,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // Finally, look for the conversion specifier. const char *conversionPosition = I++; ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; - switch (FormatStrConverter.convert(*conversionPosition)) { + switch (FormatStrConverter.convertBasicChar(*conversionPosition)) { default: break; // C99: 7.19.6.1 (section 8). diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 18d7dabd5d6ad..592bc3ad220e8 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -89,9 +89,7 @@ static ScanfSpecifierResult ParseScanfSpecifier( H.HandleNullChar(I); return true; } - SmallString<1> ConvertedChar; - FormatStrConverter.convert(StringRef(&c, 1), ConvertedChar); - if (ConvertedChar[0] == '%') { + if (FormatStrConverter.convertBasicChar(c) == '%') { Start = I++; // Record the start of the format specifier. break; } @@ -118,7 +116,7 @@ static ScanfSpecifierResult ParseScanfSpecifier( } // Look for '*' flag if it is present. - if (FormatStrConverter.convert(*I) == '*') { + if (FormatStrConverter.convertBasicChar(*I) == '*') { FS.setSuppressAssignment(I); if (++I == E) { H.HandleIncompleteSpecifier(Start, E - Start); @@ -159,7 +157,7 @@ static ScanfSpecifierResult ParseScanfSpecifier( // Finally, look for the conversion specifier. const char *conversionPosition = I++; ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier; - switch (FormatStrConverter.convert(*conversionPosition)) { + switch (FormatStrConverter.convertBasicChar(*conversionPosition)) { default: break; case '%': diff --git a/llvm/include/llvm/Support/TextEncoding.h b/llvm/include/llvm/Support/TextEncoding.h index 734972b892ecc..80709e96be04b 100644 --- a/llvm/include/llvm/Support/TextEncoding.h +++ b/llvm/include/llvm/Support/TextEncoding.h @@ -137,7 +137,9 @@ class TextEncodingConverter { return EC; } - char convert(char SingleChar) const { + // This method is used in format string handling and is only intended + // to support basic charsets, not multibyte characters. + char convertBasicChar(char SingleChar) const { SmallString<1> Result; auto EC = Converter->convert(StringRef(&SingleChar, 1), Result); if (!EC) From 7d2df4875244b052b02791bd8c84e11088b8ac5c Mon Sep 17 00:00:00 2001 From: Abhina Sreeskantharajan Date: Wed, 22 Jul 2026 14:49:20 -0400 Subject: [PATCH 4/5] change char literals to u8'' to preserve behaviour in the case the compiler is compiled with a different fexec-charset --- clang/lib/AST/FormatString.cpp | 55 ++++++++--------- clang/lib/AST/PrintfFormatString.cpp | 88 ++++++++++++++-------------- clang/lib/AST/ScanfFormatString.cpp | 56 +++++++++--------- 3 files changed, 100 insertions(+), 99 deletions(-) diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 845fb09ade998..b67de85f75467 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -45,9 +45,9 @@ OptionalAmount clang::analyze_format_string::ParseAmount( for (; I != E; ++I) { char c = FromSystemEncodingConverter.convertBasicChar(*I); - if (c >= '0' && c <= '9') { + if (c >= u8'0' && c <= u8'9') { hasDigits = true; - accumulator = (accumulator * 10) + (c - '0'); + accumulator = (accumulator * 10) + (c - u8'0'); continue; } @@ -79,7 +79,7 @@ static bool ParseWidthModifier(const char *&I, const char *E, OptionalAmount clang::analyze_format_string::ParseNonPositionAmount( const char *&Beg, const char *E, unsigned &argIndex, const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convertBasicChar(*Beg) == '*') { + if (FromSystemEncodingConverter.convertBasicChar(*Beg) == u8'*') { ++Beg; return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); } @@ -91,7 +91,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, PositionContext p, const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convertBasicChar(*Beg) == '*') { + if (FromSystemEncodingConverter.convertBasicChar(*Beg) == u8'*') { const char *I = Beg + 1; const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); @@ -108,7 +108,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( assert(Amt.getHowSpecified() == OptionalAmount::Constant); - if (FromSystemEncodingConverter.convertBasicChar(*I) == '$') { + if (FromSystemEncodingConverter.convertBasicChar(*I) == u8'$') { // Handle positional arguments // Special case: '*0$', since this is an easy mistake. @@ -166,7 +166,7 @@ bool clang::analyze_format_string::ParseArgPosition( } if (Amt.getHowSpecified() == OptionalAmount::Constant && - FromSystemEncodingConverter.convertBasicChar(*(I++)) == '$') { + FromSystemEncodingConverter.convertBasicChar(*(I++)) == u8'$') { // Warn that positional arguments are non-standard. H.HandlePosition(Start, I - Start); @@ -195,7 +195,7 @@ bool clang::analyze_format_string::ParseVectorModifier( return false; const char *Start = I; - if (FromSystemEncodingConverter.convertBasicChar(*I) == 'v') { + if (FromSystemEncodingConverter.convertBasicChar(*I) == u8'v') { ++I; if (I == E) { @@ -224,13 +224,13 @@ bool clang::analyze_format_string::ParseLengthModifier( switch (FromSystemEncodingConverter.convertBasicChar(*I)) { default: return false; - case 'h': + case u8'h': ++I; - if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == 'h') { + if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == u8'h') { ++I; lmKind = LengthModifier::AsChar; } else if (I != E && - FromSystemEncodingConverter.convertBasicChar(*I) == 'l' && + FromSystemEncodingConverter.convertBasicChar(*I) == u8'l' && LO.OpenCL) { ++I; lmKind = LengthModifier::AsShortLong; @@ -238,51 +238,52 @@ bool clang::analyze_format_string::ParseLengthModifier( lmKind = LengthModifier::AsShort; } break; - case 'l': + case u8'l': ++I; - if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == 'l') { + if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == u8'l') { ++I; lmKind = LengthModifier::AsLongLong; } else { lmKind = LengthModifier::AsLong; } break; - case 'j': + case u8'j': lmKind = LengthModifier::AsIntMax; ++I; break; - case 'z': + case u8'z': lmKind = LengthModifier::AsSizeT; ++I; break; - case 't': + case u8't': lmKind = LengthModifier::AsPtrDiff; ++I; break; - case 'L': + case u8'L': lmKind = LengthModifier::AsLongDouble; ++I; break; - case 'q': + case u8'q': lmKind = LengthModifier::AsQuad; ++I; break; - case 'a': + case u8'a': if (IsScanf && !LO.C99 && !LO.CPlusPlus11) { // For scanf in C90, look at the next character to see if this should // be parsed as the GNU extension 'a' length modifier. If not, this // will be parsed as a conversion specifier. ++I; - if (I != E && (FromSystemEncodingConverter.convertBasicChar(*I) == 's' || - FromSystemEncodingConverter.convertBasicChar(*I) == 'S' || - FromSystemEncodingConverter.convertBasicChar(*I) == '[')) { + if (I != E && + (FromSystemEncodingConverter.convertBasicChar(*I) == u8's' || + FromSystemEncodingConverter.convertBasicChar(*I) == u8'S' || + FromSystemEncodingConverter.convertBasicChar(*I) == u8'[')) { lmKind = LengthModifier::AsAllocate; break; } --I; } return false; - case 'm': + case u8'm': if (IsScanf) { lmKind = LengthModifier::AsMAllocate; ++I; @@ -291,10 +292,10 @@ bool clang::analyze_format_string::ParseLengthModifier( return false; // printf: AsInt64, AsInt32, AsInt3264 // scanf: AsInt64 - case 'I': + case u8'I': if (I + 1 != E && I + 2 != E) { - if (FromSystemEncodingConverter.convertBasicChar(I[1]) == '6' && - FromSystemEncodingConverter.convertBasicChar(I[2]) == '4') { + if (FromSystemEncodingConverter.convertBasicChar(I[1]) == u8'6' && + FromSystemEncodingConverter.convertBasicChar(I[2]) == u8'4') { I += 3; lmKind = LengthModifier::AsInt64; break; @@ -302,8 +303,8 @@ bool clang::analyze_format_string::ParseLengthModifier( if (IsScanf) return false; - if (FromSystemEncodingConverter.convertBasicChar(I[1]) == '3' && - FromSystemEncodingConverter.convertBasicChar(I[2]) == '2') { + if (FromSystemEncodingConverter.convertBasicChar(I[1]) == u8'3' && + FromSystemEncodingConverter.convertBasicChar(I[2]) == u8'2') { I += 3; lmKind = LengthModifier::AsInt32; break; diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index 25657c442a747..c8da30f98676b 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -61,8 +61,8 @@ ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS, const char *FlagBeg, StringRef Flag(FlagBeg, E - FlagBeg); // Currently there is only one flag. if (Flag.size() == 2 && - FormatStrConverter.convertBasicChar(FlagBeg[0]) == 't' && - FormatStrConverter.convertBasicChar(FlagBeg[1]) == 't') { + FormatStrConverter.convertBasicChar(FlagBeg[0]) == u8't' && + FormatStrConverter.convertBasicChar(FlagBeg[1]) == u8't') { FS.setHasObjCTechnicalTerm(FlagBeg); return false; } @@ -98,7 +98,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, H.HandleNullChar(I); return true; } - if (FormatStrConverter.convertBasicChar(c) == '%') { + if (FormatStrConverter.convertBasicChar(c) == u8'%') { Start = I++; // Record the start of the format specifier. break; } @@ -126,7 +126,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } - if (FormatStrConverter.convertBasicChar(*I) == '{') { + if (FormatStrConverter.convertBasicChar(*I) == u8'{') { ++I; unsigned char PrivacyFlags = 0; StringRef MatchedStr; @@ -148,7 +148,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, II = I; I += Matches[0].size(); - while (FormatStrConverter.convertBasicChar(*II) == ' ') + while (FormatStrConverter.convertBasicChar(*II) == u8' ') ++II; // Set the privacy flag if the privacy annotation in the @@ -191,7 +191,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, I += CommaOrBracePos + 1; } // Continue until the closing brace is found. - } while (FormatStrConverter.convertBasicChar(*(I - 1)) == ','); + } while (FormatStrConverter.convertBasicChar(*(I - 1)) == u8','); // Set the privacy flag. switch (PrivacyFlags) { @@ -263,7 +263,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } // Look for the precision (if any). - if (FormatStrConverter.convertBasicChar(*I) == '.') { + if (FormatStrConverter.convertBasicChar(*I) == u8'.') { ++I; if (I == E) { if (Warn) @@ -303,7 +303,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, // enables better recovery, and we don't know if // these flags are applicable until later. const char *ObjCModifierFlagsStart = nullptr, *ObjCModifierFlagsEnd = nullptr; - if (FormatStrConverter.convertBasicChar(*I) == '[') { + if (FormatStrConverter.convertBasicChar(*I) == u8'[') { ObjCModifierFlagsStart = I; ++I; auto flagStart = I; @@ -315,7 +315,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; } // Did we find the closing ']'? - if (FormatStrConverter.convertBasicChar(*I) == ']') { + if (FormatStrConverter.convertBasicChar(*I) == u8']') { if (ParseObjCFlags(H, FS, flagStart, I, Warn, FormatStrConverter)) return true; @@ -341,135 +341,135 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, default: break; // C99: 7.19.6.1 (section 8). - case '%': + case u8'%': k = ConversionSpecifier::PercentArg; break; - case 'A': + case u8'A': k = ConversionSpecifier::AArg; break; - case 'E': + case u8'E': k = ConversionSpecifier::EArg; break; - case 'F': + case u8'F': k = ConversionSpecifier::FArg; break; - case 'G': + case u8'G': k = ConversionSpecifier::GArg; break; - case 'X': + case u8'X': k = ConversionSpecifier::XArg; break; - case 'a': + case u8'a': k = ConversionSpecifier::aArg; break; - case 'c': + case u8'c': k = ConversionSpecifier::cArg; break; - case 'd': + case u8'd': k = ConversionSpecifier::dArg; break; - case 'e': + case u8'e': k = ConversionSpecifier::eArg; break; - case 'f': + case u8'f': k = ConversionSpecifier::fArg; break; - case 'g': + case u8'g': k = ConversionSpecifier::gArg; break; - case 'i': + case u8'i': k = ConversionSpecifier::iArg; break; - case 'n': + case u8'n': // Not handled, but reserved in OpenCL. if (!LO.OpenCL) k = ConversionSpecifier::nArg; break; - case 'o': + case u8'o': k = ConversionSpecifier::oArg; break; - case 'p': + case u8'p': k = ConversionSpecifier::pArg; break; - case 's': + case u8's': k = ConversionSpecifier::sArg; break; - case 'u': + case u8'u': k = ConversionSpecifier::uArg; break; - case 'x': + case u8'x': k = ConversionSpecifier::xArg; break; // C23. - case 'b': + case u8'b': if (isFreeBSDKPrintf) k = ConversionSpecifier::FreeBSDbArg; // int followed by char * else k = ConversionSpecifier::bArg; break; - case 'B': + case u8'B': k = ConversionSpecifier::BArg; break; // POSIX specific. - case 'C': + case u8'C': k = ConversionSpecifier::CArg; break; - case 'S': + case u8'S': k = ConversionSpecifier::SArg; break; // Apple extension for os_log - case 'P': + case u8'P': k = ConversionSpecifier::PArg; break; // Objective-C. - case '@': + case u8'@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. - case 'm': + case u8'm': k = ConversionSpecifier::PrintErrno; break; - case 'r': + case u8'r': if (isFreeBSDKPrintf) k = ConversionSpecifier::FreeBSDrArg; // int else if (LO.FixedPoint) k = ConversionSpecifier::rArg; break; - case 'y': + case u8'y': if (isFreeBSDKPrintf) k = ConversionSpecifier::FreeBSDyArg; // int break; // Apple-specific. - case 'D': + case u8'D': if (isFreeBSDKPrintf) k = ConversionSpecifier::FreeBSDDArg; // void * followed by char * else if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::DArg; break; - case 'O': + case u8'O': if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::OArg; break; - case 'U': + case u8'U': if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::UArg; break; // MS specific. - case 'Z': + case u8'Z': if (Target.getTriple().isOSMSVCRT()) k = ConversionSpecifier::ZArg; break; // ISO/IEC TR 18037 (fixed-point) specific. // NOTE: 'r' is handled up above since FreeBSD also supports %r. - case 'k': + case u8'k': if (LO.FixedPoint) k = ConversionSpecifier::kArg; break; - case 'K': + case u8'K': if (LO.FixedPoint) k = ConversionSpecifier::KArg; break; - case 'R': + case u8'R': if (LO.FixedPoint) k = ConversionSpecifier::RArg; break; diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 592bc3ad220e8..7be46f69144db 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -89,7 +89,7 @@ static ScanfSpecifierResult ParseScanfSpecifier( H.HandleNullChar(I); return true; } - if (FormatStrConverter.convertBasicChar(c) == '%') { + if (FormatStrConverter.convertBasicChar(c) == u8'%') { Start = I++; // Record the start of the format specifier. break; } @@ -116,7 +116,7 @@ static ScanfSpecifierResult ParseScanfSpecifier( } // Look for '*' flag if it is present. - if (FormatStrConverter.convertBasicChar(*I) == '*') { + if (FormatStrConverter.convertBasicChar(*I) == u8'*') { FS.setSuppressAssignment(I); if (++I == E) { H.HandleIncompleteSpecifier(Start, E - Start); @@ -160,86 +160,86 @@ static ScanfSpecifierResult ParseScanfSpecifier( switch (FormatStrConverter.convertBasicChar(*conversionPosition)) { default: break; - case '%': + case u8'%': k = ConversionSpecifier::PercentArg; break; - case 'b': + case u8'b': k = ConversionSpecifier::bArg; break; - case 'A': + case u8'A': k = ConversionSpecifier::AArg; break; - case 'E': + case u8'E': k = ConversionSpecifier::EArg; break; - case 'F': + case u8'F': k = ConversionSpecifier::FArg; break; - case 'G': + case u8'G': k = ConversionSpecifier::GArg; break; - case 'X': + case u8'X': k = ConversionSpecifier::XArg; break; - case 'a': + case u8'a': k = ConversionSpecifier::aArg; break; - case 'd': + case u8'd': k = ConversionSpecifier::dArg; break; - case 'e': + case u8'e': k = ConversionSpecifier::eArg; break; - case 'f': + case u8'f': k = ConversionSpecifier::fArg; break; - case 'g': + case u8'g': k = ConversionSpecifier::gArg; break; - case 'i': + case u8'i': k = ConversionSpecifier::iArg; break; - case 'n': + case u8'n': k = ConversionSpecifier::nArg; break; - case 'c': + case u8'c': k = ConversionSpecifier::cArg; break; - case 'C': + case u8'C': k = ConversionSpecifier::CArg; break; - case 'S': + case u8'S': k = ConversionSpecifier::SArg; break; - case '[': + case u8'[': k = ConversionSpecifier::ScanListArg; break; - case 'u': + case u8'u': k = ConversionSpecifier::uArg; break; - case 'x': + case u8'x': k = ConversionSpecifier::xArg; break; - case 'o': + case u8'o': k = ConversionSpecifier::oArg; break; - case 's': + case u8's': k = ConversionSpecifier::sArg; break; - case 'p': + case u8'p': k = ConversionSpecifier::pArg; break; // Apple extensions // Apple-specific - case 'D': + case u8'D': if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::DArg; break; - case 'O': + case u8'O': if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::OArg; break; - case 'U': + case u8'U': if (Target.getTriple().isOSDarwin()) k = ConversionSpecifier::UArg; break; From 516a14fc9464e604c873862e17c1781f7aa0e3e6 Mon Sep 17 00:00:00 2001 From: Abhina Sreeskantharajan Date: Tue, 11 Aug 2026 08:58:50 -0400 Subject: [PATCH 5/5] Add isNoop to avoid conversion code in ActOnGCCAsmStmtString --- .../utils/FormatStringConverter.cpp | 9 ++- clang/include/clang/AST/FormatString.h | 6 +- clang/lib/AST/FormatString.cpp | 62 ++++++++-------- clang/lib/AST/FormatStringParsing.h | 72 +++++++++++-------- clang/lib/AST/OSLog.cpp | 2 +- clang/lib/AST/PrintfFormatString.cpp | 26 ++----- clang/lib/AST/ScanfFormatString.cpp | 38 ++++------ clang/lib/Basic/TargetInfo.cpp | 1 - clang/lib/Sema/SemaChecking.cpp | 40 +++++------ clang/lib/Sema/SemaStmtAsm.cpp | 1 + llvm/include/llvm/Support/TextEncoding.h | 7 ++ llvm/lib/Support/TextEncoding.cpp | 2 + 12 files changed, 127 insertions(+), 139 deletions(-) diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp index 73fc53d395266..5fceff6fc6ec6 100644 --- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp +++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp @@ -619,11 +619,10 @@ bool FormatStringConverter::convertArgument(const PrintfSpecifier &FS, } /// Called for each format specifier by ParsePrintfString. -bool FormatStringConverter::HandlePrintfSpecifier(const PrintfSpecifier &FS, - const char *StartSpecifier, - unsigned SpecifierLen, - const TargetInfo &Target, - const llvm::TextEncodingConverter &) { +bool FormatStringConverter::HandlePrintfSpecifier( + const PrintfSpecifier &FS, const char *StartSpecifier, + unsigned SpecifierLen, const TargetInfo &Target, + const llvm::TextEncodingConverter &) { const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data(); assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size()); diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index ae0ccff75bb41..e233879d6f6a2 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -765,8 +765,7 @@ class FormatStringHandler { virtual bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + unsigned specifierLen, const llvm::TextEncodingConverter &Conv) { return true; } @@ -785,8 +784,7 @@ class FormatStringHandler { virtual bool HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + unsigned specifierLen, const llvm::TextEncodingConverter &Conv) { return true; } diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index b67de85f75467..fdd40208aaa0c 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -36,7 +36,7 @@ FormatStringHandler::~FormatStringHandler() {} OptionalAmount clang::analyze_format_string::ParseAmount( const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { const char *I = Beg; UpdateOnReturn UpdateBeg(Beg, I); @@ -44,7 +44,7 @@ OptionalAmount clang::analyze_format_string::ParseAmount( bool hasDigits = false; for (; I != E; ++I) { - char c = FromSystemEncodingConverter.convertBasicChar(*I); + char c = Conv.convertBasicChar(*I); if (c >= u8'0' && c <= u8'9') { hasDigits = true; accumulator = (accumulator * 10) + (c - u8'0'); @@ -78,22 +78,22 @@ static bool ParseWidthModifier(const char *&I, const char *E, OptionalAmount clang::analyze_format_string::ParseNonPositionAmount( const char *&Beg, const char *E, unsigned &argIndex, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convertBasicChar(*Beg) == u8'*') { + const llvm::TextEncodingConverter &Conv) { + if (Conv.convertBasicChar(*Beg) == u8'*') { ++Beg; return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); } - return ParseAmount(Beg, E, FromSystemEncodingConverter); + return ParseAmount(Beg, E, Conv); } OptionalAmount clang::analyze_format_string::ParsePositionAmount( FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, PositionContext p, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { - if (FromSystemEncodingConverter.convertBasicChar(*Beg) == u8'*') { + const llvm::TextEncodingConverter &Conv) { + if (Conv.convertBasicChar(*Beg) == u8'*') { const char *I = Beg + 1; - const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); + const OptionalAmount &Amt = ParseAmount(I, E, Conv); if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { H.HandleInvalidPosition(Beg, I - Beg, p); @@ -108,7 +108,7 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( assert(Amt.getHowSpecified() == OptionalAmount::Constant); - if (FromSystemEncodingConverter.convertBasicChar(*I) == u8'$') { + if (Conv.convertBasicChar(*I) == u8'$') { // Handle positional arguments // Special case: '*0$', since this is an easy mistake. @@ -128,21 +128,21 @@ OptionalAmount clang::analyze_format_string::ParsePositionAmount( return OptionalAmount(false); } - return ParseAmount(Beg, E, FromSystemEncodingConverter); + return ParseAmount(Beg, E, Conv); } bool clang::analyze_format_string::ParseFieldWidth( FormatStringHandler &H, FormatSpecifier &CS, const char *Start, const char *&Beg, const char *E, unsigned *argIndex, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { // FIXME: Support negative field widths. if (argIndex) { CS.setFieldWidth( - ParseNonPositionAmount(Beg, E, *argIndex, FromSystemEncodingConverter)); + ParseNonPositionAmount(Beg, E, *argIndex, Conv)); } else { const OptionalAmount Amt = ParsePositionAmount( H, Start, Beg, E, analyze_format_string::FieldWidthPos, - FromSystemEncodingConverter); + Conv); if (Amt.isInvalid()) return true; @@ -154,10 +154,10 @@ bool clang::analyze_format_string::ParseFieldWidth( bool clang::analyze_format_string::ParseArgPosition( FormatStringHandler &H, FormatSpecifier &FS, const char *Start, const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { const char *I = Beg; - const OptionalAmount &Amt = ParseAmount(I, E, FromSystemEncodingConverter); + const OptionalAmount &Amt = ParseAmount(I, E, Conv); if (I == E) { // No more characters left? @@ -166,7 +166,7 @@ bool clang::analyze_format_string::ParseArgPosition( } if (Amt.getHowSpecified() == OptionalAmount::Constant && - FromSystemEncodingConverter.convertBasicChar(*(I++)) == u8'$') { + Conv.convertBasicChar(*(I++)) == u8'$') { // Warn that positional arguments are non-standard. H.HandlePosition(Start, I - Start); @@ -190,12 +190,12 @@ bool clang::analyze_format_string::ParseArgPosition( bool clang::analyze_format_string::ParseVectorModifier( FormatStringHandler &H, FormatSpecifier &FS, const char *&I, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { if (!LO.OpenCL) return false; const char *Start = I; - if (FromSystemEncodingConverter.convertBasicChar(*I) == u8'v') { + if (Conv.convertBasicChar(*I) == u8'v') { ++I; if (I == E) { @@ -203,7 +203,7 @@ bool clang::analyze_format_string::ParseVectorModifier( return true; } - OptionalAmount NumElts = ParseAmount(I, E, FromSystemEncodingConverter); + OptionalAmount NumElts = ParseAmount(I, E, Conv); if (NumElts.getHowSpecified() != OptionalAmount::Constant) { H.HandleIncompleteSpecifier(Start, E - Start); return true; @@ -217,20 +217,20 @@ bool clang::analyze_format_string::ParseVectorModifier( bool clang::analyze_format_string::ParseLengthModifier( FormatSpecifier &FS, const char *&I, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FromSystemEncodingConverter, + const llvm::TextEncodingConverter &Conv, bool IsScanf) { LengthModifier::Kind lmKind = LengthModifier::None; const char *lmPosition = I; - switch (FromSystemEncodingConverter.convertBasicChar(*I)) { + switch (Conv.convertBasicChar(*I)) { default: return false; case u8'h': ++I; - if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == u8'h') { + if (I != E && Conv.convertBasicChar(*I) == u8'h') { ++I; lmKind = LengthModifier::AsChar; } else if (I != E && - FromSystemEncodingConverter.convertBasicChar(*I) == u8'l' && + Conv.convertBasicChar(*I) == u8'l' && LO.OpenCL) { ++I; lmKind = LengthModifier::AsShortLong; @@ -240,7 +240,7 @@ bool clang::analyze_format_string::ParseLengthModifier( break; case u8'l': ++I; - if (I != E && FromSystemEncodingConverter.convertBasicChar(*I) == u8'l') { + if (I != E && Conv.convertBasicChar(*I) == u8'l') { ++I; lmKind = LengthModifier::AsLongLong; } else { @@ -274,9 +274,9 @@ bool clang::analyze_format_string::ParseLengthModifier( // will be parsed as a conversion specifier. ++I; if (I != E && - (FromSystemEncodingConverter.convertBasicChar(*I) == u8's' || - FromSystemEncodingConverter.convertBasicChar(*I) == u8'S' || - FromSystemEncodingConverter.convertBasicChar(*I) == u8'[')) { + (Conv.convertBasicChar(*I) == u8's' || + Conv.convertBasicChar(*I) == u8'S' || + Conv.convertBasicChar(*I) == u8'[')) { lmKind = LengthModifier::AsAllocate; break; } @@ -294,8 +294,8 @@ bool clang::analyze_format_string::ParseLengthModifier( // scanf: AsInt64 case u8'I': if (I + 1 != E && I + 2 != E) { - if (FromSystemEncodingConverter.convertBasicChar(I[1]) == u8'6' && - FromSystemEncodingConverter.convertBasicChar(I[2]) == u8'4') { + if (Conv.convertBasicChar(I[1]) == u8'6' && + Conv.convertBasicChar(I[2]) == u8'4') { I += 3; lmKind = LengthModifier::AsInt64; break; @@ -303,8 +303,8 @@ bool clang::analyze_format_string::ParseLengthModifier( if (IsScanf) return false; - if (FromSystemEncodingConverter.convertBasicChar(I[1]) == u8'3' && - FromSystemEncodingConverter.convertBasicChar(I[2]) == u8'2') { + if (Conv.convertBasicChar(I[1]) == u8'3' && + Conv.convertBasicChar(I[2]) == u8'2') { I += 3; lmKind = LengthModifier::AsInt32; break; diff --git a/clang/lib/AST/FormatStringParsing.h b/clang/lib/AST/FormatStringParsing.h index 164efb9d847b9..52915387ccbfa 100644 --- a/clang/lib/AST/FormatStringParsing.h +++ b/clang/lib/AST/FormatStringParsing.h @@ -17,11 +17,29 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/FormatString.h" #include "clang/AST/Type.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/Support/TextEncoding.h" namespace clang { class LangOptions; +/// Creates a TextEncodingConverter for format string character comparisons. +/// On z/OS format strings are IBM-1047 encoded, so each byte must be converted +/// to UTF-8 before comparing against ASCII specifier characters. +/// On all other targets a no-op converter is returned. +/// Create this once per format string parse and pass it through. +inline llvm::TextEncodingConverter +makeFormatStrConverter(const TargetInfo &Target) { + auto MaybeConverter = + Target.getTriple().isOSzOS() + ? llvm::TextEncodingConverter::create(llvm::TextEncoding::IBM1047, + llvm::TextEncoding::UTF8) + : llvm::TextEncodingConverter::createNoopConverter(); + assert(MaybeConverter && "Failed to create format string converter"); + return std::move(*MaybeConverter); +} + template class UpdateOnReturn { T &ValueToUpdate; const T &ValueToCopy; @@ -35,45 +53,37 @@ template class UpdateOnReturn { namespace analyze_format_string { -OptionalAmount -ParseAmount(const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); - -OptionalAmount ParseNonPositionAmount( - const char *&Beg, const char *E, unsigned &argIndex, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +OptionalAmount ParseAmount(const char *&Beg, const char *E, + const llvm::TextEncodingConverter &Conv); -OptionalAmount ParsePositionAmount( - FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, - PositionContext p, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E, + unsigned &argIndex, + const llvm::TextEncodingConverter &Conv); -OptionalAmount ParsePositionAmount( - FormatStringHandler &H, const char *Start, const char *&Beg, const char *E, - PositionContext p, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +OptionalAmount ParsePositionAmount(FormatStringHandler &H, const char *Start, + const char *&Beg, const char *E, + PositionContext p, + const llvm::TextEncodingConverter &Conv); -bool ParseFieldWidth( - FormatStringHandler &H, FormatSpecifier &CS, const char *Start, - const char *&Beg, const char *E, unsigned *argIndex, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +bool ParseFieldWidth(FormatStringHandler &H, FormatSpecifier &CS, + const char *Start, const char *&Beg, const char *E, + unsigned *argIndex, + const llvm::TextEncodingConverter &Conv); -bool ParseArgPosition( - FormatStringHandler &H, FormatSpecifier &CS, const char *Start, - const char *&Beg, const char *E, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +bool ParseArgPosition(FormatStringHandler &H, FormatSpecifier &CS, + const char *Start, const char *&Beg, const char *E, + const llvm::TextEncodingConverter &Conv); -bool ParseVectorModifier( - FormatStringHandler &H, FormatSpecifier &FS, const char *&Beg, - const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); +bool ParseVectorModifier(FormatStringHandler &H, FormatSpecifier &FS, + const char *&Beg, const char *E, const LangOptions &LO, + const llvm::TextEncodingConverter &Conv); /// Returns true if a LengthModifier was parsed and installed in the /// FormatSpecifier& argument, and false otherwise. -bool ParseLengthModifier( - FormatSpecifier &FS, const char *&Beg, const char *E, const LangOptions &LO, - const llvm::TextEncodingConverter &FromSystemEncodingConverter, - bool IsScanf = false); +bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, + const LangOptions &LO, + const llvm::TextEncodingConverter &Conv, + bool IsScanf = false); /// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8 /// string; check that it won't go further than \p FmtStrEnd and write diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp index f112403add118..379a611db3f9c 100644 --- a/clang/lib/AST/OSLog.cpp +++ b/clang/lib/AST/OSLog.cpp @@ -69,7 +69,7 @@ class OSLogFormatStringHandler bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, - const TargetInfo &, + const TargetInfo &Target, const llvm::TextEncodingConverter &) override { if (!FS.consumesDataArgument() && FS.getConversionSpecifier().getKind() != diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index c8da30f98676b..1ade9b72fd1be 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -14,8 +14,6 @@ #include "FormatStringParsing.h" #include "clang/AST/FormatString.h" #include "clang/AST/OSLog.h" -#include "clang/Basic/TargetInfo.h" -#include "llvm/Support/TextEncoding.h" #include "llvm/Support/Regex.h" using clang::analyze_format_string::ArgType; @@ -288,8 +286,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, return true; // Look for the length modifier. - if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter) && - I == E) { + if (ParseLengthModifier(FS, I, E, LO, FormatStrConverter) && I == E) { // No more characters left? if (Warn) H.HandleIncompleteSpecifier(Start, E - Start); @@ -316,8 +313,7 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, } // Did we find the closing ']'? if (FormatStrConverter.convertBasicChar(*I) == u8']') { - if (ParseObjCFlags(H, FS, flagStart, I, Warn, - FormatStrConverter)) + if (ParseObjCFlags(H, FS, flagStart, I, Warn, FormatStrConverter)) return true; ++I; break; @@ -500,26 +496,12 @@ ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidPrintfConversionSpecifier( - FS, Start, Len, FormatStrConverter); + return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len, + FormatStrConverter); } return PrintfSpecifierResult(Start, FS); } -// Creates a converter for format string character analysis. On z/OS format -// strings are IBM-1047 encoded, so we convert each byte to UTF-8 before -// comparing against ASCII specifier characters. On other targets a no-op -// converter is used. Created once per format string parse and passed through. -static llvm::TextEncodingConverter makeFormatStrConverter(const TargetInfo &T) { - auto MaybeConverter = - T.getTriple().isOSzOS() - ? llvm::TextEncodingConverter::create(llvm::TextEncoding::IBM1047, - llvm::TextEncoding::UTF8) - : llvm::TextEncodingConverter::createNoopConverter(); - assert(MaybeConverter && "Failed to create format string converter"); - return std::move(*MaybeConverter); -} - bool clang::analyze_format_string::ParsePrintfString( FormatStringHandler &H, const char *I, const char *E, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf) { diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 7be46f69144db..9c5132e5ff9b8 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -13,8 +13,6 @@ #include "FormatStringParsing.h" #include "clang/AST/FormatString.h" -#include "clang/Basic/TargetInfo.h" -#include "llvm/Support/TextEncoding.h" using clang::UpdateOnReturn; using clang::analyze_format_string::ArgType; @@ -72,10 +70,11 @@ static bool ParseScanList(FormatStringHandler &H, ScanfConversionSpecifier &CS, // FIXME: Much of this is copy-paste from ParsePrintfSpecifier. // We can possibly refactor. -static ScanfSpecifierResult ParseScanfSpecifier( - FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex, - const LangOptions &LO, const TargetInfo &Target, - const llvm::TextEncodingConverter &FormatStrConverter) { +static ScanfSpecifierResult +ParseScanfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, + unsigned &argIndex, const LangOptions &LO, + const TargetInfo &Target, + const llvm::TextEncodingConverter &FormatStrConverter) { using namespace clang::analyze_format_string; using namespace clang::analyze_scanf; const char *I = Beg; @@ -126,8 +125,8 @@ static ScanfSpecifierResult ParseScanfSpecifier( // Look for the field width (if any). Unlike printf, this is either // a fixed integer or isn't present. - const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount( - I, E, FormatStrConverter); + const OptionalAmount &Amt = + clang::analyze_format_string::ParseAmount(I, E, FormatStrConverter); if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { assert(Amt.getHowSpecified() == OptionalAmount::Constant); FS.setFieldWidth(Amt); @@ -264,8 +263,8 @@ static ScanfSpecifierResult ParseScanfSpecifier( FS.setConversionSpecifier(CS); } // Assume the conversion takes one argument. - return !H.HandleInvalidScanfConversionSpecifier( - FS, Beg, Len, FormatStrConverter); + return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len, + FormatStrConverter); } return ScanfSpecifierResult(Start, FS); } @@ -613,21 +612,12 @@ void ScanfSpecifier::toString(raw_ostream &os) const { } bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, - const char *I, - const char *E, - const LangOptions &LO, - const TargetInfo &Target) { - + const char *I, + const char *E, + const LangOptions &LO, + const TargetInfo &Target) { unsigned argIndex = 0; - // On z/OS, format strings are IBM-1047 encoded. Create the converter once - // here and pass it through to avoid recreating it for every specifier. - auto MaybeConverter = - Target.getTriple().isOSzOS() - ? llvm::TextEncodingConverter::create(llvm::TextEncoding::IBM1047, - llvm::TextEncoding::UTF8) - : llvm::TextEncodingConverter::createNoopConverter(); - assert(MaybeConverter && "Failed to create format string converter"); - llvm::TextEncodingConverter Conv = std::move(*MaybeConverter); + llvm::TextEncodingConverter Conv = makeFormatStrConverter(Target); // Keep looking for a format specifier until we have exhausted the string. while (I != E) { diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 01ebd187d6848..6e390226d2e4b 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -201,7 +201,6 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { MaxOpenCLWorkGroupSize = 1024; MaxBitIntWidth.reset(); - } // Out of line virtual dtor for TargetInfo. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 4d3d458105126..5dddb89b9ebdc 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8317,7 +8317,7 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { bool HandleInvalidConversionSpecifier( unsigned argIndex, SourceLocation Loc, const char *startSpec, unsigned specifierLen, const char *csStart, unsigned csLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter); + const llvm::TextEncodingConverter &Conv); void HandlePositionalNonpositionalArgs(SourceLocation Loc, const char *startSpec, @@ -8565,7 +8565,7 @@ void UncoveredArgHandler::Diagnose(Sema &S, bool IsFunctionCall, bool CheckFormatHandler::HandleInvalidConversionSpecifier( unsigned argIndex, SourceLocation Loc, const char *startSpec, unsigned specifierLen, const char *csStart, unsigned csLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { bool keepGoing = true; if (argIndex < NumDataArgs) { // Consider the argument coverered, even though the specifier doesn't @@ -8584,7 +8584,7 @@ bool CheckFormatHandler::HandleInvalidConversionSpecifier( // exec charset, so we have to reverse the conversion to allow diagnostic // message to match an expected value when using -verify option, SmallString<4> RS; - auto EC = FromSystemEncodingConverter.convert(StringRef(csStart, csLen), RS); + auto EC = Conv.convert(StringRef(csStart, csLen), RS); if (EC) { keepGoing = false; } @@ -8743,7 +8743,7 @@ class CheckPrintfHandler : public CheckFormatHandler { bool HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) override; + const llvm::TextEncodingConverter &Conv) override; void handleInvalidMaskType(StringRef MaskType) override; @@ -8874,11 +8874,11 @@ class DecomposePrintfHandler : public CheckPrintfHandler { FormatStringType type, bool IsObjC, bool InFunctionCall, llvm::SmallVectorImpl &Args); - virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen, - const TargetInfo &Target, - const llvm::TextEncodingConverter &Conv) override; + virtual bool + HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, unsigned specifierLen, + const TargetInfo &Target, + const llvm::TextEncodingConverter &Conv) override; }; } // namespace @@ -8886,13 +8886,13 @@ class DecomposePrintfHandler : public CheckPrintfHandler { bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier( const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { const analyze_printf::PrintfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength(), FromSystemEncodingConverter); + specifierLen, CS.getStart(), CS.getLength(), Conv); } void CheckPrintfHandler::handleInvalidMaskType(StringRef MaskType) { @@ -9402,15 +9402,15 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( // Check for using an Objective-C specific conversion specifier // in a non-ObjC literal. if (!allowsObjCArg() && CS.isObjCArg()) { - return HandleInvalidPrintfConversionSpecifier( - FS, startSpecifier, specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen, Conv); } // %P can only be used with os_log. if (FSType != FormatStringType::OSLog && CS.getKind() == ConversionSpecifier::PArg) { - return HandleInvalidPrintfConversionSpecifier( - FS, startSpecifier, specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen, Conv); } // %n is not allowed with os_log. @@ -9429,8 +9429,8 @@ bool CheckPrintfHandler::HandlePrintfSpecifier( (CS.getKind() == ConversionSpecifier::PArg || CS.getKind() == ConversionSpecifier::sArg || CS.getKind() == ConversionSpecifier::ObjCObjArg)) { - return HandleInvalidPrintfConversionSpecifier( - FS, startSpecifier, specifierLen, Conv); + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen, Conv); } // Check for use of public/private annotation outside of os_log(). @@ -10109,7 +10109,7 @@ class CheckScanfHandler : public CheckFormatHandler { bool HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) override; + const llvm::TextEncodingConverter &Conv) override; void HandleIncompleteScanList(const char *start, const char *end) override; }; @@ -10126,14 +10126,14 @@ void CheckScanfHandler::HandleIncompleteScanList(const char *start, bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier( const analyze_scanf::ScanfSpecifier &FS, const char *startSpecifier, unsigned specifierLen, - const llvm::TextEncodingConverter &FromSystemEncodingConverter) { + const llvm::TextEncodingConverter &Conv) { const analyze_scanf::ScanfConversionSpecifier &CS = FS.getConversionSpecifier(); return HandleInvalidConversionSpecifier( FS.getArgIndex(), getLocationOfByte(CS.getStart()), startSpecifier, - specifierLen, CS.getStart(), CS.getLength(), FromSystemEncodingConverter); + specifierLen, CS.getStart(), CS.getLength(), Conv); } bool CheckScanfHandler::HandleScanfSpecifier( diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index f4490c940bf61..f3c6d8758c71e 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -266,6 +266,7 @@ ExprResult Sema::ActOnGCCAsmStmtString(Expr *Expr, bool ForAsmLabel, } return SL; } + if (DiagnoseUnexpandedParameterPack(Expr)) return ExprError(); if (Expr->getDependence() != ExprDependence::None) diff --git a/llvm/include/llvm/Support/TextEncoding.h b/llvm/include/llvm/Support/TextEncoding.h index 80709e96be04b..7ea1a6e1ff28b 100644 --- a/llvm/include/llvm/Support/TextEncoding.h +++ b/llvm/include/llvm/Support/TextEncoding.h @@ -59,6 +59,10 @@ class TextEncodingConverterImplBase { public: virtual ~TextEncodingConverterImplBase() = default; + /// Returns true if this converter performs no conversion (UTF-8 to + /// UTF-8). + virtual bool isNoop() const { return false; } + /// Converts a string and resets the converter to the initial state. std::error_code convert(StringRef Source, SmallVectorImpl &Result) { auto EC = convertString(Source, Result); @@ -137,6 +141,9 @@ class TextEncodingConverter { return EC; } + /// Returns true if this converter performs no conversion. + bool isNoop() const { return Converter->isNoop(); } + // This method is used in format string handling and is only intended // to support basic charsets, not multibyte characters. char convertBasicChar(char SingleChar) const { diff --git a/llvm/lib/Support/TextEncoding.cpp b/llvm/lib/Support/TextEncoding.cpp index 9672cba006a2c..bf77a4369179c 100644 --- a/llvm/lib/Support/TextEncoding.cpp +++ b/llvm/lib/Support/TextEncoding.cpp @@ -371,6 +371,8 @@ class TextEncodingConverterNoop final } void reset() override {} + + bool isNoop() const override { return true; } }; ErrorOr TextEncodingConverter::createNoopConverter() {