Skip to content

[Docs] Show formatv provider options in programmer's manual#199744

Open
Nerixyz wants to merge 1 commit into
llvm:mainfrom
Nerixyz:docs/formatv-options
Open

[Docs] Show formatv provider options in programmer's manual#199744
Nerixyz wants to merge 1 commit into
llvm:mainfrom
Nerixyz:docs/formatv-options

Conversation

@Nerixyz
Copy link
Copy Markdown
Contributor

@Nerixyz Nerixyz commented May 26, 2026

The documentation for formatv didn't show the possible options for common types like integers, floats, and ranges. It only showed a few examples. Personally, I always had to look up the way to set the number of digits for integers.

Unless you know where to search, it's not clear that the documentation is in FormatProviders.h, so I added it in the programmer's manual. This is also the document that shows when you search for llvm::formatv.
I added the examples and tables from the providers in FormatProviders.h. They should be the most common ones.

For the added examples, I added tests.

@llvmorg-github-actions
Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-support

Author: Nerixyz (Nerixyz)

Changes

The documentation for formatv didn't show the possible options for common types like integers, floats, and ranges. It only showed a few examples. Personally, I always had to look up the way to set the number of digits for integers.

Unless you know where to search, it's not clear that the documentation is in FormatProviders.h, so I added it in the programmer's manual. This is also the document that shows when you search for llvm::formatv.
I added the examples and tables from the providers in FormatProviders.h. They should be the most common ones.

For the added examples, I added tests.


Full diff: https://github.com/llvm/llvm-project/pull/199744.diff

3 Files Affected:

  • (modified) llvm/docs/ProgrammersManual.rst (+200-1)
  • (modified) llvm/include/llvm/Support/FormatProviders.h (+2-2)
  • (modified) llvm/unittests/Support/FormatVariadicTest.cpp (+47)
diff --git a/llvm/docs/ProgrammersManual.rst b/llvm/docs/ProgrammersManual.rst
index ce786a5b7b681..f95918bbd7d74 100644
--- a/llvm/docs/ProgrammersManual.rst
+++ b/llvm/docs/ProgrammersManual.rst
@@ -314,7 +314,7 @@ or ``+`` (right align).  The default is right aligned.
 
 ``style`` is an optional string consisting of a type specific that controls the
 formatting of the value.  For example, to format a floating point value as a percentage,
-you can use the style option ``P``.
+you can use the style option ``P``. See :ref:`formatv_provider_options` for common options.
 
 Custom formatting
 ^^^^^^^^^^^^^^^^^
@@ -404,6 +404,205 @@ doxygen documentation or by looking at the unit test suite.
   S = formatv("{0:$[+]}", make_range(V.begin(), V.end())); // S == "8+9+10"
   S = formatv("{0:$[ + ]@[x]}", make_range(V.begin(), V.end())); // S == "0x8 + 0x9 + 0xA"
 
+.. _formatv_provider_options:
+
+Format provider options
+^^^^^^^^^^^^^^^^^^^^^^^
+
+- **Integers**: The options string of an integral type has the grammar: ::
+
+    integer_options   :: [style][digits]
+    style             :: <see table below>
+    digits            :: <non-negative integer> 0-99
+
+  +-----------------+-----------------------+----------------------+--------------------+
+  |  ``style``      |     Meaning           |      Example         | ``digits`` Meaning |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  |                 |                       |  Input |  Output     |                    |
+  +=================+=======================+========+=============+====================+
+  |   ``x-``        | Hex no prefix, lower  |   42   |    ``2a``   | Minimum # digits   |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  |   ``X-``        | Hex no prefix, upper  |   42   |    ``2A``   | Minimum # digits   |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  | ``x+`` / ``x``  | Hex + prefix, lower   |   42   |   ``0x2a``  | Minimum # digits   |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  | ``X+`` / ``X``  | Hex + prefix, upper   |   42   |   ``0x2A``  | Minimum # digits   |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  | ``N`` / ``n``   | Digit grouped number  | 123456 | ``123,456`` | Ignored            |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  | ``D`` / ``d``   | Integer               | 100000 | ``100000``  | Ignored            |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  |``+D`` / ``+d``  | Integer with + prefix | 100000 | ``+100000`` | Ignored            |
+  |                 | for numbers ``>= 0``  |        |             |                    |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  |   ``+``         | Same as ``+D``/``+d`` |        |             |                    |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+  | (empty)         | Same as ``D``/``d``   |        |             |                    |
+  +-----------------+-----------------------+--------+-------------+--------------------+
+
+  .. Keep in sync with FormatVariadicTest.DocsInt
+
+  .. code-block:: c++
+
+    std::string S;
+    int I = 1234567;
+    S = formatv("{0}",     I);  // S == "1234567";
+    S = formatv("{0:8}",   I);  // S == "01234567";
+    S = formatv("{0:x}",   I);  // S == "0x12d687";
+    S = formatv("{0:x8}",  I);  // S == "0x0012d687";
+    S = formatv("{0:n}",   I);  // S == "1,234,567";
+    S = formatv("{0:+d}",  I);  // S == "+1234567";
+    S = formatv("{0:+d}", -I);  // S == "-1234567";
+
+- **Pointers**: The options string of a pointer type has the grammar: ::
+
+    pointer_options   :: [style][precision]
+    style             :: <see table below>
+    digits            :: <non-negative integer> 0-sizeof(void*)
+
+  +-----------------+--------------------------+---------------------------------+
+  | ``style``       |     Meaning              |      Example                    |
+  +-----------------+--------------------------+----------------+----------------+
+  |                 |                          |  Input         |  Output        |
+  +=================+==========================+================+================+
+  |   ``x-``        | Hex no prefix, lower     | ``0xDEADBEEF`` | ``deadbeef``   |
+  +-----------------+--------------------------+----------------+----------------+
+  |   ``X-``        | Hex no prefix, upper     | ``0xDEADBEEF`` | ``2ADEADBEEF`` |
+  +-----------------+--------------------------+----------------+----------------+
+  | ``x+`` / ``x``  | Hex + prefix, lower      | ``0xDEADBEEF`` | ``0xdeadbeef`` |
+  +-----------------+--------------------------+----------------+----------------+
+  | ``X+`` / ``X``  | Hex + prefix, upper      | ``0xDEADBEEF`` | ``0xDEADBEEF`` |
+  +-----------------+--------------------------+----------------+----------------+
+  | (empty)         | Same as ``X+`` / ``X``   |                |                |
+  +-----------------+--------------------------+----------------+----------------+
+
+  The default precision is the number of nibbles in a machine word, and in all
+  cases indicates the minimum number of nibbles to print.
+
+  .. Keep in sync with FormatVariadicTest.DocsPointer
+
+  .. code-block:: c++
+
+    std::string S;
+    void *P = (void*)0xABCD12;
+    S = formatv("{0}",    P);  // S == "0x0000000000ABCD12"; (sizeof(void*) == 8)
+    S = formatv("{0:8}",  P);  // S == "0x00ABCD12";
+    S = formatv("{0:x8}", P);  // S == "0x00abcd12";
+
+- **Strings**: This applies to C-style strings and string objects such as
+  ``std::string`` or ``llvm::StringRef``. The options string of a string type
+  has the grammar: ::
+
+    string_options :: [length]
+
+  where ``length`` is an optional integer specifying the maximum number of 
+  characters in the string to print.  If ``length`` is omitted, the string is
+  printed up to the null terminator.
+
+  .. Keep in sync with FormatVariadicTest.DocsStrings
+
+  .. code-block:: c++
+
+    std::string S;
+    llvm::StringRef V = "A very long string";
+    S = formatv("{0}",    V);  // S == "A very long string";
+    S = formatv("{0:6}",  V);  // S == "A very";
+
+- **Booleans**: The options string of a boolean type has the grammar: ::
+
+    bool_options :: "" | "Y" | "y" | "D" | "d" | "T" | "t"
+ 
+  +---------------+---------------------+
+  | Option        |     Meaning         |
+  +===============+=====================+
+  | ``Y``         | YES / NO            |
+  +---------------+---------------------+
+  | ``y``         | yes / no            |
+  +---------------+---------------------+
+  | ``D`` / ``d`` | Integer 0 or 1      |
+  +---------------+---------------------+
+  | ``T``         | TRUE / FALSE        |
+  +---------------+---------------------+
+  | ``t``         | true / false        |
+  +---------------+---------------------+
+  | (empty)       | Equivalent to ``t`` |
+  +---------------+---------------------+
+
+  .. Keep in sync with FormatVariadicTest.DocsBooleans
+
+  .. code-block:: c++
+
+    std::string S;
+    S = formatv("{0}",    true);   // S == "true";
+    S = formatv("{0:y}",  false);  // S == "no";
+
+- **Floating point numbers**: The options string of a floating point type has 
+  the grammar: ::
+
+    float_options   :: [style][precision]
+    style           :: <see table below>
+    precision       :: <non-negative integer> 0-99
+
+  +---------------+------------------------+----------------------+
+  | ``style``     |     Meaning            |      Example         |
+  +---------------+------------------------+--------+-------------+
+  |               |                        |  Input |  Output     |
+  +===============+========================+========+=============+
+  | ``P`` / ``p`` | Percentage             | 0.05   |  ``5.00%``  |
+  +---------------+------------------------+--------+-------------+
+  | ``F`` / ``f`` | Fixed point            | 1.0    |  ``1.00``   |
+  +---------------+------------------------+--------+-------------+
+  | ``E``         | Exponential with ``E`` | 100000 | ``1.0E+05`` |
+  +---------------+------------------------+--------+-------------+
+  | ``e``         | Exponential with ``e`` | 100000 | ``1.0e+05`` |
+  +---------------+------------------------+--------+-------------+
+  | (empty)       | Same as ``F`` / ``f``  |        |             |
+  +---------------+------------------------+--------+-------------+
+
+  The default precision is 6 for exponential (``E`` / ``e``) and 2 for
+  everything else.
+
+  .. Keep in sync with FormatVariadicTest.DocsFloats
+
+  .. code-block:: c++
+
+    std::string S;
+    float F = 0.05f;
+    S = formatv("{0}",    F);  // S == "0.05";
+    S = formatv("{0:e}",  F);  // S == "5.000000e-02";
+
+- ``llvm::iterator_range``: This will print an arbitrary range as a delimited
+  sequence of items. The options string of a range type has the grammar: ::
+ 
+    range_style       ::= [separator][element_style]
+    separator         ::= "$" delimeted_expr
+    element_style     ::= "@" delimeted_expr
+    delimeted_expr    ::= "[" expr "]" | "(" expr ")" | "<" expr ">"
+    expr              ::= <any string not containing delimeter>
+ 
+  where the ``separator`` expression is the string to insert between consecutive
+  items in the range and the ``element_style`` expression is the style 
+  specification to be used when formatting the underlying type.  The default 
+  separator if unspecified is ", ".  The syntax of the argument expression 
+  follows whatever grammar is dictated by the format provider or format adapter
+  used to format the value type.
+ 
+  Note that attempting to format an ``iterator_range<T>`` where no format
+  provider can be found for ``T`` will result in a compile error.
+
+  .. Keep in sync with FormatVariadicTest.DocsIteratorRange
+
+  .. code-block:: c++
+
+    std::string S;
+    std::vector V{1, 2, 4, 8, 16};
+    S = formatv("{0}", iterator_range(V));
+    // S == "1, 2, 4, 8, 16";
+    S = formatv("{0:@(x)}", iterator_range(V));
+    // S == "0x1, 0x2, 0x4, 0x8, 0x10";
+    S = formatv("{0:$[,]@[2]}", iterator_range(V));
+    // S == "01,02,04,08,16";
+
 .. _error_apis:
 
 Error handling
diff --git a/llvm/include/llvm/Support/FormatProviders.h b/llvm/include/llvm/Support/FormatProviders.h
index 78eeec76cf79b..6f416ac5e0c99 100644
--- a/llvm/include/llvm/Support/FormatProviders.h
+++ b/llvm/include/llvm/Support/FormatProviders.h
@@ -343,7 +343,7 @@ struct range_item_has_provider
 ///
 /// The options string of a range type has the grammar:
 ///
-///   range_style       ::= [separator] [element_style]
+///   range_style       ::= [separator][element_style]
 ///   separator         ::= "$" delimeted_expr
 ///   element_style     ::= "@" delimeted_expr
 ///   delimeted_expr    ::= "[" expr "]" | "(" expr ")" | "<" expr ">"
@@ -352,7 +352,7 @@ struct range_item_has_provider
 /// where the separator expression is the string to insert between consecutive
 /// items in the range and the argument expression is the Style specification to
 /// be used when formatting the underlying type.  The default separator if
-/// unspecified is ' ' (space).  The syntax of the argument expression follows
+/// unspecified is ", ".  The syntax of the argument expression follows
 /// whatever grammar is dictated by the format provider or format adapter used
 /// to format the value type.
 ///
diff --git a/llvm/unittests/Support/FormatVariadicTest.cpp b/llvm/unittests/Support/FormatVariadicTest.cpp
index ea549e7553b0c..1f33504d93499 100644
--- a/llvm/unittests/Support/FormatVariadicTest.cpp
+++ b/llvm/unittests/Support/FormatVariadicTest.cpp
@@ -903,6 +903,53 @@ TEST(FormatVariadicTest, Validate) {
 #endif // NDEBUG
 }
 
