[llvm-branch-commits] [clang] [clang-tools-extra] [llvm] Add format string handling (PR #196568)

Abhina Sree via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Oct 7 07:57:48 PDT 2026


https://github.com/abhina-sree updated https://github.com/llvm/llvm-project/pull/196568

>From bcd956509f05849dec71f136867ee78376f570b0 Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Wed, 7 Oct 2026 10:56:26 -0400
Subject: [PATCH 1/6] add more testcases, make diagnostic message generic

---
 clang/include/clang/Basic/DiagnosticLexKinds.td |  2 +-
 clang/test/CodeGen/systemz-charset-diag.cpp     |  2 +-
 clang/test/CodeGen/systemz-charset.cpp          |  8 ++++++--
 clang/test/Sema/systemz-charset-asm.cpp         | 11 +++++++++++
 4 files changed, 19 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Sema/systemz-charset-asm.cpp

diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index f0791ed486a74..bc1518b7995b7 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -292,7 +292,7 @@ def ext_string_too_long : Extension<"string literal of length %0 exceeds "
 def err_character_too_large : Error<
   "character too large for enclosing character literal type">;
 def err_exec_charset_conversion_failed
-    : Error<"conversion to literal encoding failed: '%0'">;
+    : Error<"encoding conversion failed: '%0'">;
 def warn_c99_compat_unicode_literal : Warning<
   "unicode literals are incompatible with C99">,
   InGroup<C99Compat>, DefaultIgnore;
diff --git a/clang/test/CodeGen/systemz-charset-diag.cpp b/clang/test/CodeGen/systemz-charset-diag.cpp
index 4ed94810150a3..a59a5ef4f0604 100644
--- a/clang/test/CodeGen/systemz-charset-diag.cpp
+++ b/clang/test/CodeGen/systemz-charset-diag.cpp
@@ -1,3 +1,3 @@
 // RUN: %clang_cc1 -triple s390x-none-zos -fexec-charset IBM-1047 %s -std=c++17 -emit-llvm -o - -verify
 
-const char* Computer = "🖥️"; // expected-error-re {{conversion to literal encoding failed: {{.*}}}}
+const char* Computer = "🖥️"; // expected-error-re {{encoding conversion failed: {{.*}}}}
diff --git a/clang/test/CodeGen/systemz-charset.cpp b/clang/test/CodeGen/systemz-charset.cpp
index 6348fb72dac9a..b6b79842468c1 100644
--- a/clang/test/CodeGen/systemz-charset.cpp
+++ b/clang/test/CodeGen/systemz-charset.cpp
@@ -92,7 +92,11 @@ constexpr string_view getfoo() { return "\174foo"; }
 
 void function()
 {
+  asm("\174foo");
+  // CHECK: asm{{.*}}@foo
+  // CHECK-UTF8: asm{{.*}}@foo
   asm((getfoo()));
+  // CHECK: asm{{.*}}|\86\96\96
+  // CHECK-UTF8: asm{{.*}}|foo
 }
-// CHECK: asm{{.*}}|\86\96\96
-// CHECK-UTF8: asm{{.*}}|foo
+
diff --git a/clang/test/Sema/systemz-charset-asm.cpp b/clang/test/Sema/systemz-charset-asm.cpp
new file mode 100644
index 0000000000000..b3c27cc53638e
--- /dev/null
+++ b/clang/test/Sema/systemz-charset-asm.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -triple s390x-none-zos -fexec-charset IBM-1047 %s -std=c++23 -fsyntax-only -verify
+// RUN: %clang_cc1 -triple s390x-none-zos -fexec-charset UTF-8 %s -std=c++23 -fsyntax-only -verify
+
+long inline_asm_ucn_named_operand(long a, long b) {
+  // The \N{...} escape encodes to U+0130.  IBM-1047 has no code point for
+  // U+0130, so the conversion reports an error.
+  asm("\tLGR %0, %[\N{LATIN CAPITAL LETTER I WITH DOT ABOVE}]\n" // expected-error-re {{encoding conversion failed: {{.*}}}}
+      : "=r"(a)
+      : [\N{LATIN CAPITAL LETTER I WITH DOT ABOVE}] "r"(b));
+  return a;
+}

>From cce9e4201c6d39902edf8e38f8a5b758953d4fd9 Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Fri, 8 May 2026 12:19:11 -0400
Subject: [PATCH 2/6] 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/Lex/TextEncoding.cpp                |   8 +-
 clang/lib/Sema/SemaChecking.cpp               |  65 ++++++----
 llvm/include/llvm/Support/TextEncoding.h      |  10 ++
 llvm/lib/Support/TextEncoding.cpp             |  19 +++
 15 files changed, 272 insertions(+), 146 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
index 98709e924a7a1..2e933b096b48d 100644
--- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
+++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
@@ -621,7 +621,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 <optional>
 
 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 46903a5361059..4eb21a7538953 100644
--- a/clang/include/clang/Lex/TextEncoding.h
+++ b/clang/include/clang/Lex/TextEncoding.h
@@ -29,9 +29,6 @@ class TextEncoding {
   std::unique_ptr<llvm::TextEncodingConverter> ToLiteralEncodingConverter;
 
   // 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<llvm::TextEncodingConverter> ToIBM1047Converter;
   std::unique_ptr<llvm::TextEncodingConverter> 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<const char *> 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 <typename T> 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 e0cff04069d59..d5272d5213707 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<PrintfSpecifier>
 
 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<const char *> 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 64b524de829b5..3aebcd004fd13 100644
--- a/clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1011,7 +1011,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 1bb0026d8422e..2ea2d407682ea 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -198,6 +198,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/Lex/TextEncoding.cpp b/clang/lib/Lex/TextEncoding.cpp
index afb5130ece88e..c988a0123e4ef 100644
--- a/clang/lib/Lex/TextEncoding.cpp
+++ b/clang/lib/Lex/TextEncoding.cpp
@@ -50,13 +50,11 @@ TextEncoding::setConvertersFromOptions(TextEncoding &TE,
   if (TInfo.getDefaultOrdinaryLiteralEncoding() == UTF8)
     return std::error_code();
 
-  // The IBM-1047 converters are only needed on z/OS, where the system
-  // (assembler/linker) default encoding is IBM-1047 rather than UTF-8.
-  assert((TInfo.getTriple().getOS() == llvm::Triple::ZOS) &&
+  // z/OS: system default encoding is IBM-1047 rather than UTF-8.
+  assert(TInfo.getTriple().getOS() == llvm::Triple::ZOS &&
          "Non-UTF-8 system encoding is only expected on z/OS");
 
-  // Create a converter from UTF-8 to IBM-1047 for use when parsing asm string
-  // literals on z/OS, where escape sequences are IBM-1047 code points.
+  // Create a converter from UTF-8 to IBM-1047 for asm string literals on z/OS.
   ErrorOr<TextEncodingConverter> ErrorOrConverter =
       llvm::TextEncodingConverter::create(
           UTF8, TInfo.getDefaultOrdinaryLiteralEncoding());
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index f2f38c84dc5f8..9e6808ee052dd 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);
@@ -8125,10 +8127,10 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler {
                        ArrayRef<FixItHint> 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,
@@ -8375,7 +8377,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
@@ -8390,7 +8393,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
@@ -8544,13 +8553,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);
@@ -8677,20 +8688,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) {
@@ -8953,9 +8966,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;
   }
@@ -9112,7 +9126,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;
 
@@ -9199,14 +9214,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.
@@ -9226,7 +9241,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().
@@ -9902,10 +9917,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;
 };
@@ -9921,13 +9936,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<TextEncodingConverter> create(StringRef From,
                                                         StringRef To);
 
+  LLVM_ABI static ErrorOr<TextEncodingConverter> 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> 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<char> &Result) override {
+    Result.assign(Source.begin(), Source.end());
+    return std::error_code();
+  }
+
+  void reset() override {}
+};
+
+ErrorOr<TextEncodingConverter> TextEncodingConverter::createNoopConverter() {
+  return TextEncodingConverter(std::make_unique<TextEncodingConverterNoop>());
+}

