Repository navigation
Fix #15105: False negative: uninitMemberVar with inherited constructor - #8937
ludviggunne wants to merge 4 commits into
Conversation
909bea4 to
58e440d
Compare
dmcppcheck
left a comment
There was a problem hiding this comment.
AI review
The PR adds a false positive. getInheritedConstructors() treats any using Base::Base; as proof that a constructor is inherited. It never checks whether Base has a constructor that actually gets inherited, so uninitMemberVar now names an "inherited constructor" that does not exist in these cases:
- The derived class hides the base constructor. If the derived class declares a constructor with the same signature and initializes the member there, the warning is still given. Nothing in the program leaves the member uninitialized, so this one is a plain false positive.
- The base has no constructor that can be inherited. Default, copy and move constructors are never inherited, so this covers a base with only a default constructor or no user-declared constructor at all. The base build reports nothing here, consistent with how cppcheck treats structs without constructors. The PR now warns about a constructor "inherited from 'Base'" that is not there.
- The base constructor is deleted or private. The inherited constructor is then deleted or inaccessible and can never run, but the warning is still given.
Apart from these, the new tests pass, and qualified bases (ns::B), template instantiations and anonymous unions behave the same way as for ordinary constructors.
Testing
I built cppcheck and testrunner in pr/, and cppcheck in base/. ./testrunner TestConstructors TestClass ran 369 tests with 0 failures.
Test file scratch/t1.cpp:
struct Base1 { Base1(); };
struct D1 : Base1 { using Base1::Base1; int x; }; // only default ctor: nothing inherited
struct Base2 {};
struct D2 : Base2 { using Base2::Base2; int x; }; // no ctors: nothing inherited
struct Base3 { Base3(int); };
struct D3 : Base3 { using Base3::Base3; D3(int) : Base3(0), x(0) {} int x; }; // Base3(int) hidden by D3(int)
struct Base7 { private: Base7(int); };
struct D7 : Base7 { using Base7::Base7; int x; }; // inherited ctor inaccessible
struct Base8 { Base8(int) = delete; };
struct D8 : Base8 { using Base8::Base8; int x; }; // inherited ctor deletedCommand: cppcheck -q --enable=warning t1.cpp
PR output (with my numbering of the file above):
t1.cpp:2:34: warning: Member variable 'D1::x' is not initialized in the constructor inherited from 'Base1'. [uninitMemberVar]
t1.cpp:5:34: warning: Member variable 'D2::x' is not initialized in the constructor inherited from 'Base2'. [uninitMemberVar]
t1.cpp:8:34: warning: Member variable 'D3::x' is not initialized in the constructor inherited from 'Base3'. [uninitMemberVar]
t1.cpp:11:34: warning: Member variable 'D7::x' is not initialized in the constructor inherited from 'Base7'. [uninitMemberVar]
t1.cpp:14:34: warning: Member variable 'D8::x' is not initialized in the constructor inherited from 'Base8'. [uninitMemberVar]
The base build gives none of these warnings.
Other cases I tried behave as expected:
- Static, const-with-default and
std::stringmembers: no warning. - Pointer member: warns.
struct D : ns::B { using ns::B::B; }: warns.- Template instantiation
D4<int>: one warning, no duplicate. - Anonymous union members: same output as an ordinary constructor gives.
Automated review of 58e440d by Claude. The pull request was built and tested locally.
|
|
||
| if (baseName && Token::Match(tok, "%name% :: %name% ;") && | ||
| tok->str() == baseName->str() && tok->strAt(2) == baseName->str()) | ||
| result.push_back(tok->tokAt(2)); |
There was a problem hiding this comment.
AI review
This only matches the using Base::Base; syntax. It never checks that Base has a constructor that gets inherited and used. Default, copy and move constructors are never inherited, and a base constructor is hidden when the derived class declares one with the same parameters. This produces false positives:
struct Base3 { Base3(int); };
struct D3 : Base3 { using Base3::Base3; D3(int) : Base3(0), x(0) {} int x; };The PR warns: Member variable 'D3::x' is not initialized in the constructor inherited from 'Base3'. Nothing is inherited here.
The same happens for struct Base1 { Base1(); }; and for struct Base2 {};, which the base build reports nothing for. It also happens when the base constructor is = delete or private.
When the base type is known, it would be better to look at its functionList. Count only constructors that are not default, copy or move constructors and are not deleted. Leave out those whose signature is redeclared in the derived class.
No description provided.