Skip to content

clang-tidy misdiagnosing uninitialized members at the end of a constructor. #70464

Closed
@cbuchner1

Description

@cbuchner1
#include <memory>
#include <vector>
#include <cassert>

template <class T> struct foo { T x; };

class bar : public foo<double> {
 public:
   bar(double x) : foo{x} { }
   std::shared_ptr<std::vector<char>> y;
};

int main(int argc, char **argv)
{
   bar test = {12.34};
   assert(test.x == 12.34);
   assert(!test.y);
   return 0;
}

I used clang-tidy based on Debian LLVM version 15.0.7 for my testing. (UPDATE: same for 17.0.1 and master branch)

Here is a short reproducer for clang-tidy misdiagnosing an uninitialized member in a constructor [clang-analyzer-optin.cplusplus.UninitializedObject] as well as misdiagnosing a garbage value in test.x [clang-analyzer-core.UndefinedBinaryOperatorResult] even though the assert verifies that the member is being initialized correctly in the constructor.

To reproduce the bug, execute

clang-tidy repro.cpp

2 warnings generated.
/home/buchner/repro/repro.cpp:9:4: warning: 1 uninitialized field at the end of the constructor call [clang-analyzer-optin.cplusplus.UninitializedObject]
   bar(double x) : foo{x} { }
   ^
/home/buchner/repro/repro.cpp:5:35: note: uninitialized field 'this->foo::x'
template <class T> struct foo { T x; };
                                  ^
/home/buchner/repro/repro.cpp:15:15: note: Calling constructor for 'bar'
   bar test = {12.34};
              ^~~~~~~
/home/buchner/repro/repro.cpp:9:4: note: 1 uninitialized field at the end of the constructor call
   bar(double x) : foo{x} { }
   ^~~
/home/buchner/repro/repro.cpp:16:18: warning: The left operand of '==' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult]
   assert(test.x == 12.34);
                 ^
/usr/include/assert.h:93:27: note: expanded from macro 'assert'
     (static_cast <bool> (expr)                                         \
                          ^~~~
/home/buchner/repro/repro.cpp:15:15: note: Calling constructor for 'bar'
   bar test = {12.34};
              ^~~~~~~
/home/buchner/repro/repro.cpp:9:29: note: Returning without writing to 'this->x'
   bar(double x) : foo{x} { }
                            ^
/home/buchner/repro/repro.cpp:15:15: note: Returning from constructor for 'bar'
   bar test = {12.34};
              ^~~~~~~
/home/buchner/repro/repro.cpp:16:18: note: The left operand of '==' is a garbage value
   assert(test.x == 12.34);
                 ^
/usr/include/assert.h:93:27: note: expanded from macro 'assert'
     (static_cast <bool> (expr)                                         \

The culprit appears to be the brace initialization of the base struct foo within the constructor initializer list of class bar.

To verify result correctness, execute the following, which does not hit the assert.

clang++ -g -o repro repro.cpp && ./repro

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions