Fixed #4302 (Member variable not initialized in public delegate constructor)

This commit is contained in:
Robert Reif 2013-01-01 09:53:40 +01:00 committed by Daniel Marjamäki
parent 62b05193c4
commit d2e8ab9c86
5 changed files with 140 additions and 65 deletions

View File

@ -105,7 +105,7 @@ void CheckClass::constructors()
// Mark all variables not used // Mark all variables not used
clearAllVar(usage); clearAllVar(usage);
std::list<std::string> callstack; std::list<const Function *> callstack;
initializeVarList(*func, callstack, &(*scope), usage); initializeVarList(*func, callstack, &(*scope), usage);
// Check if any variables are uninitialized // Check if any variables are uninitialized
@ -354,7 +354,7 @@ bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope)
return false; return false;
} }
void CheckClass::initializeVarList(const Function &func, std::list<std::string> &callstack, const Scope *scope, std::vector<Usage> &usage) void CheckClass::initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage)
{ {
bool initList = func.type == Function::eConstructor || func.type == Function::eCopyConstructor; bool initList = func.type == Function::eConstructor || func.type == Function::eCopyConstructor;
const Token *ftok = func.arg->link()->next(); const Token *ftok = func.arg->link()->next();
@ -364,8 +364,35 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
// clKalle::clKalle() : var(value) { } // clKalle::clKalle() : var(value) { }
if (initList) { if (initList) {
if (level == 0 && Token::Match(ftok, "%var% (")) { if (level == 0 && Token::Match(ftok, "%var% (")) {
if (ftok->strAt(2) != ")") if (ftok->str() != func.name()) {
initVar(ftok->str(), scope, usage); if (ftok->strAt(2) != ")")
initVar(ftok->str(), scope, usage);
} else { // c++11 delegate constructor
const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
// member function found
if (member) {
// recursive call
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
/** @todo false negative: just bail */
assignAllVar(usage);
return;
}
// member function has implementation
if (member->hasBody) {
// initialize variable use list using member function
callstack.push_back(member);
initializeVarList(*member, callstack, scope, usage);
callstack.pop_back();
}
// there is a called member function, but it has no implementation, so we assume it initializes everything
else {
assignAllVar(usage);
}
}
}
} else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) { } else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) {
initVar(ftok->str(), scope, usage); initVar(ftok->str(), scope, usage);
ftok = ftok->linkAt(1); ftok = ftok->linkAt(1);
@ -469,29 +496,22 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
// Calling member function? // Calling member function?
else if (Token::simpleMatch(ftok, "operator= (") && else if (Token::simpleMatch(ftok, "operator= (") &&
ftok->previous()->str() != "::") { ftok->previous()->str() != "::") {
// recursive call / calling overloaded function const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end()) {
/** @todo false negative: just bail */
assignAllVar(usage);
return;
}
/** @todo check function parameters for overloaded function so we check the right one */
// check if member function exists
std::list<Function>::const_iterator it;
for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it) {
if (ftok->str() == it->tokenDef->str() && it->type != Function::eConstructor)
break;
}
// member function found // member function found
if (it != scope->functionList.end()) { if (member) {
// recursive call
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
/** @todo false negative: just bail */
assignAllVar(usage);
return;
}
// member function has implementation // member function has implementation
if (it->hasBody) { if (member->hasBody) {
// initialize variable use list using member function // initialize variable use list using member function
callstack.push_back(ftok->str()); callstack.push_back(member);
initializeVarList(*it, callstack, scope, usage); initializeVarList(*member, callstack, scope, usage);
callstack.pop_back(); callstack.pop_back();
} }
@ -517,27 +537,23 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
} }
} }
// recursive call / calling overloaded function
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end()) {
assignAllVar(usage);
return;
}
// check if member function // check if member function
std::list<Function>::const_iterator it; const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it) {
if (ftok->str() == it->tokenDef->str() && it->type != Function::eConstructor)
break;
}
// member function found // member function found
if (it != scope->functionList.end()) { if (member && member->type != Function::eConstructor) {
// recursive call
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
assignAllVar(usage);
return;
}
// member function has implementation // member function has implementation
if (it->hasBody) { if (member->hasBody) {
// initialize variable use list using member function // initialize variable use list using member function
callstack.push_back(ftok->str()); callstack.push_back(member);
initializeVarList(*it, callstack, scope, usage); initializeVarList(*member, callstack, scope, usage);
callstack.pop_back(); callstack.pop_back();
// Assume that variables that are passed to it are initialized.. // Assume that variables that are passed to it are initialized..

View File

@ -244,7 +244,7 @@ private:
* @param scope pointer to variable Scope * @param scope pointer to variable Scope
* @param usage reference to usage vector * @param usage reference to usage vector
*/ */
void initializeVarList(const Function &func, std::list<std::string> &callstack, const Scope *scope, std::vector<Usage> &usage); void initializeVarList(const Function &func, std::list<const Function *> &callstack, const Scope *scope, std::vector<Usage> &usage);
static bool canNotCopy(const Scope *scope); static bool canNotCopy(const Scope *scope);
}; };

View File

@ -2299,6 +2299,8 @@ const Function *SymbolDatabase::findFunctionByToken(const Token *tok) const
return 0; return 0;
} }
//---------------------------------------------------------------------------
const Function* SymbolDatabase::findFunctionByName(const std::string& str, const Scope* startScope) const const Function* SymbolDatabase::findFunctionByName(const std::string& str, const Scope* startScope) const
{ {
const Scope* currScope = startScope; const Scope* currScope = startScope;
@ -2318,10 +2320,43 @@ const Function* SymbolDatabase::findFunctionByName(const std::string& str, const
return 0; return 0;
} }
//---------------------------------------------------------------------------
/** @todo This function only counts the number of arguments in the function call. /** @todo This function only counts the number of arguments in the function call.
It does not take into account functions with default arguments. It does not take into account functions with default arguments.
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. 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.
*/ */
const Function* SymbolDatabase::findFunctionByNameAndArgsInScope(const Token *tok, const Scope *scope) const
{
for (std::list<Function>::const_iterator i = scope->functionList.begin(); i != scope->functionList.end(); ++i) {
if (i->tokenDef->str() == tok->str()) {
const Function *func = &*i;
if (tok->strAt(1) == "(" && tok->tokAt(2)) {
// check if function has no arguments
if (tok->strAt(2) == ")" && (func->argCount() == 0 || func->argCount() == func->initializedArgCount()))
return func;
// check the arguments
unsigned int args = 0;
const Token *arg = tok->tokAt(2);
while (arg && arg->str() != ")") {
/** @todo check argument type for match */
/** @todo check for default arguments */
args++;
arg = arg->nextArgument();
}
if (args == func->argCount())
return func;
}
}
}
return 0;
}
//---------------------------------------------------------------------------
const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const
{ {
const Scope* currScope = startScope; const Scope* currScope = startScope;
@ -2332,29 +2367,9 @@ const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, cons
currScope = currScope->nestedIn; currScope = currScope->nestedIn;
} }
while (currScope) { while (currScope) {
for (std::list<Function>::const_iterator i = currScope->functionList.begin(); i != currScope->functionList.end(); ++i) { const Function *func = findFunctionByNameAndArgsInScope(tok, currScope);
if (i->tokenDef->str() == tok->str()) { if (func)
const Function *func = &*i; return func;
if (tok->strAt(1) == "(" && tok->tokAt(2)) {
// check if function has no arguments
if (tok->strAt(2) == ")" && (func->argCount() == 0 || func->argCount() == func->initializedArgCount()))
return func;
// check the arguments
unsigned int args = 0;
const Token *arg = tok->tokAt(2);
while (arg && arg->str() != ")") {
/** @todo check argument type for match */
/** @todo check for default arguments */
args++;
arg = arg->nextArgument();
}
if (args == func->argCount())
return func;
}
}
}
currScope = currScope->nestedIn; currScope = currScope->nestedIn;
} }
return 0; return 0;

View File

@ -394,6 +394,10 @@ public:
retFuncPtr(false) { retFuncPtr(false) {
} }
const std::string &name() const {
return tokenDef->str();
}
std::size_t argCount() const { std::size_t argCount() const {
return argumentList.size(); return argumentList.size();
} }
@ -605,6 +609,8 @@ public:
*/ */
const Function* findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const; const Function* findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const;
const Function* findFunctionByNameAndArgsInScope(const Token *tok, const Scope *scope) const;
const Scope* findScopeByName(const std::string& name) const; const Scope* findScopeByName(const std::string& name) const;
const Scope *findScope(const Token *tok, const Scope *startScope) const; const Scope *findScope(const Token *tok, const Scope *startScope) const;

View File

@ -73,6 +73,7 @@ private:
TEST_CASE(initvar_constvar); TEST_CASE(initvar_constvar);
TEST_CASE(initvar_staticvar); TEST_CASE(initvar_staticvar);
TEST_CASE(initvar_union); TEST_CASE(initvar_union);
TEST_CASE(initvar_delegate); // ticket #4302
TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor
TEST_CASE(initvar_copy_constructor); // ticket #1611 TEST_CASE(initvar_copy_constructor); // ticket #1611
@ -688,6 +689,43 @@ private:
} }
void initvar_delegate() {
check("class A {\n"
" int number;\n"
"public:\n"
" A(int n) { }\n"
" A() : A(42) {}\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n"
"[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str());
check("class A {\n"
" int number;\n"
"public:\n"
" A(int n) { number = n; }\n"
" A() : A(42) {}\n"
"};");
ASSERT_EQUALS("", errout.str());
check("class A {\n"
" int number;\n"
"public:\n"
" A(int n) : A() { }\n"
" A() {}\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n"
"[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str());
check("class A {\n"
" int number;\n"
"public:\n"
" A(int n) : A() { }\n"
" A() { number = 42; }\n"
"};");
ASSERT_EQUALS("", errout.str());
}
void initvar_private_constructor() { void initvar_private_constructor() {
check("class Fred\n" check("class Fred\n"
"{\n" "{\n"
@ -2465,7 +2503,7 @@ private:
" void init(int value)\n" " void init(int value)\n"
" { }\n" " { }\n"
"};"); "};");
TODO_ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", "", errout.str()); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", errout.str());
} }
void uninitVarOperatorEqual() { // ticket #2415 void uninitVarOperatorEqual() { // ticket #2415