Explicitly disallows modifiers for use with %v.

PiperOrigin-RevId: 475636693
Change-Id: Idb7c2b9c36ad8e59f24ff7df179a207d301d9e89
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc
index 3d98733..f9bb661 100644
--- a/absl/strings/internal/str_format/parser.cc
+++ b/absl/strings/internal/str_format/parser.cc
@@ -202,6 +202,10 @@
 
   auto tag = GetTagForChar(c);
 
+  if (*(pos - 1) == 'v' && *(pos - 2) != '%') {
+    return nullptr;
+  }
+
   if (ABSL_PREDICT_FALSE(!tag.is_conv())) {
     if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr;
 
@@ -312,11 +316,11 @@
     std::initializer_list<FormatConversionCharSet> convs) const {
   std::unordered_set<int> used;
   auto add_if_valid_conv = [&](int pos, char c) {
-      if (static_cast<size_t>(pos) > convs.size() ||
-          !Contains(convs.begin()[pos - 1], c))
-        return false;
-      used.insert(pos);
-      return true;
+    if (static_cast<size_t>(pos) > convs.size() ||
+        !Contains(convs.begin()[pos - 1], c))
+      return false;
+    used.insert(pos);
+    return true;
   };
   for (const ConversionItem &item : items_) {
     if (!item.is_conversion) continue;
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index 9ef6983..2774cfe 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -1020,6 +1020,17 @@
                             absl::FormatConversionCharSet::o>::New("%1$d %o")));
 }
 
+TEST_F(ParsedFormatTest, DisallowModifiersWithV) {
+  auto f = ParsedFormat<'v'>::New("ABC%80vDEF");
+  EXPECT_EQ(f, nullptr);
+
+  f = ParsedFormat<'v'>::New("ABC%0vDEF");
+  EXPECT_EQ(f, nullptr);
+
+  f = ParsedFormat<'v'>::New("ABC%.1vDEF");
+  EXPECT_EQ(f, nullptr);
+}
+
 using FormatWrapperTest = ::testing::Test;
 
 // Plain wrapper for StrFormat.