tinyformat: Add compile-time checking for literal format strings #31174

pull ryanofsky wants to merge 2 commits into bitcoin:master from ryanofsky:pr/tcheck changing 3 files +99 −62
  1. ryanofsky commented at 11:56 pm on October 28, 2024: contributor

    Add compile-time checking for literal format strings passed to strprintf and tfm::format to make sure the right number of format arguments are passed.

    There is still no compile-time checking if non-literal std::string or bilingual_str format strings are passed, but this is improved in other PRs:

    • #31061 implements compile-time checking for translated strings
    • #31072 increases compile-time checking by using literal strings as format strings, instead of std::string and bilingual_str
    • #31149 may drop the std::string overload for strprintf to require compile-time checking
  2. util: Support dynamic width & precision in ConstevalFormatString
    This is needed in the next commit to add compile-time checking to strprintf
    calls, because bitcoin-cli.cpp uses dynamic width in many format strings.
    
    This change is easiest to review ignoring whitespace.
    
    Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
    Co-authored-by: Hodlinator <172445034+hodlinator@users.noreply.github.com>
    Co-authored-by: l0rinc <pap.lorinc@gmail.com>
    184f34f2d0
  3. tinyformat: Add compile-time checking for literal format strings
    Co-authored-by: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>
    fe39acf88f
  4. DrahtBot commented at 11:56 pm on October 28, 2024: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/31174.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK maflcko, l0rinc, hodlinator

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #31061 (refactor: Check translatable format strings at compile-time by maflcko)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  5. in src/test/util_string_tests.cpp:69 in 97dd5fe512 outdated
    68+    PassFmt<3>("%2$*3$.9d");
    69+    PassFmt<3>("%2$.*3$d");
    70+    PassFmt<3>("%2$9.*3$d");
    71+    PassFmt<3>("%2$+9.*3$d");
    72+    PassFmt<4>("%3$*2$.*4$f");
    73 
    


    maflcko commented at 9:46 am on October 29, 2024:

    nit in 97dd5fe5128592332c83998825bbeda063815120: in the commit message: Missing needed *in the* next commit.

    Also, it would be good to add some failing test cases for this stuff.

    For example, "%1" should fail, because terminate called after throwing an instance of 'tinyformat::format_error' what(): tinyformat: Conversion spec incorrectly terminated by end of string. See https://godbolt.org/z/1eehh1oTv . Also see 51c56e8b38033b96fb3930c2bcba3add2047d324


    maflcko commented at 9:49 am on October 29, 2024:
    Other failing test cases to possibly add: "%*", "%+*", "%.*", …

    maflcko commented at 10:12 am on October 29, 2024:

    Also, some more “fancy” passing test cases:

    • PassFmt<3>("'%1$- 0+*3$.*2$f'"); (space and 0 are ignored, confusing, but valid)
    • PassFmt<3>("'%- 0+*.*f'"); (same, without pos specs)

    ryanofsky commented at 2:44 pm on October 29, 2024:

    re: #31174 (review)

    Thanks, I added the suggested test cases. The suggested tests that “should fail” didn’t actually fail with 97dd5fe5128592332c83998825bbeda063815120 because it accepted \0 as a valid specifier character, so I added an extra check to prevent that. I also added extra code to consume digits after . otherwise format strings like "%1.2" would be accepted treating 2 as the specifier.

  6. in src/tinyformat.h:186 in 1d16d6e6ba outdated
    178@@ -178,6 +179,23 @@ namespace tfm = tinyformat;
    179 
    180 namespace tinyformat {
    181 
    182+// Added for Bitcoin Core. Wrapper type for format strings to allow compile-time checks.
    183+struct FormatString {
    184+    const char* fmt;
    185+    operator const char*() { return fmt; }
    186+};
    


    maflcko commented at 10:33 am on October 29, 2024:

    Nit in 1d16d6e6bac994fed7c695f530b9984edcd290bd: I don’t really see why this is useful. Maybe I am missing something, but everything compiles by just moving the two members to the derived struct:

     0diff --git a/src/tinyformat.h b/src/tinyformat.h
     1index 56c25d8f7f..6227cdeca0 100644
     2--- a/src/tinyformat.h
     3+++ b/src/tinyformat.h
     4@@ -179,21 +179,16 @@ namespace tfm = tinyformat;
     5 
     6 namespace tinyformat {
     7 
     8-// Added for Bitcoin Core. Wrapper type for format strings to allow compile-time checks.
     9-struct FormatString {
    10-    const char* fmt;
    11-    operator const char*() { return fmt; }
    12-};
    13-
    14 // Added for Bitcoin Core. Wrapper for checking format strings at compile time.
    15 // Unlike ConstevalFormatString this supports std::string for runtime string
    16 // formatting without compile time checks.
    17 template <unsigned num_params>
    18-struct FormatStringCheck : FormatString {
    19-    consteval FormatStringCheck(const char* str) : FormatString{util::ConstevalFormatString<num_params>{str}.fmt} {}
    20-    FormatStringCheck(const std::string& str) : FormatString{str.c_str()} {}
    21-    FormatStringCheck(FormatString str) : FormatString{str} {}
    22-    FormatStringCheck(util::ConstevalFormatString<num_params> str) : FormatString{str.fmt} {}
    23+struct FormatStringCheck {
    24+    consteval FormatStringCheck(const char* str) : fmt{util::ConstevalFormatString<num_params>{str}.fmt} {}
    25+    FormatStringCheck(const std::string& str) : fmt{str.c_str()} {}
    26+    FormatStringCheck(util::ConstevalFormatString<num_params> str) : fmt{str.fmt} {}
    27+    const char* fmt;
    28+    operator const char*() { return fmt; }
    29 };
    30 
    31 // Added for Bitcoin Core
    

    ryanofsky commented at 2:48 pm on October 29, 2024:

    re: #31174 (review)

    Thanks, applied patch. The reason for having a FormatString class was to provide a cleaner escape from compile-time checks strprintf(FormatString{"%*s"}, width, str) before the first commit was implemented. But it’s no longer necessary after that commit.

  7. maflcko approved
  8. maflcko commented at 10:34 am on October 29, 2024: member
    Nice. This looks less scary than expected. Left a nit to add more compile time checks, but this looks good either way.
  9. in src/util/string.h:44 in 1d16d6e6ba outdated
    54+            auto add_arg = [&] {
    55+                unsigned maybe_num{0};
    56+                while ('0' <= *it && *it <= '9') {
    57+                    maybe_num *= 10;
    58+                    maybe_num += *it - '0';
    59+                    ++it;
    


    l0rinc commented at 2:39 pm on October 29, 2024:
    we still have unbounded increment without checking the end (I though we’ve fixed this already, maybe it got stuck in the comments…)

    ryanofsky commented at 3:31 pm on October 29, 2024:

    re: #31174 (review)

    we still have unbounded increment without checking the end (I though we’ve fixed this already, maybe it got stuck in the comments…)

    Thanks. I fixed this by just switching the string type to const char* instead of string_view since tinyformat already assumes the string is null terminated.

    I think it would be possible to write a clean version of this code that used string_view, but it would have to be structured differently to avoid the need for it < str.end() checks everywhere.


    l0rinc commented at 11:01 am on October 30, 2024:


    l0rinc commented at 11:10 am on October 30, 2024:
    It is indeed now, thanks for checking.
  10. ryanofsky force-pushed on Oct 29, 2024
  11. ryanofsky commented at 3:48 pm on October 29, 2024: contributor
    Updated 1d16d6e6bac994fed7c695f530b9984edcd290bd -> e6086e00e32e486aaeeeb346ccca1377bbf647b2 (pr/tcheck.1 -> pr/tcheck.2, compare) addressing comments and making ConstEvalFormatString parsing stricter to reject incomplete specifiers. Updated e6086e00e32e486aaeeeb346ccca1377bbf647b2 -> e53829d3952c6ed275507a66e77636aad67d106b (pr/tcheck.2 -> pr/tcheck.3, compare) cleaning up whitespace and comments.
  12. ryanofsky force-pushed on Oct 29, 2024
  13. laanwj added the label Utils/log/libs on Oct 30, 2024
  14. in src/util/string.h:54 in e53829d395 outdated
    65+                unsigned maybe_num{0};
    66+                while ('0' <= *it && *it <= '9') {
    67+                    maybe_num *= 10;
    68+                    maybe_num += *it - '0';
    69+                    ++it;
    70+                };
    


    hodlinator commented at 10:24 am on October 30, 2024:
    0                }
    

    ryanofsky commented at 9:39 pm on November 4, 2024:

    re: #31174 (review)

    Thanks, updated

  15. hodlinator commented at 10:44 am on October 30, 2024: contributor

    Concept ACK e53829d3952c6ed275507a66e77636aad67d106b

    Cleanest attempt at increased compile time validation of format so far. When reviewing #31149 I had the gnawing feeling that more complete format string support would have reduced the diff, but pushed it away for expediency (an earlier attempt at more complete support was attempted in #30999).

  16. bitcoin deleted a comment on Oct 30, 2024
  17. hodlinator commented at 1:34 pm on October 30, 2024: contributor
    Rebased #30933 on top of this PR (as a separate branch for now) and the mismatches between our custom consteval checking and tinyformat are gone as far as our current tests go - rebased commit: 32d4cf37d5056dc42bbf317b059e10910b984b0e
  18. ryanofsky commented at 1:49 pm on October 30, 2024: contributor

    re: #31174 (comment)

    mismatches between our custom consteval checking and tinyformat are gone as far as our current tests go

    In case you do want a test with different behavior, I think you can use %n specifier which is not supported by tinyformat

  19. in src/util/string.h:94 in e53829d395 outdated
    113             }
    114+
    115+            if (*it == '\0') throw "Format specifier incorrectly terminated by end of string";
    116+
    117             // The remainder "[flags][width][.precision][length]type" of the
    118             // specifier is not checked. Parsing continues with the next '%'.
    


    hodlinator commented at 2:40 pm on October 30, 2024:
    Should this be removed?

    ryanofsky commented at 9:39 pm on November 4, 2024:

    re: #31174 (review)

    Should this be removed?

    I think it’s useful to document the format of the specifier, and it’s still true that the remainder of the specifier (length and type) is not checked. Happy to update this if there’s a specific suggestion, but I think the comment is still helpful and accurate so would not want to remove it.


    hodlinator commented at 2:15 pm on November 8, 2024:

    As you say, only the length and type are no longer checked. This PR now implements rudimentary checking of flags, width and precision.

    0            // Length and type in "[flags][width][.precision][length]type"
    1            // is not checked. Parsing continues with the next '%'.
    

    ryanofsky commented at 11:47 am on November 12, 2024:

    re: #31174 (review)

    As you say, only the length and type are no longer checked. This PR now implements rudimentary checking of flags, width and precision.

    Thanks, applied suggestion

  20. in src/util/string.h:88 in e53829d395 outdated
    106                 ++it;
    107+                if (*it == '*') {
    108+                    ++it;
    109+                    add_arg();
    110+                } else {
    111+                   while ('0' <= *it && *it <= '9') ++it;
    


    hodlinator commented at 2:40 pm on October 30, 2024:
    (One could expect that at least one precision-digit was required after ‘.’ but it is not in tinyformat so this behavior is consistent).

    ryanofsky commented at 9:39 pm on November 4, 2024:

    re: #31174 (review)

    (One could expect that at least one precision-digit was required after ‘.’ but it is not in tinyformat so this behavior is consistent).

    That’s good to know, I was just trying to make the parsing as simple as possible, but this syntax does seem to be commonly accepted (it works in python too) and I added test cases for it following your other suggestion

  21. in src/tinyformat.h:1080 in e53829d395 outdated
    1077 
    1078 /// Format list of arguments according to the given format string and return
    1079 /// the result as a string.
    1080 template<typename... Args>
    1081-std::string format(const char* fmt, const Args&... args)
    1082+std::string format(FormatStringCheck<sizeof...(Args)> fmt, const Args&... args)
    


    hodlinator commented at 10:03 pm on October 30, 2024:
    Why not use FormatStringCheck<sizeof...(Args)> in printf and printfln directly below as well?

    ryanofsky commented at 9:53 pm on November 4, 2024:

    re: #31174 (review)

    Why not use FormatStringCheck<sizeof...(Args)> in printf and printfln directly below as well?

    No reason, added now.

  22. in src/test/util_string_tests.cpp:61 in e53829d395 outdated
    60-    PassFmt<1>("%*c");
    61-    PassFmt<2>("%2$*3$d");
    62-    PassFmt<1>("%.*f");
    63+    PassFmt<2>("%*c");
    64+    PassFmt<2>("%+*c");
    65+    PassFmt<2>("%.*f");
    


    hodlinator commented at 10:05 pm on October 30, 2024:

    Could also add

    0PassFmt<1>("%.f");
    

    maybe grouped with the <1> calls above. Confirmed tinyformat allows for it, see how the success status returned by parseWidthOrPrecision is ignored: https://github.com/bitcoin/bitcoin/blob/e53829d3952c6ed275507a66e77636aad67d106b/src/tinyformat.h#L778-L788


    ryanofsky commented at 9:39 pm on November 4, 2024:

    re: #31174 (review)

    0PassFmt<1>("%.f");
    

    Nice suggestion, added two cases: %5.f and %.f below %5.2f

  23. hodlinator commented at 10:15 pm on October 30, 2024: contributor

    In case you do want a test with different behavior, I think you can use %n specifier which is not supported by tinyformat

    Could document non-parity like so (unless you prefer I do it as part of #30933):

    0    // Non-parity
    1    int n{};
    2    BOOST_CHECK_EXCEPTION(tfm::format(std::string{"%n"}, n), tfm::format_error,
    3        HasReason{"tinyformat: %n conversion spec not supported"});
    4    ConstevalFormatString<1>::Detail_CheckNumFormatSpecifiers("%n");
    
  24. ryanofsky force-pushed on Nov 4, 2024
  25. ryanofsky commented at 10:33 pm on November 4, 2024: contributor

    Updated e53829d3952c6ed275507a66e77636aad67d106b -> ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba (pr/tcheck.3 -> pr/tcheck.4, compare) with review suggestions.

    re: #31174#pullrequestreview-2405166236

    Could document non-parity like so (unless you prefer I do it as part of #30933):

    I think that change doesn’t really fit into this PR, since this PR isn’t checking type characters. But it does make sense as part of #30933, so would be good to add there and I’d be happy to review it.

  26. hodlinator approved
  27. hodlinator commented at 2:17 pm on November 8, 2024: contributor

    ACK ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba

    Implemented my suggestions (except comment removal suggestion) + broke out parse_size() since my last review.

    util_string_tests tests passed locally.

    Left one comment, but nothing blocking.

  28. in src/test/util_string_tests.cpp:98 in a99194eb0f outdated
    88@@ -79,7 +89,18 @@ BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)
    89 
    


    maflcko commented at 2:31 pm on November 8, 2024:

    Could add new failing cases here as well?

    0FailFmtWithError<2>("%2$*$d", err_0_pos);
    1FailFmtWithError<2>("%2$*0$d", err_0_pos);
    2FailFmtWithError<3>("%3$*2$.*$f", err_0_pos);
    3FailFmtWithError<3>("%3$*2$.*0$f", err_0_pos);
    

    ryanofsky commented at 11:52 am on November 12, 2024:

    re: #31174 (review)

    Thanks, added

  29. in src/test/util_string_tests.cpp:75 in a99194eb0f outdated
    76+    // Make sure multiple flag characters "- 0+" are accepted
    77+    PassFmt<3>("'%- 0+*.*f'");
    78+    PassFmt<3>("'%1$- 0+*3$.*2$f'");
    79 
    80     auto err_mix{"Format specifiers must be all positional or all non-positional!"};
    81     FailFmtWithError<1>("%s%1$s", err_mix);
    


    maflcko commented at 2:35 pm on November 8, 2024:

    Same:

    0FailFmtWithError<2>("%2$*d", err_mix);
    1FailFmtWithError<2>("%*2$d", err_mix);
    2FailFmtWithError<2>("%.*3$d", err_mix);
    3FailFmtWithError<2>("%2$.*d", err_mix);
    

    maflcko commented at 2:36 pm on November 8, 2024:

    Same (below):

    0    FailFmtWithError<1>("%*c", err_num);
    

    ryanofsky commented at 11:53 am on November 12, 2024:

    re: #31174 (review)

    Thanks, added

  30. maflcko approved
  31. maflcko commented at 2:55 pm on November 8, 2024: member

    left some nit ideas for more tests, but this is good either way.

    review ACK ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba 🕯

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: review ACK ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba 🕯
    3+oFB4Q8dHdvzp6J/1Ir4akTLS5GbDLpGOeKvcRP31CsusrUqTTnwOMie2fGfDcGYiEyKkNN9JiriK4ne+GSICw==
    
  32. in src/util/string.h:69 in ecc5cb9a89 outdated
     97+            auto parse_size = [&] {
     98+                if (*it == '*') {
     99+                    ++it;
    100+                    add_arg();
    101+                } else {
    102+                    while ('0' <= *it && *it <= '9') ++it;
    


    l0rinc commented at 7:33 pm on November 10, 2024:

    Given that we have two separate number “parsers” (one that keeps the result and one that throws it away), we might as well extract number parsing to a local lambda like you did with the other ones.

     0diff --git a/src/util/string.h b/src/util/string.h
     1--- a/src/util/string.h	(revision ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba)
     2+++ b/src/util/string.h	(date 1731267170701)
     3@@ -45,14 +45,16 @@
     4                 continue;
     5             }
     6 
     7+            auto parse_number = [&] {
     8+                unsigned num{0};
     9+                for (; '0' <= *it && *it <= '9'; ++it) {
    10+                    num = num * 10 + (*it - '0');
    11+                }
    12+                return num;
    13+            };
    14+
    15             auto add_arg = [&] {
    16-                unsigned maybe_num{0};
    17-                while ('0' <= *it && *it <= '9') {
    18-                    maybe_num *= 10;
    19-                    maybe_num += *it - '0';
    20-                    ++it;
    21-                }
    22-
    23+                unsigned maybe_num = parse_number();
    24                 if (*it == '$') {
    25                     ++it;
    26                     // Positional specifier, like %8$s
    27@@ -75,7 +77,7 @@
    28                     ++it;
    29                     add_arg();
    30                 } else {
    31-                    while ('0' <= *it && *it <= '9') ++it;
    32+                    parse_number();
    33                 }
    34             };
    

    ryanofsky commented at 12:11 pm on November 12, 2024:

    re: #31174 (review)

    we might as well extract number parsing to a local lambda like you did with the other ones.

    This seems reasonable but I"m not sure it’s clearer, and it does make the diff bigger replacing the maybe_num code that doesn’t have to change currently. Happy to apply this suggestion if other reviews think it is a good idea.


    l0rinc commented at 3:47 pm on November 13, 2024:
    @hodlinator, @maflcko, what do you think? I can ack without this as well, but I’d prefer reducing duplication.

    maflcko commented at 4:01 pm on November 13, 2024:
    I think while ('0' <= *it && *it <= '9') ++it; is fine. It is pretty standard self-explanatory code. I don’t think a one-line while loop needs to be de-duplicated. Also, I like that the diff is minimal as-is now.
  33. in src/util/string.h:46 in ecc5cb9a89 outdated
    52-            if (++it >= str.end()) throw "Format specifier incorrectly terminated by end of string";
    53             if (*it == '%') {
    54                 // Percent escape: %%
    55-                ++it;
    56                 continue;
    57             }
    


    l0rinc commented at 7:58 pm on November 10, 2024:

    These are all here to check if we’re inside a format string, but we don’t have a %%, right? Could we maybe simplify that to something like:

    0            if (*it != '%' || *(++it) == '%') continue; // Skip escaped %%
    

    ?


    ryanofsky commented at 11:57 am on November 12, 2024:

    re: #31174 (review)

    Nice suggestion! Applied

  34. in src/util/string.h:62 in ecc5cb9a89 outdated
    90-                ++count_normal;
    91+            // Increase argument count and consume positional specifier, if present.
    92+            add_arg();
    93+
    94+            // Consume flags.
    95+            while (*it == '#' || *it == '0' || *it == '-' || *it == ' ' || *it == '+') ++it;
    


    l0rinc commented at 7:59 pm on November 10, 2024:

    In C++23 this could be a simple .contains, but even in C++20 we should be able to group the flags to something like:

    0            while ("#0- +"sv.find(*it) != std::string_view::npos) ++it;
    

    (we could even extract the flag in which case we could get rid of the comment)


    hodlinator commented at 9:47 am on November 11, 2024:

    Time to return to C89? ;)

    0            while (strchr("#0- +", *it)) ++it;
    

    l0rinc commented at 10:44 am on November 11, 2024:

    I thought of that, but not sure how to make it work, I’m getting:

    note: non-constexpr function ‘strchr’ cannot be used in a constant expression


    hodlinator commented at 1:39 pm on November 11, 2024:
    Still rebooting :brain: for this week, sorry for the noise.

    ryanofsky commented at 12:00 pm on November 12, 2024:

    re: #31174 (review)

    I can apply the "#0- +"sv.find change if others like it, but to me it seems less readable and only a little shorter.


    l0rinc commented at 3:46 pm on November 13, 2024:
    it could be more readable and a bit shorter if we extract the flags as a variable and delete the comment stating the same - what do you think?

    maflcko commented at 3:58 pm on November 13, 2024:
    My preference would be to leave style-only nits to a follow-up, especially given that they will be temporary anyway until C++23. This pull request is basically ready for two weeks now, with more than 50 style-only or test-only comments. Unless there are any real issues or bugs with the code, and a foce-push needs to happen anyway, I don’t really see the value of asking reviewers to go through more comments and code changes, some of which don’t even compile.
  35. in src/tinyformat.h:186 in ecc5cb9a89 outdated
    178@@ -178,6 +179,18 @@ namespace tfm = tinyformat;
    179 
    180 namespace tinyformat {
    181 
    182+// Added for Bitcoin Core. Wrapper for checking format strings at compile time.
    183+// Unlike ConstevalFormatString this supports std::string for runtime string
    184+// formatting without compile time checks.
    185+template <unsigned num_params>
    186+struct FormatStringCheck {
    


    l0rinc commented at 8:15 pm on November 10, 2024:
    Checked and failures seem to be validated successfully from command line, but - unlike the previous versions - doesn’t seem to be shown in the IDE… Weird :/
  36. l0rinc commented at 8:20 pm on November 10, 2024: contributor

    Nice, simple approach, like it a lot! I think we can simplify the validator a bit more - let me know what you think.

    Also, not exactly sure why %n parity wasn’t added like in https://github.com/bitcoin/bitcoin/pull/30999/files#diff-71badc1cc71ba46244f7841a088251bb294265f4fe9e662c0ad6b15be540eee4R69 (is it controversial or unnecessary or not useful)?

  37. ryanofsky force-pushed on Nov 12, 2024
  38. ryanofsky commented at 12:21 pm on November 12, 2024: contributor
    Updated ecc5cb9a89c6b001df839675b23d8fc1f7ac69ba -> fe39acf88ff552bfc4a502c99774375b91824bb1 (pr/tcheck.4 -> pr/tcheck.5, compare) with suggested changes. Thanks for the reviews and suggestions!
  39. ryanofsky commented at 12:46 pm on November 12, 2024: contributor

    re: #31174#pullrequestreview-2425834191

    Also, not exactly sure why %n parity wasn’t added

    Thanks I hadn’t seen #30999, and it seems like that would be a reasonable thing to check for, though I think there is a case for keeping the code as simple as possible and not trying to reproduce tinyformat quirks here. But the reason for not making that change here is I don’t think it’s related to this PR, and I think it’s generally better to make separate changes n separate PRs so they can be evaluated correctly and discussed more clearly.

  40. maflcko commented at 3:42 pm on November 13, 2024: member

    re-ACK fe39acf88ff552bfc4a502c99774375b91824bb1 🕐

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: re-ACK fe39acf88ff552bfc4a502c99774375b91824bb1 🕐
    3Gv99KVENxeKH2/4i5mCpvzXcxQ66+sVorM28bzE1QmucATBYm7QTTpcvRNaaGKsCRRtGOmk0QEigsfGUPldxCA==
    
  41. DrahtBot requested review from hodlinator on Nov 13, 2024
  42. in src/test/util_string_tests.cpp:72 in fe39acf88f
    73+    PassFmt<3>("%2$+9.*3$d");
    74+    PassFmt<4>("%3$*2$.*4$f");
    75+
    76+    // Make sure multiple flag characters "- 0+" are accepted
    77+    PassFmt<3>("'%- 0+*.*f'");
    78+    PassFmt<3>("'%1$- 0+*3$.*2$f'");
    


    l0rinc commented at 4:07 pm on November 13, 2024:
    are the extra surrounding ' deliberate? If so, what do they mean?

    ryanofsky commented at 4:20 pm on November 13, 2024:

    re: #31174 (review)

    I’m pretty sure they must be accidental. These cases came from #31174 (review), and I just pasted them without noticing the single quotes. Can remove if the PR is updated again.


    maflcko commented at 4:22 pm on November 13, 2024:
    The ' are used to denote the begin and the end of the string, which would otherwise not be possible, because trailing spaces can normally not be seen when printing. They are not needed in this test and they are a leftover when I tested this against tinyformat at runtime.
  43. l0rinc commented at 4:09 pm on November 13, 2024: contributor

    Thanks for improving developer productivity with these small changes <3

    ACK fe39acf88ff552bfc4a502c99774375b91824bb1

  44. hodlinator approved
  45. hodlinator commented at 11:05 pm on November 13, 2024: contributor

    re-ACK fe39acf88ff552bfc4a502c99774375b91824bb1

    git range-diff master ecc5cb9 fe39acf

    • Added more FailFmtWithError-tests (maflcko).
    • Terser skipping of %% (l0rinc).
    • Comment regarding format string components updated (me).
  46. fanquake merged this on Nov 14, 2024
  47. fanquake closed this on Nov 14, 2024


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-22 06:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me