[libcxx-commits] [libcxx] 04a3146 - [libc++][format] Fixes string-literal formatting.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jun 1 08:49:14 PDT 2022


Author: Mark de Wever
Date: 2022-06-01T17:49:09+02:00
New Revision: 04a3146caa0f25014c3c9c5ebce5b5a292d77fc9

URL: https://github.com/llvm/llvm-project/commit/04a3146caa0f25014c3c9c5ebce5b5a292d77fc9
DIFF: https://github.com/llvm/llvm-project/commit/04a3146caa0f25014c3c9c5ebce5b5a292d77fc9.diff

LOG: [libc++][format] Fixes string-literal formatting.

Formatting a string-literal had an off-by-one issue where the NUL
terminator became part of the formatted output.

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D126665

Added: 
    

Modified: 
    libcxx/include/__format/format_arg_store.h
    libcxx/test/std/utilities/format/format.functions/format_tests.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__format/format_arg_store.h b/libcxx/include/__format/format_arg_store.h
index 4a3fcd4ff339d..28c9c28d96671 100644
--- a/libcxx/include/__format/format_arg_store.h
+++ b/libcxx/include/__format/format_arg_store.h
@@ -17,8 +17,6 @@
 #include <__config>
 #include <__format/concepts.h>
 #include <__format/format_arg.h>
-#include <__iterator/data.h>
-#include <__iterator/size.h>
 #include <cstring>
 #include <string>
 #include <string_view>
@@ -173,13 +171,15 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp&& __val
   else if constexpr (__arg == __arg_t::__unsigned_long_long)
     return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
   else if constexpr (__arg == __arg_t::__string_view)
-    // When the _Traits or _Allocator are 
diff erent an implicit conversion will
-    // fail.
-    //
-    // Note since the input can be an array use the non-member functions to
-    // extract the constructor arguments.
-    return basic_format_arg<_Context>{
-        __arg, basic_string_view<typename _Context::char_type>{_VSTD::data(__value), _VSTD::size(__value)}};
+    // Using std::size on a character array will add the NUL-terminator to the size.
+    if constexpr (is_array_v<remove_cvref_t<_Tp>>)
+      return basic_format_arg<_Context>{
+          __arg, basic_string_view<typename _Context::char_type>{__value, extent_v<remove_cvref_t<_Tp>> - 1}};
+    else
+      // When the _Traits or _Allocator are 
diff erent an implicit conversion will
+      // fail.
+      return basic_format_arg<_Context>{
+          __arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}};
   else if constexpr (__arg == __arg_t::__ptr)
     return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
   else if constexpr (__arg == __arg_t::__handle)

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h
index 0c6993595b5e9..4ee580bb2a942 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_tests.h
+++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h
@@ -174,8 +174,10 @@ case #T[0]:
   return result;
 }
 
-template <class CharT, class T, class TestFunction, class ExceptionTest>
-void format_test_string(T world, T universe, TestFunction check, ExceptionTest check_exception) {
+// Using a const ref for world and universe so a string literal will be a character array.
+// When passed as character array W and U have 
diff erent types.
+template <class CharT, class W, class U, class TestFunction, class ExceptionTest>
+void format_test_string(const W& world, const U& universe, TestFunction check, ExceptionTest check_exception) {
 
   // *** Valid input tests ***
   // Unsed argument is ignored. TODO FMT what does the Standard mandate?
@@ -291,6 +293,44 @@ template <class CharT, class TestFunction>
 void format_test_string_unicode(TestFunction check) {
   (void)check;
 #ifndef TEST_HAS_NO_UNICODE
+  // Make sure all possible types are tested. For clarity don't use macros.
+  if constexpr (std::same_as<CharT, char>) {
+    const char* c_string = "aßc";
+    check.template operator()<"{:*^5}">(SV("*aßc*"), c_string);
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), c_string);
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), const_cast<char*>(c_string));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), const_cast<char*>(c_string));
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), "aßc");
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), "aßc");
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), std::string("aßc"));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), std::string("aßc"));
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), std::string_view("aßc"));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), std::string_view("aßc"));
+  }
+#  ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  else {
+    const wchar_t* c_string = L"aßc";
+    check.template operator()<"{:*^5}">(SV("*aßc*"), c_string);
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), c_string);
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), const_cast<wchar_t*>(c_string));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), const_cast<wchar_t*>(c_string));
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), L"aßc");
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), L"aßc");
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), std::wstring(L"aßc"));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), std::wstring(L"aßc"));
+
+    check.template operator()<"{:*^5}">(SV("*aßc*"), std::wstring_view(L"aßc"));
+    check.template operator()<"{:*^4.2}">(SV("*aß*"), std::wstring_view(L"aßc"));
+  }
+#  endif
+
   // ß requires one column
   check.template operator()<"{}">(SV("aßc"), STR("aßc"));
 
@@ -330,9 +370,14 @@ void format_string_tests(TestFunction check, ExceptionTest check_exception) {
   std::basic_string<CharT> world = STR("world");
   std::basic_string<CharT> universe = STR("universe");
 
-  // Testing the char const[] is a bit tricky due to array to pointer decay.
-  // Since there are separate tests in format.formatter.spec the array is not
-  // tested here.
+  // Test a string literal in a way it won't decay to a pointer.
+  if constexpr (std::same_as<CharT, char>)
+    format_test_string<CharT>("world", "universe", check, check_exception);
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  else
+    format_test_string<CharT>(L"world", L"universe", check, check_exception);
+#endif
+
   format_test_string<CharT>(world.c_str(), universe.c_str(), check, check_exception);
   format_test_string<CharT>(const_cast<CharT*>(world.c_str()), const_cast<CharT*>(universe.c_str()), check,
                             check_exception);


        


More information about the libcxx-commits mailing list