aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-08-13 22:30:08 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-08-13 22:42:11 +0300
commita22375f74c2d4696846adf89626ca853b08cc782 (patch)
tree21af9ae9f1e0c825821cd0c46b024f8cf5a0c230
parent70ed6114541643f807012c222d8b8cc4c03a0ae3 (diff)
downloadydb-a22375f74c2d4696846adf89626ca853b08cc782.tar.gz
YT-22512: Static analysis for TError method, ctors and related macros
Static analysis enabled for TError creation and related macros TRuntimeFormat can be used to disable this feature, but requires copying the viewed object. See NYT::TError::DisableFormat overloads to optimize constructions which want to move the given string Note to future readers: TError is not "perfect-forwarding" unfriendly class. This means that the code ``` template <class... TArgs> TError MakeError(TArgs&&... args) { return TError(std::forward<TArgs>(args)...); } ``` will not compile and needs to be properly adjusted (see. TError::Wrap for implementation example) This implies that emplace construction in containers will not work either. Use move construction instead, as it is simply a pointer swap and therefore free Annotations: [nodiff:caesar] cff12f05849402d09a4487bad26ffcd968215dc7
-rw-r--r--library/cpp/yt/logging/logger-inl.h8
-rw-r--r--library/cpp/yt/string/format_analyser-inl.h112
-rw-r--r--library/cpp/yt/string/format_analyser.h133
-rw-r--r--library/cpp/yt/string/format_string.h7
-rw-r--r--yt/yt/client/api/persistent_queue.cpp5
-rw-r--r--yt/yt/client/table_client/merge_table_schemas.cpp4
-rw-r--r--yt/yt/client/table_client/validate_logical_type.cpp3
-rw-r--r--yt/yt/core/bus/tcp/server.cpp2
-rw-r--r--yt/yt/core/crypto/crypto.cpp2
-rw-r--r--yt/yt/core/dns/ares_dns_resolver.cpp2
-rw-r--r--yt/yt/core/json/json_parser.cpp2
-rw-r--r--yt/yt/core/json/json_writer.cpp2
-rw-r--r--yt/yt/core/misc/arithmetic_formula.cpp2
-rw-r--r--yt/yt/core/misc/error-inl.h257
-rw-r--r--yt/yt/core/misc/error.cpp10
-rw-r--r--yt/yt/core/misc/error.h91
-rw-r--r--yt/yt/core/misc/unittests/error_ut.cpp78
-rw-r--r--yt/yt/core/rpc/bus/channel.cpp4
-rw-r--r--yt/yt/core/rpc/grpc/channel.cpp4
-rw-r--r--yt/yt/library/formats/skiff_yson_converter.cpp18
20 files changed, 527 insertions, 219 deletions
diff --git a/library/cpp/yt/logging/logger-inl.h b/library/cpp/yt/logging/logger-inl.h
index 5c1f1b0b55..f3993a4c48 100644
--- a/library/cpp/yt/logging/logger-inl.h
+++ b/library/cpp/yt/logging/logger-inl.h
@@ -181,12 +181,6 @@ struct TLogMessage
TStringBuf Anchor;
};
-template <class T>
-concept CLiteralLogFormat =
- requires (T& t) {
- [] (const char*) { } (t);
- };
-
template <class... TArgs>
TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
@@ -200,7 +194,7 @@ TLogMessage BuildLogMessage(
}
template <CFormattable T>
- requires (!CLiteralLogFormat<std::remove_cvref_t<T>>)
+ requires (!CStringLiteral<std::remove_cvref_t<T>>)
TLogMessage BuildLogMessage(
const TLoggingContext& loggingContext,
const TLogger& logger,
diff --git a/library/cpp/yt/string/format_analyser-inl.h b/library/cpp/yt/string/format_analyser-inl.h
deleted file mode 100644
index 8a3dd897ff..0000000000
--- a/library/cpp/yt/string/format_analyser-inl.h
+++ /dev/null
@@ -1,112 +0,0 @@
-#ifndef FORMAT_ANALYSER_INL_H_
-#error "Direct inclusion of this file is not allowed, include format_analyser.h"
-// For the sake of sane code completion.
-#include "format_analyser.h"
-#endif
-
-#include "format_arg.h"
-
-namespace NYT::NDetail {
-
-////////////////////////////////////////////////////////////////////////////////
-
-consteval bool Contains(std::string_view sv, char symbol)
-{
- return sv.find(symbol) != std::string_view::npos;
-}
-
-template <class... TArgs>
-consteval void TFormatAnalyser::ValidateFormat(std::string_view format)
-{
- std::array<std::string_view, sizeof...(TArgs)> markers = {};
- std::array<TSpecifiers, sizeof...(TArgs)> specifiers{GetSpecifiers<TArgs>()...};
-
- int markerCount = 0;
- int currentMarkerStart = -1;
-
- for (int index = 0; index < std::ssize(format); ++index) {
- auto symbol = format[index];
-
- // Parse verbatim text.
- if (currentMarkerStart == -1) {
- if (symbol == IntroductorySymbol) {
- // Marker maybe begins.
- currentMarkerStart = index;
- }
- continue;
- }
-
- // NB: We check for %% first since
- // in order to verify if symbol is a specifier
- // we need markerCount to be within range of our
- // specifier array.
- if (symbol == IntroductorySymbol) {
- if (currentMarkerStart + 1 != index) {
- // '%a% detected'
- CrashCompilerWrongTermination("You may not terminate flag sequence other than %% with \'%\' symbol");
- return;
- }
- // '%%' detected --- skip
- currentMarkerStart = -1;
- continue;
- }
-
- // We are inside of marker.
- if (markerCount == std::ssize(markers)) {
- // To many markers
- CrashCompilerNotEnoughArguments("Number of arguments supplied to format is smaller than the number of flag sequences");
- return;
- }
-
- if (Contains(specifiers[markerCount].Conversion, symbol)) {
- // Marker has finished.
-
- markers[markerCount]
- = std::string_view(format.begin() + currentMarkerStart, index - currentMarkerStart + 1);
- currentMarkerStart = -1;
- ++markerCount;
-
- continue;
- }
-
- if (!Contains(specifiers[markerCount].Flags, symbol)) {
- CrashCompilerWrongFlagSpecifier("Symbol is not a valid flag specifier; See FlagSpecifiers");
- }
- }
-
- if (currentMarkerStart != -1) {
- // Runaway marker.
- CrashCompilerMissingTermination("Unterminated flag sequence detected; Use \'%%\' to type plain %");
- return;
- }
-
- if (markerCount < std::ssize(markers)) {
- // Missing markers.
- CrashCompilerTooManyArguments("Number of arguments supplied to format is greater than the number of flag sequences");
- return;
- }
-
- // TODO(arkady-e1ppa): Consider per-type verification
- // of markers.
-}
-
-template <class TArg>
-consteval auto TFormatAnalyser::GetSpecifiers()
-{
- if constexpr (!CFormattable<TArg>) {
- CrashCompilerNotFormattable<TArg>("Your specialization of TFormatArg is broken");
- }
-
- return TSpecifiers{
- .Conversion = std::string_view{
- std::data(TFormatArg<TArg>::ConversionSpecifiers),
- std::size(TFormatArg<TArg>::ConversionSpecifiers)},
- .Flags = std::string_view{
- std::data(TFormatArg<TArg>::FlagSpecifiers),
- std::size(TFormatArg<TArg>::FlagSpecifiers)},
- };
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NDetail
diff --git a/library/cpp/yt/string/format_analyser.h b/library/cpp/yt/string/format_analyser.h
index 3dd1ba4cd3..e8287ea8f3 100644
--- a/library/cpp/yt/string/format_analyser.h
+++ b/library/cpp/yt/string/format_analyser.h
@@ -1,5 +1,7 @@
#pragma once
+#include "format_arg.h"
+
#include <util/generic/strbuf.h>
#include <array>
@@ -12,36 +14,143 @@ namespace NYT::NDetail {
struct TFormatAnalyser
{
public:
+ // TODO(arkady-e1ppa): Until clang-19 consteval functions
+ // defined out of line produce symbols in rare cases
+ // causing linker to crash.
template <class... TArgs>
- static consteval void ValidateFormat(std::string_view fmt);
+ static consteval void ValidateFormat(std::string_view fmt)
+ {
+ DoValidateFormat<TArgs...>(fmt);
+ }
-public:
+private:
// Non-constexpr function call will terminate compilation.
// Purposefully undefined and non-constexpr/consteval
template <class T>
static void CrashCompilerNotFormattable(std::string_view /*msg*/)
{ /*Suppress "internal linkage but undefined" warning*/ }
- static void CrashCompilerNotEnoughArguments(std::string_view msg);
- static void CrashCompilerTooManyArguments(std::string_view msg);
- static void CrashCompilerWrongTermination(std::string_view msg);
- static void CrashCompilerMissingTermination(std::string_view msg);
- static void CrashCompilerWrongFlagSpecifier(std::string_view msg);
+ static void CrashCompilerNotEnoughArguments(std::string_view /*msg*/)
+ { }
+
+ static void CrashCompilerTooManyArguments(std::string_view /*msg*/)
+ { }
+
+ static void CrashCompilerWrongTermination(std::string_view /*msg*/)
+ { }
+
+ static void CrashCompilerMissingTermination(std::string_view /*msg*/)
+ { }
+
+ static void CrashCompilerWrongFlagSpecifier(std::string_view /*msg*/)
+ { }
+
+
+ static consteval bool Contains(std::string_view sv, char symbol)
+ {
+ return sv.find(symbol) != std::string_view::npos;
+ }
struct TSpecifiers
{
std::string_view Conversion;
std::string_view Flags;
};
+
template <class TArg>
- static consteval auto GetSpecifiers();
+ static consteval auto GetSpecifiers()
+ {
+ if constexpr (!CFormattable<TArg>) {
+ CrashCompilerNotFormattable<TArg>("Your specialization of TFormatArg is broken");
+ }
+
+ return TSpecifiers{
+ .Conversion = std::string_view{
+ std::data(TFormatArg<TArg>::ConversionSpecifiers),
+ std::size(TFormatArg<TArg>::ConversionSpecifiers)},
+ .Flags = std::string_view{
+ std::data(TFormatArg<TArg>::FlagSpecifiers),
+ std::size(TFormatArg<TArg>::FlagSpecifiers)},
+ };
+ }
static constexpr char IntroductorySymbol = '%';
+
+ template <class... TArgs>
+ static consteval void DoValidateFormat(std::string_view format)
+ {
+ std::array<std::string_view, sizeof...(TArgs)> markers = {};
+ std::array<TSpecifiers, sizeof...(TArgs)> specifiers{GetSpecifiers<TArgs>()...};
+
+ int markerCount = 0;
+ int currentMarkerStart = -1;
+
+ for (int index = 0; index < std::ssize(format); ++index) {
+ auto symbol = format[index];
+
+ // Parse verbatim text.
+ if (currentMarkerStart == -1) {
+ if (symbol == IntroductorySymbol) {
+ // Marker maybe begins.
+ currentMarkerStart = index;
+ }
+ continue;
+ }
+
+ // NB: We check for %% first since
+ // in order to verify if symbol is a specifier
+ // we need markerCount to be within range of our
+ // specifier array.
+ if (symbol == IntroductorySymbol) {
+ if (currentMarkerStart + 1 != index) {
+ // '%a% detected'
+ CrashCompilerWrongTermination("You may not terminate flag sequence other than %% with \'%\' symbol");
+ return;
+ }
+ // '%%' detected --- skip
+ currentMarkerStart = -1;
+ continue;
+ }
+
+ // We are inside of marker.
+ if (markerCount == std::ssize(markers)) {
+ // To many markers
+ CrashCompilerNotEnoughArguments("Number of arguments supplied to format is smaller than the number of flag sequences");
+ return;
+ }
+
+ if (Contains(specifiers[markerCount].Conversion, symbol)) {
+ // Marker has finished.
+
+ markers[markerCount]
+ = std::string_view(format.begin() + currentMarkerStart, index - currentMarkerStart + 1);
+ currentMarkerStart = -1;
+ ++markerCount;
+
+ continue;
+ }
+
+ if (!Contains(specifiers[markerCount].Flags, symbol)) {
+ CrashCompilerWrongFlagSpecifier("Symbol is not a valid flag specifier; See FlagSpecifiers");
+ }
+ }
+
+ if (currentMarkerStart != -1) {
+ // Runaway marker.
+ CrashCompilerMissingTermination("Unterminated flag sequence detected; Use \'%%\' to type plain %");
+ return;
+ }
+
+ if (markerCount < std::ssize(markers)) {
+ // Missing markers.
+ CrashCompilerTooManyArguments("Number of arguments supplied to format is greater than the number of flag sequences");
+ return;
+ }
+
+ // TODO(arkady-e1ppa): Consider per-type verification
+ // of markers.
+ }
};
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NDetail
-
-#define FORMAT_ANALYSER_INL_H_
-#include "format_analyser-inl.h"
-#undef FORMAT_ANALYSER_INL_H_
diff --git a/library/cpp/yt/string/format_string.h b/library/cpp/yt/string/format_string.h
index ebef9d7042..786c2e39ed 100644
--- a/library/cpp/yt/string/format_string.h
+++ b/library/cpp/yt/string/format_string.h
@@ -56,6 +56,13 @@ using TFormatString = TBasicFormatString<std::type_identity_t<TArgs>...>;
////////////////////////////////////////////////////////////////////////////////
+template <class T>
+concept CStringLiteral = requires (T& t) {
+ [] (const char*) { } (t);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
#define FORMAT_STRING_INL_H_
diff --git a/yt/yt/client/api/persistent_queue.cpp b/yt/yt/client/api/persistent_queue.cpp
index b75702a14d..82f7b1fe09 100644
--- a/yt/yt/client/api/persistent_queue.cpp
+++ b/yt/yt/client/api/persistent_queue.cpp
@@ -679,10 +679,7 @@ private:
batch.EndRowIndex - 1,
transaction->GetId());
} catch (const std::exception& ex) {
- THROW_ERROR_EXCEPTION("Error confirming persistent queue rows",
- batch.TabletIndex,
- batch.BeginRowIndex,
- batch.EndRowIndex - 1)
+ THROW_ERROR_EXCEPTION("Error confirming persistent queue rows")
<< TErrorAttribute("poller_id", PollerId_)
<< TErrorAttribute("transaction_id", transaction->GetId())
<< TErrorAttribute("tablet_index", batch.TabletIndex)
diff --git a/yt/yt/client/table_client/merge_table_schemas.cpp b/yt/yt/client/table_client/merge_table_schemas.cpp
index 40b55bb519..d41e58baf5 100644
--- a/yt/yt/client/table_client/merge_table_schemas.cpp
+++ b/yt/yt/client/table_client/merge_table_schemas.cpp
@@ -55,7 +55,9 @@ TTableSchemaPtr MergeTableSchemas(
}
if (firstSchemaColumn->SortOrder() != secondSchemaColumn.SortOrder()) {
THROW_ERROR_EXCEPTION("Mismatching sort orders in column %Qv: %Qv and %Qv",
- firstSchemaColumn->Name());
+ firstSchemaColumn->Name(),
+ firstSchemaColumn->SortOrder(),
+ secondSchemaColumn.SortOrder());
}
try {
diff --git a/yt/yt/client/table_client/validate_logical_type.cpp b/yt/yt/client/table_client/validate_logical_type.cpp
index 9f96713acb..3e6b3d1d65 100644
--- a/yt/yt/client/table_client/validate_logical_type.cpp
+++ b/yt/yt/client/table_client/validate_logical_type.cpp
@@ -235,8 +235,7 @@ private:
if (Cursor_.GetCurrent().GetType() == EYsonItemType::EndList) {
THROW_ERROR_EXCEPTION(EErrorCode::SchemaViolation,
"Cannot parse %Qv; empty yson",
- GetDescription(fieldId),
- Cursor_.GetCurrent().GetType());
+ GetDescription(fieldId));
}
ValidateLogicalType(type.GetElement(), fieldId.OptionalElement());
if (Cursor_.GetCurrent().GetType() != EYsonItemType::EndList) {
diff --git a/yt/yt/core/bus/tcp/server.cpp b/yt/yt/core/bus/tcp/server.cpp
index 6d6737a96b..527c0e18c1 100644
--- a/yt/yt/core/bus/tcp/server.cpp
+++ b/yt/yt/core/bus/tcp/server.cpp
@@ -285,7 +285,7 @@ protected:
if (attempt == Config_->BindRetryCount) {
CloseServerSocket();
- THROW_ERROR_EXCEPTION(NRpc::EErrorCode::TransportError, errorMessage)
+ THROW_ERROR_EXCEPTION(NRpc::EErrorCode::TransportError, TRuntimeFormat(errorMessage))
<< ex;
} else {
YT_LOG_WARNING(ex, "Error binding socket, starting %v retry", attempt + 1);
diff --git a/yt/yt/core/crypto/crypto.cpp b/yt/yt/core/crypto/crypto.cpp
index d8f4839a83..8f84434340 100644
--- a/yt/yt/core/crypto/crypto.cpp
+++ b/yt/yt/core/crypto/crypto.cpp
@@ -291,7 +291,7 @@ TString GenerateCryptoStrongRandomString(int length)
auto* data = reinterpret_cast<char*>(bytes.data());
return TString{data, static_cast<size_t>(length)};
} else {
- THROW_ERROR_EXCEPTION("Failed to generate %v random bytes")
+ THROW_ERROR_EXCEPTION("Failed to generate %v random bytes", length)
<< TErrorAttribute("openssl_error_code", ERR_get_error());
}
}
diff --git a/yt/yt/core/dns/ares_dns_resolver.cpp b/yt/yt/core/dns/ares_dns_resolver.cpp
index 45377f7e96..b627cda872 100644
--- a/yt/yt/core/dns/ares_dns_resolver.cpp
+++ b/yt/yt/core/dns/ares_dns_resolver.cpp
@@ -737,7 +737,7 @@ private:
request->HostName)
<< TErrorAttribute("enable_ipv4", request->Options.EnableIPv4)
<< TErrorAttribute("enable_ipv6", request->Options.EnableIPv6)
- << TError(ares_strerror(status));
+ << TError(TRuntimeFormat(ares_strerror(status)));
}
void FailRequest(
diff --git a/yt/yt/core/json/json_parser.cpp b/yt/yt/core/json/json_parser.cpp
index 989c2099fa..5fc00c6d73 100644
--- a/yt/yt/core/json/json_parser.cpp
+++ b/yt/yt/core/json/json_parser.cpp
@@ -194,7 +194,7 @@ private:
1,
reinterpret_cast<const unsigned char*>(data),
len);
- auto error = TError("Error parsing JSON") << TError((char*) errorMessage);
+ auto error = TError("Error parsing JSON") << TError(TRuntimeFormat((char*) errorMessage));
yajl_free_error(YajlHandle_.get(), errorMessage);
THROW_ERROR_EXCEPTION(error);
}
diff --git a/yt/yt/core/json/json_writer.cpp b/yt/yt/core/json/json_writer.cpp
index c24fc0940f..7412002ebd 100644
--- a/yt/yt/core/json/json_writer.cpp
+++ b/yt/yt/core/json/json_writer.cpp
@@ -164,7 +164,7 @@ static void CheckYajlCode(int yajlCode)
default:
errorMessage = Format("Yajl writer failed with code %v", yajlCode);
}
- THROW_ERROR_EXCEPTION(errorMessage);
+ THROW_ERROR_EXCEPTION(std::move(errorMessage), NYT::TError::DisableFormat);
}
TJsonWriter::TJsonWriter(IOutputStream* output, bool isPretty)
diff --git a/yt/yt/core/misc/arithmetic_formula.cpp b/yt/yt/core/misc/arithmetic_formula.cpp
index 9309f47452..8fe147ef65 100644
--- a/yt/yt/core/misc/arithmetic_formula.cpp
+++ b/yt/yt/core/misc/arithmetic_formula.cpp
@@ -96,7 +96,7 @@ void ThrowError(const TString& formula, int position, const TString& message, EE
formula);
builder.AppendChar(' ', position);
builder.AppendFormat("^\n%v", message);
- THROW_ERROR_EXCEPTION(builder.Flush())
+ THROW_ERROR_EXCEPTION(std::move(builder.Flush()), NYT::TError::DisableFormat)
<< TErrorAttribute("context", context)
<< TErrorAttribute("context_pos", contextPosition);
}
diff --git a/yt/yt/core/misc/error-inl.h b/yt/yt/core/misc/error-inl.h
index b51b9e5154..449303229d 100644
--- a/yt/yt/core/misc/error-inl.h
+++ b/yt/yt/core/misc/error-inl.h
@@ -52,28 +52,27 @@ constexpr bool TErrorCode::operator == (TErrorCode rhs) const
namespace NDetail {
-template <size_t Length, class... TArgs>
-TString FormatErrorMessage(const char (&format)[Length], TArgs&&... args)
+template <class... TArgs>
+TString FormatErrorMessage(TStringBuf format, TArgs&&... args)
{
return Format(TRuntimeFormat{format}, std::forward<TArgs>(args)...);
}
-template <size_t Length>
-TString FormatErrorMessage(const char (&message)[Length])
+inline TString FormatErrorMessage(TStringBuf format)
{
- return TString(message);
+ return TString(format);
}
} // namespace NDetail
-template <size_t Length, class... TArgs>
-TError::TErrorOr(const char (&messageOrFormat)[Length], TArgs&&... args)
- : TErrorOr(NYT::EErrorCode::Generic, NYT::NDetail::FormatErrorMessage(messageOrFormat, std::forward<TArgs>(args)...))
+template <class... TArgs>
+TError::TErrorOr(TFormatString<TArgs...> format, TArgs&&... args)
+ : TErrorOr(NYT::EErrorCode::Generic, NYT::NDetail::FormatErrorMessage(format.Get(), std::forward<TArgs>(args)...), DisableFormat)
{ }
-template <size_t Length, class... TArgs>
-TError::TErrorOr(TErrorCode code, const char (&messageOrFormat)[Length], TArgs&&... args)
- : TErrorOr(code, NYT::NDetail::FormatErrorMessage(messageOrFormat, std::forward<TArgs>(args)...))
+template <class... TArgs>
+TError::TErrorOr(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args)
+ : TErrorOr(code, NYT::NDetail::FormatErrorMessage(format.Get(), std::forward<TArgs>(args)...), DisableFormat)
{ }
template <CInvocable<bool(const TError&)> TFilter>
@@ -102,20 +101,55 @@ std::optional<TError> TError::FindMatching(const TFilter& filter) const
return FindMatching([&] (const TError& error) { return filter(error.GetCode()); });
}
+#define IMPLEMENT_COPY_WRAP(...) \
+ return TError(__VA_ARGS__) << *this; \
+ static_assert(true)
+
+#define IMPLEMENT_MOVE_WRAP(...) \
+ return TError(__VA_ARGS__) << std::move(*this); \
+ static_assert(true)
+
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+TError TError::Wrap(U&& u) const &
+{
+ IMPLEMENT_COPY_WRAP(std::forward<U>(u));
+}
+
template <class... TArgs>
- requires std::constructible_from<TError, TArgs...>
-TError TError::Wrap(TArgs&&... args) const &
+TError TError::Wrap(TFormatString<TArgs...> format, TArgs&&... args) const &
{
- return TError(std::forward<TArgs>(args)...) << *this;
+ IMPLEMENT_COPY_WRAP(format, std::forward<TArgs>(args)...);
}
template <class... TArgs>
- requires std::constructible_from<TError, TArgs...>
-TError TError::Wrap(TArgs&&... args) &&
+TError TError::Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &
{
- return TError(std::forward<TArgs>(args)...) << std::move(*this);
+ IMPLEMENT_COPY_WRAP(code, format, std::forward<TArgs>(args)...);
}
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+TError TError::Wrap(U&& u) &&
+{
+ IMPLEMENT_MOVE_WRAP(std::forward<U>(u));
+}
+
+template <class... TArgs>
+TError TError::Wrap(TFormatString<TArgs...> format, TArgs&&... args) &&
+{
+ IMPLEMENT_MOVE_WRAP(format, std::forward<TArgs>(args)...);
+}
+
+template <class... TArgs>
+TError TError::Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&
+{
+ IMPLEMENT_MOVE_WRAP(code, format, std::forward<TArgs>(args)...);
+}
+
+#undef IMPLEMENT_COPY_WRAP
+#undef IMPLEMENT_MOVE_WRAP
+
template <CErrorNestable TValue>
TError&& TError::operator << (TValue&& rhs) &&
{
@@ -148,14 +182,63 @@ TError TError::operator << (const std::optional<TValue>& rhs) const &
}
}
+#define IMPLEMENT_THROW_ON_ERROR(...) \
+ if (!IsOK()) { \
+ THROW_ERROR std::move(*this).Wrap(__VA_ARGS__); \
+ } \
+ static_assert(true)
+
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+void TError::ThrowOnError(U&& u) const &
+{
+ IMPLEMENT_THROW_ON_ERROR(std::forward<U>(u));
+}
+
template <class... TArgs>
-void TError::ThrowOnError(TArgs&&... args) const
+void TError::ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) const &
{
- if (!IsOK()) {
- THROW_ERROR std::move(*this).Wrap(std::forward<TArgs>(args)...);
- }
+ IMPLEMENT_THROW_ON_ERROR(format, std::forward<TArgs>(args)...);
+}
+
+template <class... TArgs>
+void TError::ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &
+{
+ IMPLEMENT_THROW_ON_ERROR(code, format, std::forward<TArgs>(args)...);
+}
+
+inline void TError::ThrowOnError() const &
+{
+ IMPLEMENT_THROW_ON_ERROR();
}
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+void TError::ThrowOnError(U&& u) &&
+{
+ IMPLEMENT_THROW_ON_ERROR(std::forward<U>(u));
+}
+
+template <class... TArgs>
+void TError::ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) &&
+{
+ IMPLEMENT_THROW_ON_ERROR(format, std::forward<TArgs>(args)...);
+}
+
+template <class... TArgs>
+void TError::ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&
+{
+ IMPLEMENT_THROW_ON_ERROR(code, format, std::forward<TArgs>(args)...);
+}
+
+inline void TError::ThrowOnError() &&
+{
+ IMPLEMENT_THROW_ON_ERROR();
+}
+
+
+#undef IMPLEMENT_THROW_ON_ERROR
+
////////////////////////////////////////////////////////////////////////////////
template <class T>
@@ -249,37 +332,108 @@ TErrorOr<T>& TErrorOr<T>::operator = (TErrorOr<T>&& other) noexcept
return *this;
}
+#define IMPLEMENT_VALUE_OR_THROW_REF(...) \
+ if (!IsOK()) { \
+ THROW_ERROR Wrap(__VA_ARGS__); \
+ } \
+ return *Value_; \
+ static_assert(true)
+
+#define IMPLEMENT_VALUE_OR_THROW_MOVE(...) \
+ if (!IsOK()) { \
+ THROW_ERROR std::move(*this).Wrap(__VA_ARGS__); \
+ } \
+ return std::move(*Value_); \
+ static_assert(true)
+
+template <class T>
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+const T& TErrorOr<T>::ValueOrThrow(U&& u) const & Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_REF(std::forward<U>(u));
+}
+
template <class T>
template <class... TArgs>
-T&& TErrorOr<T>::ValueOrThrow(TArgs&&... args) && Y_LIFETIME_BOUND
+const T& TErrorOr<T>::ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND
{
- if (!IsOK()) {
- THROW_ERROR std::move(*this).Wrap(std::forward<TArgs>(args)...);
- }
- return std::move(*Value_);
+ IMPLEMENT_VALUE_OR_THROW_REF(format, std::forward<TArgs>(args)...);
}
template <class T>
template <class... TArgs>
-T& TErrorOr<T>::ValueOrThrow(TArgs&&... args) & Y_LIFETIME_BOUND
+const T& TErrorOr<T>::ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND
{
- if (!IsOK()) {
- THROW_ERROR Wrap(std::forward<TArgs>(args)...);
- }
- return *Value_;
+ IMPLEMENT_VALUE_OR_THROW_REF(code, format, std::forward<TArgs>(args)...);
+}
+
+template <class T>
+const T& TErrorOr<T>::ValueOrThrow() const & Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_REF();
+}
+
+template <class T>
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+T& TErrorOr<T>::ValueOrThrow(U&& u) & Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_REF(std::forward<U>(u));
}
template <class T>
template <class... TArgs>
-const T& TErrorOr<T>::ValueOrThrow(TArgs&&... args) const & Y_LIFETIME_BOUND
+T& TErrorOr<T>::ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND
{
- if (!IsOK()) {
- THROW_ERROR Wrap(std::forward<TArgs>(args)...);
- }
- return *Value_;
+ IMPLEMENT_VALUE_OR_THROW_REF(format, std::forward<TArgs>(args)...);
+}
+
+template <class T>
+template <class... TArgs>
+T& TErrorOr<T>::ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_REF(code, format, std::forward<TArgs>(args)...);
+}
+
+template <class T>
+T& TErrorOr<T>::ValueOrThrow() & Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_REF();
+}
+
+template <class T>
+template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+T&& TErrorOr<T>::ValueOrThrow(U&& u) && Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_MOVE(std::forward<U>(u));
+}
+
+template <class T>
+template <class... TArgs>
+T&& TErrorOr<T>::ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_MOVE(format, std::forward<TArgs>(args)...);
}
template <class T>
+template <class... TArgs>
+T&& TErrorOr<T>::ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_MOVE(code, format, std::forward<TArgs>(args)...);
+}
+
+template <class T>
+T&& TErrorOr<T>::ValueOrThrow() && Y_LIFETIME_BOUND
+{
+ IMPLEMENT_VALUE_OR_THROW_MOVE();
+}
+
+#undef IMPLEMENT_VALUE_OR_THROW_REF
+#undef IMPLEMENT_VALUE_OR_THROW_MOVE
+
+template <class T>
T&& TErrorOr<T>::Value() && Y_LIFETIME_BOUND
{
YT_ASSERT(IsOK());
@@ -376,15 +530,34 @@ TArg&& TErrorAdaptor::operator << (TArg&& rhs) const
return std::forward<TArg>(rhs);
}
-template <class TErrorLike, class... TArgs>
+template <class TErrorLike, class U>
requires
std::derived_from<std::remove_cvref_t<TErrorLike>, TError> &&
- std::constructible_from<TError, TArgs...>
-void ThrowErrorExceptionIfFailed(TErrorLike&& error, TArgs&&... args)
+ (!CStringLiteral<std::remove_cvref_t<U>>)
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, U&& u)
{
- if (!error.IsOK()) {
- THROW_ERROR std::move(error).Wrap(std::forward<TArgs>(args)...);
- }
+ std::move(error).ThrowOnError(std::forward<U>(u));
+}
+
+template <class TErrorLike, class... TArgs>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, TFormatString<TArgs...> format, TArgs&&... args)
+{
+ std::move(error).ThrowOnError(format, std::forward<TArgs>(args)...);
+}
+
+template <class TErrorLike, class... TArgs>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args)
+{
+ std::move(error).ThrowOnError(code, format, std::forward<TArgs>(args)...);
+}
+
+template <class TErrorLike>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error)
+{
+ std::move(error).ThrowOnError();
}
} // namespace NDetail
diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp
index bd27f1769a..fb0ee6f37f 100644
--- a/yt/yt/core/misc/error.cpp
+++ b/yt/yt/core/misc/error.cpp
@@ -425,7 +425,7 @@ TError::TErrorOr(TError&& other) noexcept
TError::TErrorOr(const std::exception& ex)
{
if (auto simpleException = dynamic_cast<const TSimpleException*>(&ex)) {
- *this = TError(NYT::EErrorCode::Generic, simpleException->GetMessage());
+ *this = TError(NYT::EErrorCode::Generic, TRuntimeFormat(simpleException->GetMessage()));
// NB: clang-14 is incapable of capturing structured binding variables
// so we force materialize them via this function call.
auto addAttribute = [this] (const auto& key, const auto& value) {
@@ -446,16 +446,16 @@ TError::TErrorOr(const std::exception& ex)
} else if (const auto* errorEx = dynamic_cast<const TErrorException*>(&ex)) {
*this = errorEx->Error();
} else {
- *this = TError(NYT::EErrorCode::Generic, ex.what());
+ *this = TError(NYT::EErrorCode::Generic, TRuntimeFormat(ex.what()));
}
YT_VERIFY(!IsOK());
}
-TError::TErrorOr(TString message)
+TError::TErrorOr(TString message, TDisableFormat)
: Impl_(std::make_unique<TImpl>(std::move(message)))
{ }
-TError::TErrorOr(TErrorCode code, TString message)
+TError::TErrorOr(TErrorCode code, TString message, TDisableFormat)
: Impl_(std::make_unique<TImpl>(code, std::move(message)))
{ }
@@ -482,7 +482,7 @@ TError TError::FromSystem()
TError TError::FromSystem(int error)
{
- return TError(TErrorCode(LinuxErrorCodeBase + error), LastSystemErrorText(error)) <<
+ return TError(TErrorCode(LinuxErrorCodeBase + error), TRuntimeFormat(LastSystemErrorText(error))) <<
TErrorAttribute("errno", error);
}
diff --git a/yt/yt/core/misc/error.h b/yt/yt/core/misc/error.h
index 742935583f..52ba6cfc93 100644
--- a/yt/yt/core/misc/error.h
+++ b/yt/yt/core/misc/error.h
@@ -124,18 +124,22 @@ public:
TErrorOr(const std::exception& ex);
- explicit TErrorOr(TString message);
- TErrorOr(TErrorCode code, TString message);
+ struct TDisableFormat
+ { };
+ static constexpr TDisableFormat DisableFormat = {};
- template <size_t Length, class... TArgs>
+ TErrorOr(TString message, TDisableFormat);
+ TErrorOr(TErrorCode code, TString message, TDisableFormat);
+
+ template <class... TArgs>
explicit TErrorOr(
- const char (&messageOrFormat)[Length],
+ TFormatString<TArgs...> format,
TArgs&&... arg);
- template <size_t Length, class... TArgs>
+ template <class... TArgs>
TErrorOr(
TErrorCode code,
- const char (&messageOrFormat)[Length],
+ TFormatString<TArgs...> format,
TArgs&&... args);
TError& operator = (const TError& other);
@@ -188,8 +192,23 @@ public:
bool IsOK() const;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ void ThrowOnError(U&& u) const &;
template <class... TArgs>
- void ThrowOnError(TArgs&&... args) const;
+ void ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) const &;
+ template <class... TArgs>
+ void ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &;
+ inline void ThrowOnError() const &;
+
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ void ThrowOnError(U&& u) &&;
+ template <class... TArgs>
+ void ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) &&;
+ template <class... TArgs>
+ void ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&;
+ inline void ThrowOnError() &&;
template <CInvocable<bool(const TError&)> TFilter>
std::optional<TError> FindMatching(const TFilter& filter) const;
@@ -198,14 +217,22 @@ public:
std::optional<TError> FindMatching(TErrorCode code) const;
std::optional<TError> FindMatching(const THashSet<TErrorCode>& codes) const;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ TError Wrap(U&& u) const &;
template <class... TArgs>
- requires std::constructible_from<TError, TArgs...>
- TError Wrap(TArgs&&... args) const &;
+ TError Wrap(TFormatString<TArgs...> format, TArgs&&... args) const &;
+ template <class... TArgs>
+ TError Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &;
TError Wrap() const &;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ TError Wrap(U&& u) &&;
+ template <class... TArgs>
+ TError Wrap(TFormatString<TArgs...> format, TArgs&&... args) &&;
template <class... TArgs>
- requires std::constructible_from<TError, TArgs...>
- TError Wrap(TArgs&&... args) &&;
+ TError Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&;
TError Wrap() &&;
//! Perform recursive aggregation of error codes and messages over the error tree.
@@ -349,11 +376,23 @@ struct TErrorAdaptor
};
// Make these to correctly forward TError to Wrap call.
-template <class TErrorLike, class... TArgs>
+template <class TErrorLike, class U>
requires
std::derived_from<std::remove_cvref_t<TErrorLike>, TError> &&
- std::constructible_from<TError, TArgs...>
-void ThrowErrorExceptionIfFailed(TErrorLike&& error, TArgs&&... args);
+ (!CStringLiteral<std::remove_cvref_t<U>>)
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, U&& u);
+
+template <class TErrorLike, class... TArgs>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, TFormatString<TArgs...> format, TArgs&&... args);
+
+template <class TErrorLike, class... TArgs>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error, TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args);
+
+template <class TErrorLike>
+ requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError>
+void ThrowErrorExceptionIfFailed(TErrorLike&& error);
} // namespace NDetail
@@ -411,14 +450,32 @@ public:
T& Value() & Y_LIFETIME_BOUND;
T&& Value() && Y_LIFETIME_BOUND;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ const T& ValueOrThrow(U&& u) const & Y_LIFETIME_BOUND;
+ template <class... TArgs>
+ const T& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND;
template <class... TArgs>
- const T& ValueOrThrow(TArgs&&... args) const & Y_LIFETIME_BOUND;
+ const T& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND;
+ const T& ValueOrThrow() const & Y_LIFETIME_BOUND;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ T& ValueOrThrow(U&& u) & Y_LIFETIME_BOUND;
template <class... TArgs>
- T& ValueOrThrow(TArgs&&... args) & Y_LIFETIME_BOUND;
+ T& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND;
+ template <class... TArgs>
+ T& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND;
+ T& ValueOrThrow() & Y_LIFETIME_BOUND;
+ template <class U>
+ requires (!CStringLiteral<std::remove_cvref_t<U>>)
+ T&& ValueOrThrow(U&& u) && Y_LIFETIME_BOUND;
+ template <class... TArgs>
+ T&& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND;
template <class... TArgs>
- T&& ValueOrThrow(TArgs&&... args) && Y_LIFETIME_BOUND;
+ T&& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND;
+ T&& ValueOrThrow() && Y_LIFETIME_BOUND;
const T& ValueOrDefault(const T& defaultValue Y_LIFETIME_BOUND) const & Y_LIFETIME_BOUND;
T& ValueOrDefault(T& defaultValue Y_LIFETIME_BOUND) & Y_LIFETIME_BOUND;
diff --git a/yt/yt/core/misc/unittests/error_ut.cpp b/yt/yt/core/misc/unittests/error_ut.cpp
index 9ab7ee2b54..42e731234a 100644
--- a/yt/yt/core/misc/unittests/error_ut.cpp
+++ b/yt/yt/core/misc/unittests/error_ut.cpp
@@ -475,7 +475,7 @@ TEST(TErrorTest, ErrorSkeletonStubImplementation)
TEST(TErrorTest, FormatCtor)
{
- EXPECT_EQ("Some error %v", TError("Some error %v").GetMessage());
+ // EXPECT_EQ("Some error %v", TError("Some error %v").GetMessage()); // No longer compiles due to static analysis.
EXPECT_EQ("Some error hello", TError("Some error %v", "hello").GetMessage());
}
@@ -853,6 +853,82 @@ TEST(TErrorTest, AttributeSerialization)
" L3\n"));
}
+TEST(TErrorTest, MacroStaticAnalysis)
+{
+ auto swallow = [] (auto expr) {
+ try {
+ expr();
+ } catch (...) {
+ }
+ };
+
+ swallow([] {
+ THROW_ERROR_EXCEPTION("Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION("Hello, %v", "World");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v%v", "Bar", "Baz");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
+ });
+}
+
+TEST(TErrorTest, WrapStaticAnalysis)
+{
+ TError error;
+ Y_UNUSED(error.Wrap());
+ Y_UNUSED(error.Wrap(std::exception{}));
+ Y_UNUSED(error.Wrap("Hello"));
+ Y_UNUSED(error.Wrap("Hello, %v", "World"));
+ Y_UNUSED(error.Wrap(TRuntimeFormat{"Hello, %v"}));
+}
+
+// NB(arkady-e1ppa): Uncomment these occasionally to see
+// that static analysis is still working.
+TEST(TErrorTest, MacroStaticAnalysisBrokenFormat)
+{
+ // auto swallow = [] (auto expr) {
+ // try {
+ // expr();
+ // } catch (...) {
+ // }
+ // };
+
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION("Hello, %v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION(TErrorCode{}, "Foo%v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, TErrorCode{}, "Foo%v");
+ // });
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace
diff --git a/yt/yt/core/rpc/bus/channel.cpp b/yt/yt/core/rpc/bus/channel.cpp
index c3a83ec520..2c6643a26e 100644
--- a/yt/yt/core/rpc/bus/channel.cpp
+++ b/yt/yt/core/rpc/bus/channel.cpp
@@ -558,9 +558,9 @@ private:
requestControl,
responseHandler,
TStringBuf("Request timed out"),
- TError(NYT::EErrorCode::Timeout, aborted
+ TError(NYT::EErrorCode::Timeout, TRuntimeFormat(aborted
? "Request timed out or timer was aborted"
- : "Request timed out"));
+ : "Request timed out")));
}
void HandleAcknowledgementTimeout(const TClientRequestControlPtr& requestControl, bool aborted)
diff --git a/yt/yt/core/rpc/grpc/channel.cpp b/yt/yt/core/rpc/grpc/channel.cpp
index 48aeea0d8b..5072245a29 100644
--- a/yt/yt/core/rpc/grpc/channel.cpp
+++ b/yt/yt/core/rpc/grpc/channel.cpp
@@ -95,7 +95,7 @@ public:
statusDetail = "Unknown error";
}
- return TError(StatusCodeToErrorCode(static_cast<grpc_status_code>(statusCode)), statusDetail)
+ return TError(StatusCodeToErrorCode(static_cast<grpc_status_code>(statusCode)), std::move(statusDetail), TError::DisableFormat)
<< TErrorAttribute("status_code", statusCode);
}
@@ -593,7 +593,7 @@ private:
if (serializedError) {
error = DeserializeError(serializedError);
} else {
- error = TError(StatusCodeToErrorCode(ResponseStatusCode_), ResponseStatusDetails_.AsString())
+ error = TError(StatusCodeToErrorCode(ResponseStatusCode_), ResponseStatusDetails_.AsString(), TError::DisableFormat)
<< TErrorAttribute("status_code", ResponseStatusCode_);
}
NotifyError(TStringBuf("Request failed"), error);
diff --git a/yt/yt/library/formats/skiff_yson_converter.cpp b/yt/yt/library/formats/skiff_yson_converter.cpp
index 3e375d03ec..82fac3ef1b 100644
--- a/yt/yt/library/formats/skiff_yson_converter.cpp
+++ b/yt/yt/library/formats/skiff_yson_converter.cpp
@@ -134,12 +134,15 @@ struct TOptionalTypesMatch
<< ex;
}
-template <typename... Args>
-[[noreturn]] void ThrowYsonToSkiffConversionError(const TComplexTypeFieldDescriptor& descriptor, const Args&... args)
+template <typename... TArgs>
+[[noreturn]] void ThrowYsonToSkiffConversionError(
+ const TComplexTypeFieldDescriptor& descriptor,
+ TFormatString<TArgs...> format,
+ TArgs&&... args)
{
THROW_ERROR_EXCEPTION("Yson to Skiff conversion error while converting %Qv field",
descriptor.GetDescription())
- << TError(args...);
+ << TError(format, std::forward<TArgs>(args)...);
}
[[noreturn]] void ThrowBadYsonToken(
@@ -168,12 +171,15 @@ template <typename... Args>
actual);
}
-template <typename... Args>
-[[noreturn]] void ThrowSkiffToYsonConversionError(const TComplexTypeFieldDescriptor& descriptor, const Args&... args)
+template <typename... TArgs>
+[[noreturn]] void ThrowSkiffToYsonConversionError(
+ const TComplexTypeFieldDescriptor& descriptor,
+ TFormatString<TArgs...> format,
+ TArgs&&... args)
{
THROW_ERROR_EXCEPTION("Skiff to Yson conversion error while converting %Qv field",
descriptor.GetDescription())
- << TError(args...);
+ << TError(format, std::forward<TArgs>(args)...);
}
TOptionalTypesMatch MatchOptionalTypes(