Repository navigation
Fix #15105: False negative: uninitMemberVar with inherited constructor #8937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
68d66bc
1ff8dd1
fa2cbdc
58e440d
762ef95
c19716f
a93405b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -195,6 +195,65 @@ void CheckClassImpl::handleUnionMembers(std::vector<Usage>& usageList) | |
| } | ||
| } | ||
|
|
||
| static std::vector<const Token *> getInheritedConstructors(const Scope *scope) | ||
| { | ||
| std::vector<const Token *> result; | ||
|
|
||
| if (!scope || !scope->bodyStart || !scope->definedType) | ||
| return result; | ||
|
|
||
| const auto isInherited = [](const Function &baseCtor, const Scope *scope) { | ||
| if (baseCtor.type != FunctionType::eConstructor || baseCtor.argCount() == 0) | ||
| return false; | ||
| if (baseCtor.isDelete() || baseCtor.access == AccessControl::Private) | ||
| return false; | ||
| return std::none_of(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function &func) { | ||
| return func.isConstructor() && func.argCount() == baseCtor.argCount() && | ||
| func.argsMatch(baseCtor.nestedIn, baseCtor.argDef, func.argDef, "", 0); | ||
| }); | ||
| }; | ||
|
|
||
| const std::vector<Type::BaseInfo> &bases = scope->definedType->derivedFrom; | ||
| const Token *baseName = nullptr; | ||
| const std::list<Function> *baseFuncs = nullptr; | ||
| for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { | ||
| if (Token::Match(tok, "using %name%")) { | ||
| const auto base = std::find_if( | ||
| bases.cbegin(), | ||
| bases.cend(), | ||
| [&tok](const Type::BaseInfo &info) { | ||
| return info.name == tok->strAt(1); | ||
| } | ||
| ); | ||
| if (base != bases.cend() && base->type && base->type->classScope) { | ||
| baseName = tok->next(); | ||
| baseFuncs = &base->type->classScope->functionList; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI review This only looks at constructors that the base declares itself. If the base inherits its constructors with its own struct A { A(int); };
struct B : A { using A::A; };
struct D : B { using B::B; int x; }; // no warning for D::xIt would be worth adding at least a TODO test for this. |
||
| } | ||
| continue; | ||
| } | ||
|
|
||
| if (tok->str() == ";") { | ||
| baseName = nullptr; | ||
| continue; | ||
| } | ||
|
|
||
| if (tok->str() == "{") { | ||
| baseName = nullptr; | ||
| tok = tok->link(); | ||
| continue; | ||
| } | ||
|
|
||
| if (baseName && Token::Match(tok, "%name% :: %name% ;") && | ||
| tok->str() == baseName->str() && tok->strAt(2) == baseName->str() && | ||
| std::any_of(baseFuncs->cbegin(), baseFuncs->cend(), [&](const Function &func) { | ||
| return isInherited(func, scope); | ||
| })) | ||
| result.push_back(tok->tokAt(2)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AI review This only matches the struct Base3 { Base3(int); };
struct D3 : Base3 { using Base3::Base3; D3(int) : Base3(0), x(0) {} int x; };The PR warns: The same happens for When the base type is known, it would be better to look at its |
||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| //--------------------------------------------------------------------------- | ||
| // ClassCheck: Check that all class constructors are ok. | ||
| //--------------------------------------------------------------------------- | ||
|
|
@@ -223,9 +282,11 @@ void CheckClassImpl::constructors() | |
| }); | ||
| }); | ||
|
|
||
| const std::vector<const Token *> inheritedCtors = getInheritedConstructors(scope); | ||
|
|
||
| // There are no constructors. | ||
| std::set<const Variable*> diagVars; | ||
| if (scope->numConstructors == 0 && printStyle && !usedInUnion) { | ||
| if (scope->numConstructors == 0 && inheritedCtors.empty() && printStyle && !usedInUnion) { | ||
| // If there is a private variable, there should be a constructor.. | ||
| int needInit = 0, haveInit = 0; | ||
| std::vector<const Variable*> uninitVars; | ||
|
|
@@ -339,6 +400,30 @@ void CheckClassImpl::constructors() | |
| } | ||
| } | ||
|
|
||
| if (!inheritedCtors.empty()) { | ||
| clearAllVar(usageList); | ||
|
|
||
| for (Usage &usage : usageList) { | ||
| if (usage.var->hasDefault()) | ||
| usage.init = true; | ||
| } | ||
|
|
||
| handleUnionMembers(usageList); | ||
|
|
||
| // Inherited constructors don't initialize members of the derived class | ||
| for (const Token *baseCtorTok : inheritedCtors) { | ||
| for (const Usage &usage : usageList) { | ||
| if (isInitialized(usage, FunctionType::eConstructor)) | ||
| continue; | ||
| const Variable& var = *usage.var; | ||
| if (var.typeScope() && var.typeScope()->numConstructors > 0) | ||
| continue; | ||
| uninitVarError(baseCtorTok, false, FunctionType::eConstructor, var.scope()->className, var.name(), scope != var.scope(), false, false, true); | ||
| diagVars.emplace(&var); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (scope->numConstructors == 0) { | ||
|
|
||
| // Mark all variables not used | ||
|
|
@@ -1172,12 +1257,15 @@ void CheckClassImpl::noExplicitConstructorError(const Token *tok, const std::str | |
| reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); | ||
| } | ||
|
|
||
| void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor) | ||
| void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor, bool inheritedCtor) | ||
| { | ||
| std::string message("Member variable '$symbol' "); | ||
| if (noCtor) | ||
| message += "has no initializer."; | ||
| else { | ||
| else if (inheritedCtor) { | ||
| message += tok ? "is not initialized in the constructor inherited from '" + tok->str() + "'." : | ||
| "is not initialized in the inherited constructor."; | ||
| } else { | ||
| message += "is not initialized in the "; | ||
| if (functionType == FunctionType::eCopyConstructor) | ||
| message += "copy "; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI review
For a template-instantiated base,
info.nameisB7<int>. Neitherusing B7::B7;norusing B7<int>::B7;matches it, sostruct D : B7<int> { using B7::B7; int x; };gets no warning. This is a false negative, not a false positive.