Skip to content

Commit d2e8ab9

Browse files
IOBYTEdanmar
authored andcommitted
Fixed cppcheck-opensource#4302 (Member variable not initialized in public delegate constructor)
1 parent 62b0519 commit d2e8ab9

5 files changed

Lines changed: 140 additions & 65 deletions

File tree

lib/checkclass.cpp

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ void CheckClass::constructors()
105105
// Mark all variables not used
106106
clearAllVar(usage);
107107

108-
std::list<std::string> callstack;
108+
std::list<const Function *> callstack;
109109
initializeVarList(*func, callstack, &(*scope), usage);
110110

111111
// Check if any variables are uninitialized
@@ -354,7 +354,7 @@ bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope)
354354
return false;
355355
}
356356

357-
void CheckClass::initializeVarList(const Function &func, std::list<std::string> &callstack, const Scope *scope, std::vector<Usage> &usage)
357+
void CheckClass::initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage)
358358
{
359359
bool initList = func.type == Function::eConstructor || func.type == Function::eCopyConstructor;
360360
const Token *ftok = func.arg->link()->next();
@@ -364,8 +364,35 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
364364
// clKalle::clKalle() : var(value) { }
365365
if (initList) {
366366
if (level == 0 && Token::Match(ftok, "%var% (")) {
367-
if (ftok->strAt(2) != ")")
368-
initVar(ftok->str(), scope, usage);
367+
if (ftok->str() != func.name()) {
368+
if (ftok->strAt(2) != ")")
369+
initVar(ftok->str(), scope, usage);
370+
} else { // c++11 delegate constructor
371+
const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
372+
// member function found
373+
if (member) {
374+
// recursive call
375+
// assume that all variables are initialized
376+
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
377+
/** @todo false negative: just bail */
378+
assignAllVar(usage);
379+
return;
380+
}
381+
382+
// member function has implementation
383+
if (member->hasBody) {
384+
// initialize variable use list using member function
385+
callstack.push_back(member);
386+
initializeVarList(*member, callstack, scope, usage);
387+
callstack.pop_back();
388+
}
389+
390+
// there is a called member function, but it has no implementation, so we assume it initializes everything
391+
else {
392+
assignAllVar(usage);
393+
}
394+
}
395+
}
369396
} else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) {
370397
initVar(ftok->str(), scope, usage);
371398
ftok = ftok->linkAt(1);
@@ -469,29 +496,22 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
469496
// Calling member function?
470497
else if (Token::simpleMatch(ftok, "operator= (") &&
471498
ftok->previous()->str() != "::") {
472-
// recursive call / calling overloaded function
473-
// assume that all variables are initialized
474-
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end()) {
475-
/** @todo false negative: just bail */
476-
assignAllVar(usage);
477-
return;
478-
}
479-
480-
/** @todo check function parameters for overloaded function so we check the right one */
481-
// check if member function exists
482-
std::list<Function>::const_iterator it;
483-
for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it) {
484-
if (ftok->str() == it->tokenDef->str() && it->type != Function::eConstructor)
485-
break;
486-
}
487-
499+
const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
488500
// member function found
489-
if (it != scope->functionList.end()) {
501+
if (member) {
502+
// recursive call
503+
// assume that all variables are initialized
504+
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
505+
/** @todo false negative: just bail */
506+
assignAllVar(usage);
507+
return;
508+
}
509+
490510
// member function has implementation
491-
if (it->hasBody) {
511+
if (member->hasBody) {
492512
// initialize variable use list using member function
493-
callstack.push_back(ftok->str());
494-
initializeVarList(*it, callstack, scope, usage);
513+
callstack.push_back(member);
514+
initializeVarList(*member, callstack, scope, usage);
495515
callstack.pop_back();
496516
}
497517

@@ -517,27 +537,23 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
517537
}
518538
}
519539

520-
// recursive call / calling overloaded function
521-
// assume that all variables are initialized
522-
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end()) {
523-
assignAllVar(usage);
524-
return;
525-
}
526-
527540
// check if member function
528-
std::list<Function>::const_iterator it;
529-
for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it) {
530-
if (ftok->str() == it->tokenDef->str() && it->type != Function::eConstructor)
531-
break;
532-
}
541+
const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
533542

534543
// member function found
535-
if (it != scope->functionList.end()) {
544+
if (member && member->type != Function::eConstructor) {
545+
// recursive call
546+
// assume that all variables are initialized
547+
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
548+
assignAllVar(usage);
549+
return;
550+
}
551+
536552
// member function has implementation
537-
if (it->hasBody) {
553+
if (member->hasBody) {
538554
// initialize variable use list using member function
539-
callstack.push_back(ftok->str());
540-
initializeVarList(*it, callstack, scope, usage);
555+
callstack.push_back(member);
556+
initializeVarList(*member, callstack, scope, usage);
541557
callstack.pop_back();
542558

543559
// Assume that variables that are passed to it are initialized..

lib/checkclass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class CPPCHECKLIB CheckClass : public Check {
244244
* @param scope pointer to variable Scope
245245
* @param usage reference to usage vector
246246
*/
247-
void initializeVarList(const Function &func, std::list<std::string> &callstack, const Scope *scope, std::vector<Usage> &usage);
247+
void initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage);
248248

249249
static bool canNotCopy(const Scope *scope);
250250
};

lib/symboldatabase.cpp

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,6 +2299,8 @@ const Function *SymbolDatabase::findFunctionByToken(const Token *tok) const
22992299
return 0;
23002300
}
23012301

2302+
//---------------------------------------------------------------------------
2303+
23022304
const Function* SymbolDatabase::findFunctionByName(const std::string& str, const Scope* startScope) const
23032305
{
23042306
const Scope* currScope = startScope;
@@ -2318,10 +2320,43 @@ const Function* SymbolDatabase::findFunctionByName(const std::string& str, const
23182320
return 0;
23192321
}
23202322

2323+
//---------------------------------------------------------------------------
2324+
23212325
/** @todo This function only counts the number of arguments in the function call.
23222326
It does not take into account functions with default arguments.
23232327
It does not take into account argument types. This can be difficult because of promotion and conversion operators and casts and because the argument can also be a function call.
23242328
*/
2329+
const Function* SymbolDatabase::findFunctionByNameAndArgsInScope(const Token *tok, const Scope *scope) const
2330+
{
2331+
for (std::list<Function>::const_iterator i = scope->functionList.begin(); i != scope->functionList.end(); ++i) {
2332+
if (i->tokenDef->str() == tok->str()) {
2333+
const Function *func = &*i;
2334+
if (tok->strAt(1) == "(" && tok->tokAt(2)) {
2335+
// check if function has no arguments
2336+
if (tok->strAt(2) == ")" && (func->argCount() == 0 || func->argCount() == func->initializedArgCount()))
2337+
return func;
2338+
2339+
// check the arguments
2340+
unsigned int args = 0;
2341+
const Token *arg = tok->tokAt(2);
2342+
while (arg && arg->str() != ")") {
2343+
/** @todo check argument type for match */
2344+
/** @todo check for default arguments */
2345+
args++;
2346+
arg = arg->nextArgument();
2347+
}
2348+
2349+
if (args == func->argCount())
2350+
return func;
2351+
}
2352+
}
2353+
}
2354+
2355+
return 0;
2356+
}
2357+
2358+
//---------------------------------------------------------------------------
2359+
23252360
const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const
23262361
{
23272362
const Scope* currScope = startScope;
@@ -2332,29 +2367,9 @@ const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, cons
23322367
currScope = currScope->nestedIn;
23332368
}
23342369
while (currScope) {
2335-
for (std::list<Function>::const_iterator i = currScope->functionList.begin(); i != currScope->functionList.end(); ++i) {
2336-
if (i->tokenDef->str() == tok->str()) {
2337-
const Function *func = &*i;
2338-
if (tok->strAt(1) == "(" && tok->tokAt(2)) {
2339-
// check if function has no arguments
2340-
if (tok->strAt(2) == ")" && (func->argCount() == 0 || func->argCount() == func->initializedArgCount()))
2341-
return func;
2342-
2343-
// check the arguments
2344-
unsigned int args = 0;
2345-
const Token *arg = tok->tokAt(2);
2346-
while (arg && arg->str() != ")") {
2347-
/** @todo check argument type for match */
2348-
/** @todo check for default arguments */
2349-
args++;
2350-
arg = arg->nextArgument();
2351-
}
2352-
2353-
if (args == func->argCount())
2354-
return func;
2355-
}
2356-
}
2357-
}
2370+
const Function *func = findFunctionByNameAndArgsInScope(tok, currScope);
2371+
if (func)
2372+
return func;
23582373
currScope = currScope->nestedIn;
23592374
}
23602375
return 0;

lib/symboldatabase.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ class CPPCHECKLIB Function {
394394
retFuncPtr(false) {
395395
}
396396

397+
const std::string &name() const {
398+
return tokenDef->str();
399+
}
400+
397401
std::size_t argCount() const {
398402
return argumentList.size();
399403
}
@@ -605,6 +609,8 @@ class CPPCHECKLIB SymbolDatabase {
605609
*/
606610
const Function* findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const;
607611

612+
const Function* findFunctionByNameAndArgsInScope(const Token *tok, const Scope *scope) const;
613+
608614
const Scope* findScopeByName(const std::string& name) const;
609615

610616
const Scope *findScope(const Token *tok, const Scope *startScope) const;

test/testconstructors.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class TestConstructors : public TestFixture {
7373
TEST_CASE(initvar_constvar);
7474
TEST_CASE(initvar_staticvar);
7575
TEST_CASE(initvar_union);
76+
TEST_CASE(initvar_delegate); // ticket #4302
7677

7778
TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor
7879
TEST_CASE(initvar_copy_constructor); // ticket #1611
@@ -688,6 +689,43 @@ class TestConstructors : public TestFixture {
688689
}
689690

690691

692+
void initvar_delegate() {
693+
check("class A {\n"
694+
" int number;\n"
695+
"public:\n"
696+
" A(int n) { }\n"
697+
" A() : A(42) {}\n"
698+
"};");
699+
ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n"
700+
"[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str());
701+
702+
check("class A {\n"
703+
" int number;\n"
704+
"public:\n"
705+
" A(int n) { number = n; }\n"
706+
" A() : A(42) {}\n"
707+
"};");
708+
ASSERT_EQUALS("", errout.str());
709+
710+
check("class A {\n"
711+
" int number;\n"
712+
"public:\n"
713+
" A(int n) : A() { }\n"
714+
" A() {}\n"
715+
"};");
716+
ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n"
717+
"[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str());
718+
719+
check("class A {\n"
720+
" int number;\n"
721+
"public:\n"
722+
" A(int n) : A() { }\n"
723+
" A() { number = 42; }\n"
724+
"};");
725+
ASSERT_EQUALS("", errout.str());
726+
}
727+
728+
691729
void initvar_private_constructor() {
692730
check("class Fred\n"
693731
"{\n"
@@ -2465,7 +2503,7 @@ class TestConstructors : public TestFixture {
24652503
" void init(int value)\n"
24662504
" { }\n"
24672505
"};");
2468-
TODO_ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", "", errout.str());
2506+
ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", errout.str());
24692507
}
24702508

24712509
void uninitVarOperatorEqual() { // ticket #2415

0 commit comments

Comments
 (0)