aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
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 /library/cpp
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
Diffstat (limited to 'library/cpp')
-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
4 files changed, 129 insertions, 131 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_