+TEST(FormatVariadicTest, DocsInt) {
+  int I = 1234567;
+  EXPECT_EQ(formatv("{0}", I).str(), "1234567");
+  EXPECT_EQ(formatv("{0:8}", I).str(), "01234567");
+  EXPECT_EQ(formatv("{0:x}", I).str(), "0x12d687");
+  EXPECT_EQ(formatv("{0:x8}", I).str(), "0x0012d687");
+  EXPECT_EQ(formatv("{0:n}", I).str(), "1,234,567");
+  EXPECT_EQ(formatv("{0:+d}", I).str(), "+1234567");
+  EXPECT_EQ(formatv("{0:+d}", -I).str(), "-1234567");
+}
+
+TEST(FormatVariadicTest, DocsPointers) {
+  void *P = (void *)0xABCD12;
+  if constexpr (sizeof(void *) == 8) {
+    EXPECT_EQ(formatv("{0}", P).str(), "0x0000000000ABCD12");
+  } else {
+    EXPECT_EQ(formatv("{0}", P).str(), "0x00ABCD12");
+  }
+  EXPECT_EQ(formatv("{0:8}", P).str(), "0x00ABCD12");
+  EXPECT_EQ(formatv("{0:x8}", P).str(), "0x00abcd12");
+}
+
+TEST(FormatVariadicTest, DocsStrings) {
+  llvm::StringRef V = "A very long string";
+  EXPECT_EQ(formatv("{0}", V).str(), "A very long string");
+  EXPECT_EQ(formatv("{0:6}", V).str(), "A very");
+}
+
+TEST(FormatVariadicTest, DocsBooleans) {
+  EXPECT_EQ(formatv("{0}", true).str(), "true");
+  EXPECT_EQ(formatv("{0:y}", false).str(), "no");
+}
+
+TEST(FormatVariadicTest, DocsFloats) {
+  float F = 0.05f;
+  EXPECT_EQ(formatv("{0}", F).str(), "0.05");
+  EXPECT_EQ(formatv("{0:e}", F).str(), "5.000000e-02");
+}
+
+TEST(FormatVariadicTest, DocsIteratorRange) {
+  std::vector V{1, 2, 4, 8, 16};
+  EXPECT_EQ(formatv("{0}", iterator_range(V)).str(), "1, 2, 4, 8, 16");
+  EXPECT_EQ(formatv("{0:@(x)}", iterator_range(V)).str(),
+            "0x1, 0x2, 0x4, 0x8, 0x10");
+  EXPECT_EQ(formatv("{0:$[,]@[2]}", iterator_range(V)).str(), "01,02,04,08,16");
+}
+
 namespace {
 
 enum class Base { First };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant