Adds support for "%v" in absl::StrFormat and related functions for numeric types, including integer and floating point values. Users may now specify %v and have the format specifier deduced. Integer values will print according to %d specifications, unsigned values will use %u, and floating point values will use %g. Note that %v does not work for `char` due to ambiguity regarding the intended output. Please continue to use %c for `char`.

PiperOrigin-RevId: 474658166
Change-Id: Iecae39263e368b27232db440535f2bf7da8d863c
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc
index 007e8e8..b0e06bf 100644
--- a/absl/strings/internal/str_format/arg.cc
+++ b/absl/strings/internal/str_format/arg.cc
@@ -278,12 +278,33 @@
   return true;
 }
 
+template <typename T,
+          typename std::enable_if<(std::is_integral<T>::value &&
+                                   std::is_signed<T>::value) ||
+                                      std::is_same<T, int128>::value,
+                                  int>::type = 0>
+constexpr auto ConvertV(T) {
+  return FormatConversionCharInternal::d;
+}
+
+template <typename T,
+          typename std::enable_if<(std::is_integral<T>::value &&
+                                   std::is_unsigned<T>::value) ||
+                                      std::is_same<T, uint128>::value,
+                                  int>::type = 0>
+constexpr auto ConvertV(T) {
+  return FormatConversionCharInternal::u;
+}
+
 template <typename T>
-bool ConvertIntArg(T v, const FormatConversionSpecImpl conv,
-                   FormatSinkImpl *sink) {
+bool ConvertIntArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
   using U = typename MakeUnsigned<T>::type;
   IntDigits as_digits;
 
+  if (conv.conversion_char() == FormatConversionCharInternal::v) {
+    conv.set_conversion_char(ConvertV(T{}));
+  }
+
   // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes
   // it to complain about a switch/case type mismatch, even though both are
   // FormatConverionChar.  Likely this is because at this point
@@ -334,8 +355,11 @@
 }
 
 template <typename T>
-bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv,
-                     FormatSinkImpl *sink) {
+bool ConvertFloatArg(T v, FormatConversionSpecImpl conv, FormatSinkImpl *sink) {
+  if (conv.conversion_char() == FormatConversionCharInternal::v) {
+    conv.set_conversion_char(FormatConversionCharInternal::g);
+  }
+
   return FormatConversionCharIsFloat(conv.conversion_char()) &&
          ConvertFloatImpl(v, conv, sink);
 }
@@ -413,19 +437,18 @@
 }
 
 // ==================== Chars ====================
-IntegralConvertResult FormatConvertImpl(char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
+CharConvertResult FormatConvertImpl(char v, const FormatConversionSpecImpl conv,
+                                    FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
+CharConvertResult FormatConvertImpl(signed char v,
+                                    const FormatConversionSpecImpl conv,
+                                    FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        const FormatConversionSpecImpl conv,
-                                        FormatSinkImpl *sink) {
+CharConvertResult FormatConvertImpl(unsigned char v,
+                                    const FormatConversionSpecImpl conv,
+                                    FormatSinkImpl *sink) {
   return {ConvertIntArg(v, conv, sink)};
 }
 
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index a56ca30..3c2bb3e 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -176,9 +176,15 @@
 using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
     FormatConversionCharSetInternal::c,
     FormatConversionCharSetInternal::kNumeric,
+    FormatConversionCharSetInternal::kStar,
+    FormatConversionCharSetInternal::v)>;
+using FloatingConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::kFloating,
+    FormatConversionCharSetInternal::v)>;
+using CharConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
+    FormatConversionCharSetInternal::c,
+    FormatConversionCharSetInternal::kNumeric,
     FormatConversionCharSetInternal::kStar)>;
-using FloatingConvertResult =
-    ArgConvertResult<FormatConversionCharSetInternal::kFloating>;
 
 // Floats.
 FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv,
@@ -190,14 +196,14 @@
                                         FormatSinkImpl* sink);
 
 // Chars.
-IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(signed char v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
-IntegralConvertResult FormatConvertImpl(unsigned char v,
-                                        FormatConversionSpecImpl conv,
-                                        FormatSinkImpl* sink);
+CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv,
+                                    FormatSinkImpl* sink);
+CharConvertResult FormatConvertImpl(signed char v,
+                                    FormatConversionSpecImpl conv,
+                                    FormatSinkImpl* sink);
+CharConvertResult FormatConvertImpl(unsigned char v,
+                                    FormatConversionSpecImpl conv,
+                                    FormatSinkImpl* sink);
 
 // Ints.
 IntegralConvertResult FormatConvertImpl(short v,  // NOLINT
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc
index b186e7d..680517f 100644
--- a/absl/strings/internal/str_format/checker_test.cc
+++ b/absl/strings/internal/str_format/checker_test.cc
@@ -45,10 +45,10 @@
   EXPECT_EQ(ConvToString(conv), "sp");
 
   conv = ArgumentToConv<double>();
-  EXPECT_EQ(ConvToString(conv), "fFeEgGaA");
+  EXPECT_EQ(ConvToString(conv), "fFeEgGaAv");
 
   conv = ArgumentToConv<int>();
-  EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaA*");
+  EXPECT_EQ(ConvToString(conv), "cdiouxXfFeEgGaAv*");
 
   conv = ArgumentToConv<std::string*>();
   EXPECT_EQ(ConvToString(conv), "p");
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h
index f8d98bf..603bd49 100644
--- a/absl/strings/internal/str_format/extension.h
+++ b/absl/strings/internal/str_format/extension.h
@@ -292,6 +292,8 @@
     return conv_;
   }
 
+  void set_conversion_char(FormatConversionChar c) { conv_ = c; }
+
   // Returns the specified width. If width is unspecfied, it returns a negative
   // value.
   int width() const { return width_; }
diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc
index 1c93fdb..694c126 100644
--- a/absl/strings/internal/str_format/extension_test.cc
+++ b/absl/strings/internal/str_format/extension_test.cc
@@ -19,6 +19,7 @@
 #include <random>
 #include <string>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
@@ -95,4 +96,14 @@
 #undef X_VAL
 }
 
+TEST(FormatExtensionTest, SetConversionChar) {
+  absl::str_format_internal::FormatConversionSpecImpl spec;
+  EXPECT_EQ(spec.conversion_char(),
+            absl::str_format_internal::FormatConversionCharInternal::kNone);
+  spec.set_conversion_char(
+      absl::str_format_internal::FormatConversionCharInternal::d);
+  EXPECT_EQ(spec.conversion_char(),
+            absl::str_format_internal::FormatConversionCharInternal::d);
+}
+
 }  // namespace
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 4ac6aa2..9ef6983 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -42,6 +42,18 @@
   EXPECT_TRUE(Format(&sink, pc, 123));
   EXPECT_EQ("A format 123", sink);
 }
+
+TEST_F(FormatEntryPointTest, FormatWithV) {
+  std::string sink;
+  EXPECT_TRUE(Format(&sink, "A format %v", 123));
+  EXPECT_EQ("A format 123", sink);
+  sink.clear();
+
+  ParsedFormat<'v'> pc("A format %v");
+  EXPECT_TRUE(Format(&sink, pc, 123));
+  EXPECT_EQ("A format 123", sink);
+}
+
 TEST_F(FormatEntryPointTest, UntypedFormat) {
   constexpr const char* formats[] = {
     "",
@@ -87,6 +99,9 @@
 TEST_F(FormatEntryPointTest, StringFormatV) {
   std::string hello = "hello";
   EXPECT_EQ("hello", StrFormat("%v", hello));
+  EXPECT_EQ("123", StrFormat("%v", 123));
+  constexpr absl::string_view view("=%v=", 4);
+  EXPECT_EQ("=123=", StrFormat(view, 123));
 }
 
 TEST_F(FormatEntryPointTest, AppendFormat) {
@@ -96,6 +111,13 @@
   EXPECT_EQ("123", r);
 }
 
+TEST_F(FormatEntryPointTest, AppendFormatWithV) {
+  std::string s;
+  std::string& r = StrAppendFormat(&s, "%v", 123);
+  EXPECT_EQ(&s, &r);  // should be same object
+  EXPECT_EQ("123", r);
+}
+
 TEST_F(FormatEntryPointTest, AppendFormatFail) {
   std::string s = "orig";
 
@@ -108,6 +130,17 @@
                 {&arg, 1}));
 }
 
+TEST_F(FormatEntryPointTest, AppendFormatFailWithV) {
+  std::string s = "orig";
+
+  UntypedFormatSpec format(" more %v");
+  FormatArgImpl arg("not an int");
+
+  EXPECT_EQ("orig",
+            str_format_internal::AppendPack(
+                &s, str_format_internal::UntypedFormatSpecImpl::Extract(format),
+                {&arg, 1}));
+}
 
 TEST_F(FormatEntryPointTest, ManyArgs) {
   EXPECT_EQ("24", StrFormat("%24$d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
@@ -128,6 +161,15 @@
   EXPECT_EQ("=123=", StrFormat(ParsedFormat<'d'>(view), 123));
 }
 
+TEST_F(FormatEntryPointTest, PreparsedWithV) {
+  ParsedFormat<'v'> pc("%v");
+  EXPECT_EQ("123", StrFormat(pc, 123));
+  // rvalue ok?
+  EXPECT_EQ("123", StrFormat(ParsedFormat<'v'>("%v"), 123));
+  constexpr absl::string_view view("=%v=", 4);
+  EXPECT_EQ("=123=", StrFormat(ParsedFormat<'v'>(view), 123));
+}
+
 TEST_F(FormatEntryPointTest, FormatCountCapture) {
   int n = 0;
   EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
@@ -136,6 +178,14 @@
   EXPECT_EQ(3, n);
 }
 
+TEST_F(FormatEntryPointTest, FormatCountCaptureWithV) {
+  int n = 0;
+  EXPECT_EQ("", StrFormat("%n", FormatCountCapture(&n)));
+  EXPECT_EQ(0, n);
+  EXPECT_EQ("123", StrFormat("%v%n", 123, FormatCountCapture(&n)));
+  EXPECT_EQ(3, n);
+}
+
 TEST_F(FormatEntryPointTest, FormatCountCaptureWrongType) {
   // Should reject int*.
   int n = 0;
@@ -148,6 +198,18 @@
                     absl::MakeSpan(args)));
 }
 
+TEST_F(FormatEntryPointTest, FormatCountCaptureWrongTypeWithV) {
+  // Should reject int*.
+  int n = 0;
+  UntypedFormatSpec format("%v%n");
+  int i = 123, *ip = &n;
+  FormatArgImpl args[2] = {FormatArgImpl(i), FormatArgImpl(ip)};
+
+  EXPECT_EQ("", str_format_internal::FormatPack(
+                    str_format_internal::UntypedFormatSpecImpl::Extract(format),
+                    absl::MakeSpan(args)));
+}
+
 TEST_F(FormatEntryPointTest, FormatCountCaptureMultiple) {
   int n1 = 0;
   int n2 = 0;
@@ -212,23 +274,33 @@
 }
 
 TEST_F(FormatEntryPointTest, StreamWithV) {
-  const std::string format = "%d %u %c %v %f %g";
+  const std::string formats[] = {
+      "",
+      "a",
+      "%v %u %c %v %f %v",
+  };
 
-  const std::string format_for_buf = "%d %u %c %s %f %g";
+  const std::string formats_for_buf[] = {
+      "",
+      "a",
+      "%d %u %c %s %f %g",
+  };
 
   std::string buf(4096, '\0');
-  const auto parsed =
-      ParsedFormat<'d', 'u', 'c', 'v', 'f', 'g'>::NewAllowIgnored(format);
-  std::ostringstream oss;
-  oss << StreamFormat(*parsed, 123, 3, 49,
-                      absl::string_view("multistreaming!!!"), 1.01, 1.01);
-  int fmt_result =
-      snprintf(&*buf.begin(), buf.size(), format_for_buf.c_str(),  //
-               123, 3, 49, "multistreaming!!!", 1.01, 1.01);
-  ASSERT_TRUE(oss) << format;
-  ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
-      << fmt_result;
-  EXPECT_EQ(buf.c_str(), oss.str());
+  for (auto i = 0; i < ABSL_ARRAYSIZE(formats); ++i) {
+    const auto parsed =
+        ParsedFormat<'v', 'u', 'c', 'v', 'f', 'v'>::NewAllowIgnored(formats[i]);
+    std::ostringstream oss;
+    oss << StreamFormat(*parsed, 123, 3, 49,
+                        absl::string_view("multistreaming!!!"), 1.01, 1.01);
+    int fmt_result =
+        snprintf(&*buf.begin(), buf.size(), formats_for_buf[i].c_str(),  //
+                 123, 3, 49, "multistreaming!!!", 1.01, 1.01);
+    ASSERT_TRUE(oss) << formats[i];
+    ASSERT_TRUE(fmt_result >= 0 && static_cast<size_t>(fmt_result) < buf.size())
+        << fmt_result;
+    EXPECT_EQ(buf.c_str(), oss.str());
+  }
 }
 
 TEST_F(FormatEntryPointTest, StreamOk) {
@@ -238,6 +310,13 @@
   EXPECT_TRUE(oss.good());
 }
 
+TEST_F(FormatEntryPointTest, StreamOkWithV) {
+  std::ostringstream oss;
+  oss << StreamFormat("hello %v", 123);
+  EXPECT_EQ("hello 123", oss.str());
+  EXPECT_TRUE(oss.good());
+}
+
 TEST_F(FormatEntryPointTest, StreamFail) {
   std::ostringstream oss;
   UntypedFormatSpec format("hello %d");
@@ -248,6 +327,16 @@
   EXPECT_TRUE(oss.fail());
 }
 
+TEST_F(FormatEntryPointTest, StreamFailWithV) {
+  std::ostringstream oss;
+  UntypedFormatSpec format("hello %v");
+  FormatArgImpl arg("non-numeric");
+  oss << str_format_internal::Streamable(
+      str_format_internal::UntypedFormatSpecImpl::Extract(format), {&arg, 1});
+  EXPECT_EQ("hello ", oss.str());  // partial write
+  EXPECT_TRUE(oss.fail());
+}
+
 std::string WithSnprintf(const char* fmt, ...) {
   std::string buf;
   buf.resize(128);
@@ -425,19 +514,19 @@
   EXPECT_EQ(result, 11);
   EXPECT_EQ(std::string(buffer), "STRING: ABC");
 
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456);
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 123456);
   EXPECT_EQ(result, 14);
   EXPECT_EQ(std::string(buffer), "NUMBER: 123456");
 
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 1234567);
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 1234567);
   EXPECT_EQ(result, 15);
   EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
 
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 12345678);
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 12345678);
   EXPECT_EQ(result, 16);
   EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
 
-  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %d", 123456789);
+  result = SNPrintF(buffer, sizeof(buffer), "NUMBER: %v", 123456789);
   EXPECT_EQ(result, 17);
   EXPECT_EQ(std::string(buffer), "NUMBER: 1234567");
 
@@ -451,7 +540,7 @@
   std::string s = absl::StrFormat("%s, %d!", "Hello", 123);
   EXPECT_EQ("Hello, 123!", s);
   std::string hello = "Hello";
-  std::string s2 = absl::StrFormat("%v, %d!", hello, 123);
+  std::string s2 = absl::StrFormat("%v, %v!", hello, 123);
   EXPECT_EQ("Hello, 123!", s2);
   // The format of a replacement is
   // '%'[position][flags][width['.'precision]][length_modifier][format]
@@ -480,13 +569,18 @@
   EXPECT_EQ(StrFormat("%d", int{10}), "10");
   EXPECT_EQ(StrFormat("%d", long{10}), "10");  // NOLINT
   EXPECT_EQ(StrFormat("%d", uint64_t{10}), "10");
+  EXPECT_EQ(StrFormat("%v", int{10}), "10");
+  EXPECT_EQ(StrFormat("%v", long{10}), "10");  // NOLINT
+  EXPECT_EQ(StrFormat("%v", uint64_t{10}), "10");
   //     d,i - signed decimal          Eg: -10 -> "-10"
   EXPECT_EQ(StrFormat("%d", -10), "-10");
   EXPECT_EQ(StrFormat("%i", -10), "-10");
+  EXPECT_EQ(StrFormat("%v", -10), "-10");
   //      o  - octal                   Eg:  10 -> "12"
   EXPECT_EQ(StrFormat("%o", 10), "12");
   //      u  - unsigned decimal        Eg:  10 -> "10"
   EXPECT_EQ(StrFormat("%u", 10), "10");
+  EXPECT_EQ(StrFormat("%v", 10), "10");
   //     x/X - lower,upper case hex    Eg:  10 -> "a"/"A"
   EXPECT_EQ(StrFormat("%x", 10), "a");
   EXPECT_EQ(StrFormat("%X", 10), "A");
@@ -511,6 +605,8 @@
   EXPECT_EQ(StrFormat("%g", .01), "0.01");
   EXPECT_EQ(StrFormat("%g", 1e10), "1e+10");
   EXPECT_EQ(StrFormat("%G", 1e10), "1E+10");
+  EXPECT_EQ(StrFormat("%v", .01), "0.01");
+  EXPECT_EQ(StrFormat("%v", 1e10), "1e+10");
   //     a/A - lower,upper case hex    Eg: -3.0 -> "-0x1.8p+1"/"-0X1.8P+1"
 
 // On Android platform <=21, there is a regression in hexfloat formatting.
@@ -598,10 +694,10 @@
 }
 
 TEST_F(ParsedFormatTest, SimpleCheckedWithV) {
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]",
-            SummarizeParsedFormat(ParsedFormat<'d'>("ABC%dDEF")));
-  EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}",
-            SummarizeParsedFormat(ParsedFormat<'v', 'd', 'f'>("%vFFF%dZZZ%f")));
+  EXPECT_EQ("[ABC]{v:1$v}[DEF]",
+            SummarizeParsedFormat(ParsedFormat<'v'>("ABC%vDEF")));
+  EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}",
+            SummarizeParsedFormat(ParsedFormat<'v', 'v', 'f'>("%vFFF%vZZZ%f")));
   EXPECT_EQ("{v:1$v}[ ]{.*d:3$.2$*d}",
             SummarizeParsedFormat(ParsedFormat<'v', '*', 'd'>("%v %.*d")));
 }
@@ -637,20 +733,20 @@
 }
 
 TEST_F(ParsedFormatTest, SimpleUncheckedCorrectWithV) {
-  auto f = ParsedFormat<'d'>::New("ABC%dDEF");
+  auto f = ParsedFormat<'v'>::New("ABC%vDEF");
   ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+  EXPECT_EQ("[ABC]{v:1$v}[DEF]", SummarizeParsedFormat(*f));
 
-  std::string format = "%vFFF%dZZZ%f";
-  auto f2 = ParsedFormat<'v', 'd', 'f'>::New(format);
+  std::string format = "%vFFF%vZZZ%f";
+  auto f2 = ParsedFormat<'v', 'v', 'f'>::New(format);
 
   ASSERT_TRUE(f2);
-  EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+  EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
 
-  f2 = ParsedFormat<'v', 'd', 'f'>::New("%v %d %f");
+  f2 = ParsedFormat<'v', 'v', 'f'>::New("%v %v %f");
 
   ASSERT_TRUE(f2);
-  EXPECT_EQ("{v:1$v}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+  EXPECT_EQ("{v:1$v}[ ]{v:2$v}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
 }
 
 TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgs) {
@@ -668,6 +764,18 @@
   EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
 }
 
+TEST_F(ParsedFormatTest, SimpleUncheckedIgnoredArgsWithV) {
+  EXPECT_FALSE((ParsedFormat<'v', 'v'>::New("ABC")));
+  EXPECT_FALSE((ParsedFormat<'v', 'v'>::New("%vABC")));
+  EXPECT_FALSE((ParsedFormat<'v', 's'>::New("ABC%2$s")));
+  auto f = ParsedFormat<'v', 'v'>::NewAllowIgnored("ABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+  f = ParsedFormat<'v', 'v'>::NewAllowIgnored("%vABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("{v:1$v}[ABC]", SummarizeParsedFormat(*f));
+}
+
 TEST_F(ParsedFormatTest, SimpleUncheckedUnsupported) {
   EXPECT_FALSE(ParsedFormat<'d'>::New("%1$d %1$x"));
   EXPECT_FALSE(ParsedFormat<'x'>::New("%1$d %1$x"));
@@ -683,12 +791,12 @@
 }
 
 TEST_F(ParsedFormatTest, SimpleUncheckedIncorrectWithV) {
-  EXPECT_FALSE(ParsedFormat<'d'>::New(""));
+  EXPECT_FALSE(ParsedFormat<'v'>::New(""));
 
-  EXPECT_FALSE(ParsedFormat<'d'>::New("ABC%dDEF%d"));
+  EXPECT_FALSE(ParsedFormat<'v'>::New("ABC%vDEF%v"));
 
-  std::string format = "%vFFF%dZZZ%f";
-  EXPECT_FALSE((ParsedFormat<'v', 'd', 'g'>::New(format)));
+  std::string format = "%vFFF%vZZZ%f";
+  EXPECT_FALSE((ParsedFormat<'v', 'v', 'g'>::New(format)));
 }
 
 #if defined(__cpp_nontype_template_parameter_auto)
@@ -739,17 +847,17 @@
 }
 
 TEST_F(ParsedFormatTest, ExtendedTypingWithV) {
-  EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New(""));
-  ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d"));
-  auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::v>::New("%d%v");
+  EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::v>::New(""));
+  ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::v>::New("%v"));
+  auto v1 = ParsedFormat<'v', absl::FormatConversionCharSet::v>::New("%v%v");
   ASSERT_TRUE(v1);
-  auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 'v'>::New("%d%v");
+  auto v2 = ParsedFormat<absl::FormatConversionCharSet::v, 'v'>::New("%v%v");
   ASSERT_TRUE(v2);
-  auto v3 = ParsedFormat<absl::FormatConversionCharSet::d |
+  auto v3 = ParsedFormat<absl::FormatConversionCharSet::v |
                              absl::FormatConversionCharSet::v,
-                         'v'>::New("%d%v");
+                         'v'>::New("%v%v");
   ASSERT_TRUE(v3);
-  auto v4 = ParsedFormat<absl::FormatConversionCharSet::d |
+  auto v4 = ParsedFormat<absl::FormatConversionCharSet::v |
                              absl::FormatConversionCharSet::v,
                          'v'>::New("%v%v");
   ASSERT_TRUE(v4);
@@ -799,24 +907,24 @@
 
 TEST_F(ParsedFormatTest, UncheckedCorrectWithV) {
   auto f =
-      ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF");
+      ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New("ABC%vDEF");
   ASSERT_TRUE(f);
-  EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
+  EXPECT_EQ("[ABC]{v:1$v}[DEF]", SummarizeParsedFormat(*f));
 
-  std::string format = "%vFFF%dZZZ%f";
+  std::string format = "%vFFF%vZZZ%f";
   auto f2 = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d,
+      absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
       absl::FormatConversionCharSet::kFloating>::New(format);
 
   ASSERT_TRUE(f2);
-  EXPECT_EQ("{v:1$v}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
+  EXPECT_EQ("{v:1$v}[FFF]{v:2$v}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
 
   f2 = ExtendedParsedFormat<
-      absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::d,
-      absl::FormatConversionCharSet::kFloating>::New("%v %d %f");
+      absl::FormatConversionCharSet::v, absl::FormatConversionCharSet::v,
+      absl::FormatConversionCharSet::kFloating>::New("%v %v %f");
 
   ASSERT_TRUE(f2);
-  EXPECT_EQ("{v:1$v}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
+  EXPECT_EQ("{v:1$v}[ ]{v:2$v}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
 }
 
 TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
@@ -846,6 +954,28 @@
   EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
 }
 
+TEST_F(ParsedFormatTest, UncheckedIgnoredArgsWithV) {
+  EXPECT_FALSE(
+      (ExtendedParsedFormat<absl::FormatConversionCharSet::v,
+                            absl::FormatConversionCharSet::v>::New("ABC")));
+  EXPECT_FALSE(
+      (ExtendedParsedFormat<absl::FormatConversionCharSet::v,
+                            absl::FormatConversionCharSet::v>::New("%vABC")));
+  EXPECT_FALSE((ExtendedParsedFormat<absl::FormatConversionCharSet::v,
+                                     absl::FormatConversionCharSet::s>::
+                    New("ABC%2$s")));
+  auto f = ExtendedParsedFormat<
+      absl::FormatConversionCharSet::v,
+      absl::FormatConversionCharSet::v>::NewAllowIgnored("ABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
+  f = ExtendedParsedFormat<
+      absl::FormatConversionCharSet::v,
+      absl::FormatConversionCharSet::v>::NewAllowIgnored("%vABC");
+  ASSERT_TRUE(f);
+  EXPECT_EQ("{v:1$v}[ABC]", SummarizeParsedFormat(*f));
+}
+
 TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
   auto dx =
       ExtendedParsedFormat<absl::FormatConversionCharSet::d |
@@ -873,15 +1003,14 @@
 }
 
 TEST_F(ParsedFormatTest, UncheckedIncorrectWithV) {
-  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(""));
+  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New(""));
 
-  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(
-      "ABC%dDEF%d"));
+  EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::v>::New(
+      "ABC%vDEF%v"));
 
-  std::string format = "%vFFF%dZZZ%f";
+  std::string format = "%vFFF%vZZZ%f";
   EXPECT_FALSE(
       (ExtendedParsedFormat<absl::FormatConversionCharSet::v,
-                            absl::FormatConversionCharSet::d,
                             absl::FormatConversionCharSet::g>::New(format)));
 }