Why clang-tidy reports about uninitialized fields, if they are inited?

93 views Asked by At

I reported issue to spdlog project where I provided clang-tidy report about uninitialized fields. It looks like this:

/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:2955:15: warning: 5 uninitialized fields at the end of the constructor call [clang-analyzer-optin.cplusplus.UninitializedObject]
 2955 |         types_{
      |               ^
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:732:7: note: uninitialized field 'this->context_.num_args_'
  732 |   int num_args_;
      |       ^~~~~~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:733:15: note: uninitialized pointer 'this->context_.types_'
  733 |   const type* types_;
      |               ^~~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:432:15: note: uninitialized pointer 'this->context_.basic_format_parse_context::format_str_.data_'
  432 |   const Char* data_;
      |               ^~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:433:10: note: uninitialized field 'this->context_.basic_format_parse_context::format_str_.size_'
  433 |   size_t size_;
      |          ^~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:657:7: note: uninitialized field 'this->context_.basic_format_parse_context::next_arg_id_'
  657 |   int next_arg_id_;
      |       ^~~~~~~~~~~~
/Users/user/projects/github/spdlog/myapp/main.cpp:6:19: note: Calling constructor for 'basic_format_string<char, int>'
    6 |     spdlog::error("Some error message with arg: {}", 1);
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:3155:5: note: Taking true branch
 3155 |     if constexpr (detail::count_named_args<Args...>() ==
      |     ^
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:3159:47: note: Calling constructor for 'format_string_checker<char, fmt::detail::error_handler, int>'
 3159 |       detail::parse_format_string<true>(str_, checker(s, {}));
      |                                               ^~~~~~~~~~~~~~
/Users/user/projects/github/spdlog/myapp/../artifacts/include/spdlog/fmt/bundled/core.h:2955:15: note: 5 uninitialized fields at the end of the constructor call
 2955 |         types_{
      |               ^
 2956 |             mapped_type_constant<Args,
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~
 2957 |                                  basic_format_context<Char*, Char>>::value...} {
      |                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

main.cpp code looks quite simpe:

#include <spdlog/spdlog.h>

int main() 
{
    spdlog::error("Some error message with arg: {}", 1);
}

Then I started code reading to understand why variables are not inited and how can I fix that. Unfortunately, I didn't realise the root of the warnings.

spdlog::error("Some error message with arg: {}", 1); leads to calling error function. It has the following signature:

template<typename... Args>
inline void error(format_string_t<Args...> fmt, Args &&... args);

Here fmt argument has to be created - leads to calling format_string_t constructor:

template<typename... Args>
using format_string_t = fmt::format_string<Args...>;

fmt::format_string expands to:

template<typename... Args>
using format_string = basic_format_string<char, type_identity_t<Args>...>;

basic_format_string class looks like this:

template<typename Char, typename... Args>
class basic_format_string
{
  ...
public:
    template<typename S, FMT_ENABLE_IF(std::is_convertible<const S &, basic_string_view<Char>>::value)>
    FMT_CONSTEVAL FMT_INLINE basic_format_string(const S &s)
        : str_(s)
    {
        ...
        if constexpr (detail::count_named_args<Args...>() == detail::count_statically_named_args<Args...>())
        {
            using checker = detail::format_string_checker<Char, detail::error_handler, remove_cvref_t<Args>...>;
            detail::parse_format_string<true>(str_, checker(s, {}));
        }
    }
  ...
}

Here format_string_checker constructor is calling:

template <typename Char, typename ErrorHandler, typename... Args>
class format_string_checker {
  ...

 public:
  explicit FMT_CONSTEXPR format_string_checker(
      basic_string_view<Char> format_str, ErrorHandler eh)
      : context_(format_str, num_args, types_, eh),
        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
        types_{
            mapped_type_constant<Args,
                                 basic_format_context<Char*, Char>>::value...} {
  }
  ...
}

So, context_ constructor is calling as well - compile_parse_context<Char, ErrorHandler>

private:
    int num_args_;
    const type *types_;
    using base = basic_format_parse_context<Char, ErrorHandler>;

public:
    explicit FMT_CONSTEXPR compile_parse_context(
        basic_string_view<Char> format_str, int num_args, const type *types, ErrorHandler eh = {}, int next_arg_id = 0)
        : base(format_str, eh, next_arg_id)
        , num_args_(num_args)
        , types_(types)
    {}
  ...
}

num_args_ and types_ are inited. I thought maybe I'm missing something and somehow default constructor is calling, and members don't have default values. However, even I set default values for these member - clang-tidy reports about the same problem.

Am I missing something or clang-tidy reports about that problem by mistake?


spdlog: v1.13.0

clang information

user@mac build % clang++ --version                                 
Apple clang version 15.0.0 (clang-1500.1.0.2.5)
Target: arm64-apple-darwin23.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
user@mac build % clang-tidy --version                                 
LLVM (http://llvm.org/):
  LLVM version 19.0.0git
  Optimized build.

UPD.O:

  • I tried to specify core.h in HeaderFilterRegex to not analyze it. The same result.
  • I managed to suppress this warning by adding /* NOLINTBEGIN */ and /* NOLINTEND */ before FMT_BEGIN_DETAIL_NAMESPACE and after FMT_END_DETAIL_NAMESPACE (wrap compile_parse_context class) - it works. However, it is bad practice to patch 3rd party library and even worse do that without confidence the problem doesn't exist.
0

There are 0 answers