static_analysis-inl.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. #ifndef STATIC_ANALYSIS_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include static_analysis.h"
  3. // For the sake of sane code completion.
  4. #include "static_analysis.h"
  5. #endif
  6. #include <library/cpp/yt/misc/preprocessor.h>
  7. #include <library/cpp/yt/string/format.h>
  8. #include <string_view>
  9. #include <variant> // monostate
  10. namespace NYT::NLogging::NDetail {
  11. ////////////////////////////////////////////////////////////////////////////////
  12. // Tag for dispatching proper TFormatArg specialization.
  13. template <class T>
  14. struct TLoggerFormatArg
  15. { };
  16. // Required for TLoggerFormatArg to inherit CFormattable concept
  17. // from T.
  18. template <class T>
  19. requires CFormattable<T>
  20. void FormatValue(TStringBuilderBase*, const TLoggerFormatArg<T>&, TStringBuf);
  21. ////////////////////////////////////////////////////////////////////////////////
  22. // Stateless constexpr way of capturing arg types
  23. // without invoking any ctors. With the help of macros
  24. // can turn non-constexpr argument pack of arguments
  25. // into constexpr pack of types.
  26. template <class... TArgs>
  27. struct TLoggerFormatArgs
  28. { };
  29. // Used for macro conversion. Purposefully undefined.
  30. template <class... TArgs>
  31. TLoggerFormatArgs<std::remove_cvref_t<TArgs>...>
  32. AsFormatArgs(TArgs&&...);
  33. ////////////////////////////////////////////////////////////////////////////////
  34. template <bool First, bool Second>
  35. struct TAnalyserDispatcher
  36. {
  37. template <class... TArgs>
  38. static consteval void Do(std::string_view, std::string_view, TLoggerFormatArgs<TArgs...>)
  39. {
  40. // Give up :(
  41. // We can't crash here, because, for example, YT_LOG_ERROR(error) exists
  42. // and we can't really check if error is actually TError or something else here.
  43. // and probably shouldn't bother trying.
  44. }
  45. };
  46. template <bool Second>
  47. struct TAnalyserDispatcher<true, Second>
  48. {
  49. template <class TFirst, class... TArgs>
  50. static consteval void Do(std::string_view str, std::string_view, TLoggerFormatArgs<TFirst, TArgs...>)
  51. {
  52. // Remove outer \"'s generated by PP_STRINGIZE.
  53. auto stripped = std::string_view(std::begin(str) + 1, std::size(str) - 2);
  54. ::NYT::NDetail::TFormatAnalyser::ValidateFormat<TLoggerFormatArg<TArgs>...>(stripped);
  55. }
  56. };
  57. template <>
  58. struct TAnalyserDispatcher<false, true>
  59. {
  60. template <class TFirst, class TSecond, class... TArgs>
  61. static consteval void Do(std::string_view, std::string_view str, TLoggerFormatArgs<TFirst, TSecond, TArgs...>)
  62. {
  63. // Remove outer \"'s generated by PP_STRINGIZE.
  64. auto stripped = std::string_view(std::begin(str) + 1, std::size(str) - 2);
  65. ::NYT::NDetail::TFormatAnalyser::ValidateFormat<TLoggerFormatArg<TArgs>...>(stripped);
  66. }
  67. };
  68. ////////////////////////////////////////////////////////////////////////////////
  69. // This value is never read since homogenization works for unevaluated expressions.
  70. inline constexpr auto InvalidToken = std::monostate{};
  71. ////////////////////////////////////////////////////////////////////////////////
  72. #define PP_VA_PICK_1_IMPL(N, ...) N
  73. #define PP_VA_PICK_2_IMPL(_1, N, ...) N
  74. ////////////////////////////////////////////////////////////////////////////////
  75. //! Parameter pack parsing.
  76. #define STATIC_ANALYSIS_CAPTURE_TYPES(...) \
  77. decltype(::NYT::NLogging::NDetail::AsFormatArgs(__VA_ARGS__)){}
  78. #define STATIC_ANALYSIS_FIRST_TOKEN(...) \
  79. PP_STRINGIZE( \
  80. PP_VA_PICK_1_IMPL(__VA_ARGS__ __VA_OPT__(,) ::NYT::NLogging::NDetail::InvalidToken))
  81. #define STATIC_ANALYSIS_SECOND_TOKEN(...) \
  82. PP_STRINGIZE(\
  83. PP_VA_PICK_2_IMPL( \
  84. __VA_ARGS__ __VA_OPT__(,) \
  85. ::NYT::NLogging::NDetail::InvalidToken, \
  86. ::NYT::NLogging::NDetail::InvalidToken))
  87. #define STATIC_ANALYSIS_FIRST_TOKEN_COND(...) \
  88. STATIC_ANALYSIS_FIRST_TOKEN(__VA_ARGS__)[0] == '\"'
  89. #define STATIC_ANALYSIS_SECOND_TOKEN_COND(...) \
  90. STATIC_ANALYSIS_SECOND_TOKEN(__VA_ARGS__)[0] == '\"'
  91. #undef STATIC_ANALYSIS_CHECK_LOG_FORMAT
  92. #define STATIC_ANALYSIS_CHECK_LOG_FORMAT(...) \
  93. ::NYT \
  94. ::NLogging \
  95. ::NDetail \
  96. ::TAnalyserDispatcher< \
  97. STATIC_ANALYSIS_FIRST_TOKEN_COND(__VA_ARGS__), \
  98. STATIC_ANALYSIS_SECOND_TOKEN_COND(__VA_ARGS__) \
  99. >::Do( \
  100. STATIC_ANALYSIS_FIRST_TOKEN(__VA_ARGS__), \
  101. STATIC_ANALYSIS_SECOND_TOKEN(__VA_ARGS__), \
  102. STATIC_ANALYSIS_CAPTURE_TYPES(__VA_ARGS__))
  103. ////////////////////////////////////////////////////////////////////////////////
  104. } // namespace NYT::NLogging::NDetail
  105. template <class T>
  106. struct NYT::TFormatArg<NYT::NLogging::NDetail::TLoggerFormatArg<T>>
  107. : public NYT::TFormatArgBase
  108. {
  109. // We mix in '\"' and ' ' which is an artifact of logging stringize.
  110. // We want to support YT_LOG_XXX("Value: %" PRIu64, 42)
  111. // for plantform independent prints of numbers.
  112. // String below may be converted to a token:
  113. // "\"Value: %\" \"u\""
  114. // Thus adding a \" \" sequence.
  115. static constexpr auto FlagSpecifiers
  116. = TFormatArgBase::ExtendFlags</*Hot*/ false, 2, std::array{'\"', ' '}, /*TFrom*/ T>();
  117. };