mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-26 19:23:26 -03:00
refactor: Check translatable format strings at compile-time
This commit is contained in:
parent
fa1d5acb8d
commit
fadc6b9bac
3 changed files with 38 additions and 24 deletions
|
@ -18,7 +18,6 @@ FUZZ_TARGET(str_printf)
|
||||||
{
|
{
|
||||||
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
||||||
const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
|
const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
|
||||||
const bilingual_str bilingual_string{format_string, format_string};
|
|
||||||
|
|
||||||
const int digits_in_format_specifier = std::count_if(format_string.begin(), format_string.end(), IsDigit);
|
const int digits_in_format_specifier = std::count_if(format_string.begin(), format_string.end(), IsDigit);
|
||||||
|
|
||||||
|
@ -53,27 +52,21 @@ FUZZ_TARGET(str_printf)
|
||||||
fuzzed_data_provider,
|
fuzzed_data_provider,
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool());
|
|
||||||
});
|
});
|
||||||
} catch (const tinyformat::format_error&) {
|
} catch (const tinyformat::format_error&) {
|
||||||
}
|
}
|
||||||
|
@ -99,35 +92,27 @@ FUZZ_TARGET(str_printf)
|
||||||
fuzzed_data_provider,
|
fuzzed_data_provider,
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
|
|
||||||
},
|
},
|
||||||
[&] {
|
[&] {
|
||||||
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
|
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
|
||||||
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
|
|
||||||
});
|
});
|
||||||
} catch (const tinyformat::format_error&) {
|
} catch (const tinyformat::format_error&) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,26 @@
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(translation_tests)
|
BOOST_AUTO_TEST_SUITE(translation_tests)
|
||||||
|
|
||||||
|
static TranslateFn translate{[](const char * str) { return strprintf("t(%s)", str); }};
|
||||||
|
|
||||||
|
// Custom translation function _t(), similar to _() but internal to this test.
|
||||||
|
consteval auto _t(util::TranslatedLiteral str)
|
||||||
|
{
|
||||||
|
str.translate_fn = &translate;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(translation_namedparams)
|
BOOST_AUTO_TEST_CASE(translation_namedparams)
|
||||||
{
|
{
|
||||||
bilingual_str arg{"original", "translated"};
|
bilingual_str arg{"original", "translated"};
|
||||||
bilingual_str format{"original [%s]", "translated [%s]"};
|
bilingual_str result{strprintf(_t("original [%s]"), arg)};
|
||||||
bilingual_str result{strprintf(format, arg)};
|
|
||||||
BOOST_CHECK_EQUAL(result.original, "original [original]");
|
BOOST_CHECK_EQUAL(result.original, "original [original]");
|
||||||
BOOST_CHECK_EQUAL(result.translated, "translated [translated]");
|
BOOST_CHECK_EQUAL(result.translated, "t(original [translated])");
|
||||||
|
|
||||||
|
util::TranslatedLiteral arg2{"original", &translate};
|
||||||
|
bilingual_str result2{strprintf(_t("original [%s]"), arg2)};
|
||||||
|
BOOST_CHECK_EQUAL(result2.original, "original [original]");
|
||||||
|
BOOST_CHECK_EQUAL(result2.translated, "t(original [t(original)])");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -67,6 +67,13 @@ template<typename T>
|
||||||
T operator+(const T& lhs, const TranslatedLiteral& rhs) { return lhs + static_cast<T>(rhs); }
|
T operator+(const T& lhs, const TranslatedLiteral& rhs) { return lhs + static_cast<T>(rhs); }
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T operator+(const TranslatedLiteral& lhs, const T& rhs) { return static_cast<T>(lhs) + rhs; }
|
T operator+(const TranslatedLiteral& lhs, const T& rhs) { return static_cast<T>(lhs) + rhs; }
|
||||||
|
|
||||||
|
template <unsigned num_params>
|
||||||
|
struct BilingualFmt {
|
||||||
|
const ConstevalFormatString<num_params> original;
|
||||||
|
TranslatedLiteral lit;
|
||||||
|
consteval BilingualFmt(TranslatedLiteral l) : original{l.original}, lit{l} {}
|
||||||
|
};
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
consteval auto _(util::TranslatedLiteral str) { return str; }
|
consteval auto _(util::TranslatedLiteral str) { return str; }
|
||||||
|
@ -74,20 +81,29 @@ consteval auto _(util::TranslatedLiteral str) { return str; }
|
||||||
/** Mark a bilingual_str as untranslated */
|
/** Mark a bilingual_str as untranslated */
|
||||||
inline bilingual_str Untranslated(std::string original) { return {original, original}; }
|
inline bilingual_str Untranslated(std::string original) { return {original, original}; }
|
||||||
|
|
||||||
// Provide an overload of tinyformat::format which can take bilingual_str arguments.
|
// Provide an overload of tinyformat::format for BilingualFmt format strings and bilingual_str or TranslatedLiteral args.
|
||||||
namespace tinyformat {
|
namespace tinyformat {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
bilingual_str format(const bilingual_str& fmt, const Args&... args)
|
bilingual_str format(util::BilingualFmt<sizeof...(Args)> fmt, const Args&... args)
|
||||||
{
|
{
|
||||||
const auto translate_arg{[](const auto& arg, bool translated) -> const auto& {
|
const auto original_arg{[](const auto& arg) -> const auto& {
|
||||||
if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) {
|
if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) {
|
||||||
return translated ? arg.translated : arg.original;
|
return arg.original;
|
||||||
|
} else if constexpr (std::is_same_v<decltype(arg), const util::TranslatedLiteral&>) {
|
||||||
|
return arg.original;
|
||||||
} else {
|
} else {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
return bilingual_str{tfm::format(fmt.original, translate_arg(args, false)...),
|
const auto translated_arg{[](const auto& arg) -> const auto& {
|
||||||
tfm::format(fmt.translated, translate_arg(args, true)...)};
|
if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) {
|
||||||
|
return arg.translated;
|
||||||
|
} else {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
return bilingual_str{tfm::format(fmt.original, original_arg(args)...),
|
||||||
|
tfm::format(std::string{fmt.lit}, translated_arg(args)...)};
|
||||||
}
|
}
|
||||||
} // namespace tinyformat
|
} // namespace tinyformat
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue