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
clearAllVar(usage);
std::list<std::string> callstack;
std::list<const Function *> callstack;
initializeVarList(*func, callstack, &(*scope), usage);
// Check if any variables are uninitialized
@ -354,7 +354,7 @@ bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope)
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;
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) { }
if (initList) {
if (level == 0 && Token::Match(ftok, "%var% (")) {
if (ftok->str() != func.name()) {
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%")) {
initVar(ftok->str(), scope, usage);
ftok = ftok->linkAt(1);
@ -469,29 +496,22 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
// Calling member function?
else if (Token::simpleMatch(ftok, "operator= (") &&
ftok->previous()->str() != "::") {
// recursive call / calling overloaded function
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(), ftok->str()) != callstack.end()) {
if (std::find(callstack.begin(), callstack.end(), member) != 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
if (it != scope->functionList.end()) {
// member function has implementation
if (it->hasBody) {
if (member->hasBody) {
// initialize variable use list using member function
callstack.push_back(ftok->str());
initializeVarList(*it, callstack, scope, usage);
callstack.push_back(member);
initializeVarList(*member, callstack, scope, usage);
callstack.pop_back();
}
@ -517,27 +537,23 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
}
}
// recursive call / calling overloaded function
// check if member function
const Function *member = symbolDatabase->findFunctionByNameAndArgsInScope(ftok, scope);
// member function found
if (member && member->type != Function::eConstructor) {
// recursive call
// assume that all variables are initialized
if (std::find(callstack.begin(), callstack.end(), ftok->str()) != callstack.end()) {
if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) {
assignAllVar(usage);
return;
}
// check if member function
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
if (it != scope->functionList.end()) {
// member function has implementation
if (it->hasBody) {
if (member->hasBody) {
// initialize variable use list using member function
callstack.push_back(ftok->str());
initializeVarList(*it, callstack, scope, usage);
callstack.push_back(member);
initializeVarList(*member, callstack, scope, usage);
callstack.pop_back();
// Assume that variables that are passed to it are initialized..

View File

@ -244,7 +244,7 @@ private:
* @param scope pointer to variable Scope
* @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);
};

View File

@ -2299,6 +2299,8 @@ const Function *SymbolDatabase::findFunctionByToken(const Token *tok) const
return 0;
}
//---------------------------------------------------------------------------
const Function* SymbolDatabase::findFunctionByName(const std::string& str, const Scope* startScope) const
{
const Scope* currScope = startScope;
@ -2318,21 +2320,15 @@ const Function* SymbolDatabase::findFunctionByName(const std::string& str, const
return 0;
}
//---------------------------------------------------------------------------
/** @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 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::findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const
const Function* SymbolDatabase::findFunctionByNameAndArgsInScope(const Token *tok, const Scope *scope) const
{
const Scope* currScope = startScope;
while (currScope && currScope->isExecutable()) {
if (currScope->functionOf)
currScope = currScope->functionOf;
else
currScope = currScope->nestedIn;
}
while (currScope) {
for (std::list<Function>::const_iterator i = currScope->functionList.begin(); i != currScope->functionList.end(); ++i) {
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)) {
@ -2355,6 +2351,25 @@ const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, cons
}
}
}
return 0;
}
//---------------------------------------------------------------------------
const Function* SymbolDatabase::findFunctionByNameAndArgs(const Token *tok, const Scope *startScope) const
{
const Scope* currScope = startScope;
while (currScope && currScope->isExecutable()) {
if (currScope->functionOf)
currScope = currScope->functionOf;
else
currScope = currScope->nestedIn;
}
while (currScope) {
const Function *func = findFunctionByNameAndArgsInScope(tok, currScope);
if (func)
return func;
currScope = currScope->nestedIn;
}
return 0;

View File

@ -394,6 +394,10 @@ public:
retFuncPtr(false) {
}
const std::string &name() const {
return tokenDef->str();
}
std::size_t argCount() const {
return argumentList.size();
}
@ -605,6 +609,8 @@ public:
*/
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 *findScope(const Token *tok, const Scope *startScope) const;

View File

@ -73,6 +73,7 @@ private:
TEST_CASE(initvar_constvar);
TEST_CASE(initvar_staticvar);
TEST_CASE(initvar_union);
TEST_CASE(initvar_delegate); // ticket #4302
TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor
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() {
check("class Fred\n"
"{\n"
@ -2465,7 +2503,7 @@ private:
" void init(int value)\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