-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge bitcoin/bitcoin#30546: util: Use consteval checked format strin…
…g in FatalErrorf, LogConnectFailure fa5bc45 util: Use compile-time check for LogConnectFailure (MarcoFalke) fa7087b util: Use compile-time check for FatalErrorf (MarcoFalke) faa62c0 util: Add ConstevalFormatString (MarcoFalke) fae7b83 lint: Remove forbidden functions from lint-format-strings.py (MarcoFalke) Pull request description: The `test/lint/lint-format-strings.py` was designed to count the number of format specifiers and assert that they are equal to the number of parameters passed to the format function. The goal seems reasonable, but the implementation has many problems: * It is written in Python, meaning that C++ code can not be parsed correctly. Currently it relies on brittle regex and string parsing. * Apart from the parsing errors, there are also many logic errors. For example, `count_format_specifiers` allows a mix of positional specifiers and non-positional specifiers, which can lead to runtime format bugs. Also, `count_format_specifiers` silently skipped over "special" format specifiers, which are valid in tinyformat, which again can lead to runtime format bugs being undetected. * The brittle logic has a history of breaking in pull requests that are otherwise fine. This causes the CI to fail and the pull request being blocked from progress until the bug in the linter is fixed, or the code is rewritten to work around the bug. * It is only run in the CI, or when the developer invokes the script. It would be better if the developer got the error message at compile-time, directly when writing the code. Fix all issues by using a `consteval` checked format string in `FatalErrorf` and `LogConnectFailure`. This is the first step toward bitcoin/bitcoin#30530 and a follow-up will apply the approach to the other places. ACKs for top commit: stickies-v: re-ACK fa5bc45 l0rinc: ACK fa5bc45 hodlinator: ACK fa5bc45 ryanofsky: Code review ACK fa5bc45 Tree-SHA512: d6189096b16083143687ed1b1559cf4f92f97dd87bc5d00673e44f4fb9fce7bb7b215cfdfc39b6e6a24f0b75a79a03ededce966639e554f7172e1fc22cf015ae
- Loading branch information
Showing
8 changed files
with
166 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Copyright (c) 2024-present The Bitcoin Core developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include <util/string.h> | ||
|
||
#include <boost/test/unit_test.hpp> | ||
|
||
using namespace util; | ||
|
||
BOOST_AUTO_TEST_SUITE(util_string_tests) | ||
|
||
// Helper to allow compile-time sanity checks while providing the number of | ||
// args directly. Normally PassFmt<sizeof...(Args)> would be used. | ||
template <unsigned NumArgs> | ||
inline void PassFmt(util::ConstevalFormatString<NumArgs> fmt) | ||
{ | ||
// This was already executed at compile-time, but is executed again at run-time to avoid -Wunused. | ||
decltype(fmt)::Detail_CheckNumFormatSpecifiers(fmt.fmt); | ||
} | ||
template <unsigned WrongNumArgs> | ||
inline void FailFmtWithError(std::string_view wrong_fmt, std::string_view error) | ||
{ | ||
using ErrType = const char*; | ||
auto check_throw{[error](const ErrType& str) { return str == error; }}; | ||
BOOST_CHECK_EXCEPTION(util::ConstevalFormatString<WrongNumArgs>::Detail_CheckNumFormatSpecifiers(wrong_fmt), ErrType, check_throw); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec) | ||
{ | ||
PassFmt<0>(""); | ||
PassFmt<0>("%%"); | ||
PassFmt<1>("%s"); | ||
PassFmt<0>("%%s"); | ||
PassFmt<0>("s%%"); | ||
PassFmt<1>("%%%s"); | ||
PassFmt<1>("%s%%"); | ||
PassFmt<0>(" 1$s"); | ||
PassFmt<1>("%1$s"); | ||
PassFmt<1>("%1$s%1$s"); | ||
PassFmt<2>("%2$s"); | ||
PassFmt<2>("%2$s 4$s %2$s"); | ||
PassFmt<129>("%129$s 999$s %2$s"); | ||
PassFmt<1>("%02d"); | ||
PassFmt<1>("%+2s"); | ||
PassFmt<1>("%.6i"); | ||
PassFmt<1>("%5.2f"); | ||
PassFmt<1>("%#x"); | ||
PassFmt<1>("%1$5i"); | ||
PassFmt<1>("%1$-5i"); | ||
PassFmt<1>("%1$.5i"); | ||
// tinyformat accepts almost any "type" spec, even '%', or '_', or '\n'. | ||
PassFmt<1>("%123%"); | ||
PassFmt<1>("%123%s"); | ||
PassFmt<1>("%_"); | ||
PassFmt<1>("%\n"); | ||
|
||
// The `*` specifier behavior is unsupported and can lead to runtime | ||
// errors when used in a ConstevalFormatString. Please refer to the | ||
// note in the ConstevalFormatString docs. | ||
PassFmt<1>("%*c"); | ||
PassFmt<2>("%2$*3$d"); | ||
PassFmt<1>("%.*f"); | ||
|
||
auto err_mix{"Format specifiers must be all positional or all non-positional!"}; | ||
FailFmtWithError<1>("%s%1$s", err_mix); | ||
|
||
auto err_num{"Format specifier count must match the argument count!"}; | ||
FailFmtWithError<1>("", err_num); | ||
FailFmtWithError<0>("%s", err_num); | ||
FailFmtWithError<2>("%s", err_num); | ||
FailFmtWithError<0>("%1$s", err_num); | ||
FailFmtWithError<2>("%1$s", err_num); | ||
|
||
auto err_0_pos{"Positional format specifier must have position of at least 1"}; | ||
FailFmtWithError<1>("%$s", err_0_pos); | ||
FailFmtWithError<1>("%$", err_0_pos); | ||
FailFmtWithError<0>("%0$", err_0_pos); | ||
FailFmtWithError<0>("%0$s", err_0_pos); | ||
|
||
auto err_term{"Format specifier incorrectly terminated by end of string"}; | ||
FailFmtWithError<1>("%", err_term); | ||
FailFmtWithError<1>("%1$", err_term); | ||
} | ||
|
||
BOOST_AUTO_TEST_SUITE_END() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters