llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.11k stars 12.01k forks source link

False positive on clang-analyzer-optin.cplusplus.UninitializedObject #59733

Open brevzin opened 1 year ago

brevzin commented 1 year ago

Trying to reduce this from some fmt code:

#include <fmt/format.h>

using EH = fmt::detail::error_handler;

namespace fmt::detail {
template <typename Char, typename ErrorHandler = detail::error_handler>
class CompileParseContext
    : public basic_format_parse_context<Char, ErrorHandler> {
 private:
  int num_args_;
  const type* types_;
  using base = basic_format_parse_context<Char, ErrorHandler>;

 public:
  explicit constexpr CompileParseContext(
      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) {}
};
}

struct FormatStringChecker {
    #ifdef OK
    using parse_context_type = fmt::detail::CompileParseContext<char, EH>;
    #else
    using parse_context_type = fmt::detail::compile_parse_context<char, EH>;
    #endif

    parse_context_type context_;
    fmt::detail::type types_[1];

    constexpr FormatStringChecker(fmt::string_view format_str, EH eh)
        : context_(format_str, 0, types_, eh)
        , types_{}
    { }
};

void show() {
    FormatStringChecker c("", {});
}

When I run (with clang-15 and fmt-9.1.0):

$ clang-tidy -checks='-*,clang-analyzer-optin.cplusplus.UninitializedObject' foo.cxx -- -std=c++17 -I fmt/include -DOK

$ clang-tidy -checks='-*,clang-analyzer-optin.cplusplus.UninitializedObject' foo.cxx -- -std=c++17 -I fmt/include
1 warning generated.
/home/brevzin/sandbox/foo.cxx:34:17: error: 5 uninitialized fields at the end of the constructor call [clang-analyzer-optin.cplusplus.UninitializedObject,-warnings-as-errors]
        , types_{}
                ^
/home/brevzin/sandbox/fmt/include/fmt/core.h:732:7: note: uninitialized field 'this->context_.num_args_'
  int num_args_;
      ^~~~~~~~~
/home/brevzin/sandbox/fmt/include/fmt/core.h:733:15: note: uninitialized pointer 'this->context_.types_'
  const type* types_;
              ^~~~~~
/home/brevzin/sandbox/fmt/include/fmt/core.h:432:15: note: uninitialized pointer 'this->context_.basic_format_parse_context::format_str_.data_'
  const Char* data_;
              ^~~~~
/home/brevzin/sandbox/fmt/include/fmt/core.h:433:10: note: uninitialized field 'this->context_.basic_format_parse_context::format_str_.size_'
  size_t size_;
         ^~~~~
/home/brevzin/sandbox/fmt/include/fmt/core.h:657:7: note: uninitialized field 'this->context_.basic_format_parse_context::next_arg_id_'
  int next_arg_id_;
      ^~~~~~~~~~~~
/home/brevzin/sandbox/foo.cxx:39:25: note: Calling constructor for 'FormatStringChecker'
    FormatStringChecker c("", {});
                        ^~~~~~~~~
/home/brevzin/sandbox/foo.cxx:34:17: note: 5 uninitialized fields at the end of the constructor call
        , types_{}
                ^~
1 warning treated as error

All of these fields are actually initialized though. What's especially confusing is that my CompileParseContext is exactly the same as fmt::detail::compile_parse_context, as you can see here:

template <typename Char, typename ErrorHandler = detail::error_handler>
class compile_parse_context
    : public basic_format_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) {}

And yet my version (which I copied to continue attempting to reduce) doesn't give uninitialized errors.

llvmbot commented 1 year ago

@llvm/issue-subscribers-clang-static-analyzer