>From 08e05043da592457ded48e1d80549e27810d9c60 Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Thu, 28 May 2026 15:16:49 -0400
Subject: [PATCH 3/6] 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<const char *> 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 d5272d5213707..5b38488df8183 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 9e6808ee052dd..ed9a6f0d03ce6 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8130,7 +8130,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,
@@ -8378,7 +8378,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
@@ -8396,10 +8396,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
@@ -8554,7 +8556,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;
 
@@ -8697,13 +8699,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) {
@@ -9213,15 +9215,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.
@@ -9240,8 +9242,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().
@@ -9920,7 +9922,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;
 };
@@ -9937,14 +9939,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 c2c9c5a2f2466493c5be996600a0e0d78e38c563 Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Fri, 17 Jul 2026 14:23:44 -0400
Subject: [PATCH 4/6] 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 5b38488df8183..07d00891877bf 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 c4a289a4adcc0db5a9584a8f88f9c64413eb4cef Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Wed, 22 Jul 2026 14:49:20 -0400
Subject: [PATCH 5/6] 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 07d00891877bf..46a4e145ef8cc 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 4e9a8f099368bbfb01cd861f5cf5c43dc56ace7e Mon Sep 17 00:00:00 2001
From: Abhina Sreeskantharajan <Abhina.Sreeskantharajan at ibm.com>
Date: Tue, 11 Aug 2026 08:58:50 -0400
Subject: [PATCH 6/6] 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 2e933b096b48d..8e8963fed4a1c 100644
--- a/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
+++ b/clang-tools-extra/clang-tidy/utils/FormatStringConverter.cpp
@@ -618,11 +618,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<const char *> 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 <typename T> class UpdateOnReturn {
   T &ValueToUpdate;
   const T &ValueToCopy;
@@ -35,45 +53,37 @@ template <typename T> 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 46a4e145ef8cc..1e2e6a4572cc0 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 2ea2d407682ea..1bb0026d8422e 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -198,7 +198,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 ed9a6f0d03ce6..18ce07dac0f7c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8130,7 +8130,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,
@@ -8378,7 +8378,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
@@ -8397,7 +8397,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;
   }
@@ -8556,7 +8556,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;
 
@@ -8687,11 +8687,11 @@ class DecomposePrintfHandler : public CheckPrintfHandler {
                 FormatStringType type, bool IsObjC, bool InFunctionCall,
                 llvm::SmallVectorImpl<EquatableFormatArgument> &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
@@ -8699,13 +8699,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) {
@@ -9215,15 +9215,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.
@@ -9242,8 +9242,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().
@@ -9922,7 +9922,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;
 };
@@ -9939,14 +9939,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 f8c5e904effb7..fa5f55eeadfdf 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -261,6 +261,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<char> &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> TextEncodingConverter::createNoopConverter() {



More information about the llvm-branch-commits mailing list