Fixed #2172 (False positive: struct is not initialized in constructor)

This commit is contained in:
Robert Reif 2010-12-02 07:35:01 +01:00 committed by Daniel Marjamäki
parent b4be71aa4e
commit 1bc8a2b6ba
5 changed files with 339 additions and 33 deletions

View File

@ -122,16 +122,25 @@ void CheckClass::constructors()
std::list<SymbolDatabase::Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{
// skip classes for regular constructor
if (var->isClass && func->type == SymbolDatabase::Func::Constructor)
continue;
if (var->assign || var->init || var->isStatic)
continue;
if (var->isConst && var->token->previous()->str() != "*")
continue;
// Check if this is a class constructor
if (var->isClass && func->type == SymbolDatabase::Func::Constructor)
{
// Unknown type so assume it is initialized
if (!var->type)
continue;
// Known type that doesn't need initialization or
// known type that has member variables of an unknown type
else if (var->type->needInitialization != SymbolDatabase::SpaceInfo::True)
continue;
}
// It's non-static and it's not initialized => error
if (func->type == SymbolDatabase::Func::OperatorEqual)
{

View File

@ -310,12 +310,13 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
std::list<SpaceInfo *>::iterator it;
// fill in base class info
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
// skip namespaces and functions
if (info->type == SpaceInfo::Namespace || info->type == SpaceInfo::Function)
if (!info->isClassOrStruct())
continue;
// finish filling in base class info
@ -346,9 +347,127 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
}
}
}
}
// find variables
info->getVarList();
// fill in variable info
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
// skip functions
if (info->type != SpaceInfo::Function)
{
// find variables
info->getVarList();
}
}
// determine if user defined type needs initialization
unsigned int unknowns = 0; // stop checking when there are no unknowns
unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason
do
{
unknowns = 0;
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
if (info->isClassOrStruct() && info->needInitialization == SpaceInfo::Unknown)
{
// check for default constructor
bool hasDefaultConstructor = false;
std::list<SymbolDatabase::Func>::const_iterator func;
for (func = info->functionList.begin(); func != info->functionList.end(); ++func)
{
if (func->type == SymbolDatabase::Func::Constructor)
{
// check for no arguments: func ( )
/** @todo check for arguents with default values someday */
if (func->argDef->next() == func->argDef->link())
{
hasDefaultConstructor = true;
break;
}
}
}
// User defined types with user defined defaut constructor doesn't need initialization.
// We assume the default constructor initializes everything.
// Another check will figure out if the constructor actually initializes everything.
if (hasDefaultConstructor)
info->needInitialization = SpaceInfo::False;
// check each member variable to see if it needs initialization
else
{
bool needInitialization = false;
bool unknown = false;
std::list<Var>::const_iterator var;
for (var = info->varlist.begin(); var != info->varlist.end(); ++var)
{
if (var->isClass)
{
if (var->type)
{
// does this type need initialization?
if (var->type->needInitialization == SpaceInfo::True)
needInitialization = true;
else if (var->type->needInitialization == SpaceInfo::Unknown)
unknown = true;
}
}
else
needInitialization = true;
}
if (!unknown)
{
if (needInitialization)
info->needInitialization = SpaceInfo::True;
else
info->needInitialization = SpaceInfo::False;
}
if (info->needInitialization == SpaceInfo::Unknown)
unknowns++;
}
}
}
retry++;
}
while (unknowns && retry < 100);
// this shouldn't happen so output a debug warning
if (retry == 100 && _settings->debugwarnings)
{
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
info = *it;
if (info->isClassOrStruct() && info->needInitialization == SpaceInfo::Unknown)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = info->classDef->linenr();
loc.setfile(_tokenizer->file(info->classDef));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"SymbolDatabase::SymbolDatabase couldn't resolve all user defined types.",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
}
}
}
@ -745,7 +864,8 @@ SymbolDatabase::SpaceInfo::SpaceInfo(SymbolDatabase *check_, const Token *classD
classStart(NULL),
classEnd(NULL),
nestedIn(nestedIn_),
numConstructors(0)
numConstructors(0),
needInitialization(SpaceInfo::Unknown)
{
if (!classDef)
{
@ -874,9 +994,12 @@ void SymbolDatabase::SpaceInfo::getVarList()
// Search for start of statement..
else if (!tok->previous() || !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:"))
continue;
else if (Token::Match(tok, ";|{|}"))
continue;
// This is the start of a statement
const Token *vartok = 0;
const Token *vartok = NULL;
const Token *typetok = NULL;
// Is it const..?
bool isConst = false;
@ -913,7 +1036,10 @@ void SymbolDatabase::SpaceInfo::getVarList()
if (Token::Match(tok, "%type% %var% ;|:"))
{
if (!tok->isStandardType())
{
isClass = true;
typetok = tok;
}
vartok = tok->next();
tok = vartok->next();
@ -922,31 +1048,37 @@ void SymbolDatabase::SpaceInfo::getVarList()
{
isClass = true;
vartok = tok->tokAt(3);
typetok = vartok->previous();
tok = vartok->next();
}
else if (Token::Match(tok, ":: %type% :: %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(4);
typetok = vartok->previous();
tok = vartok->next();
}
else if (Token::Match(tok, "%type% :: %type% :: %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(5);
typetok = vartok->previous();
tok = vartok->next();
}
else if (Token::Match(tok, ":: %type% :: %type% :: %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(6);
typetok = vartok->previous();
tok = vartok->next();
}
// Structure?
else if (Token::Match(tok, "struct|union %type% %var% ;"))
{
isClass = true;
vartok = tok->tokAt(2);
typetok = vartok->previous();
tok = vartok->next();
}
@ -976,7 +1108,10 @@ void SymbolDatabase::SpaceInfo::getVarList()
else if (Token::Match(tok, "%type% %var% [") && tok->next()->str() != "operator")
{
if (!tok->isStandardType())
{
isClass = true;
typetok = tok;
}
vartok = tok->next();
tok = vartok->next()->link()->next();
@ -1013,10 +1148,15 @@ void SymbolDatabase::SpaceInfo::getVarList()
// find matching ">"
int level = 0;
const Token *tok1 = NULL;
for (; tok; tok = tok->next())
{
if (tok->str() == "<")
{
if (level == 0)
tok1 = tok->previous();
level++;
}
else if (tok->str() == ">")
{
level--;
@ -1040,12 +1180,14 @@ void SymbolDatabase::SpaceInfo::getVarList()
{
isClass = true;
vartok = tok->next();
typetok = tok1;
tok = vartok->next();
}
else if (tok && (Token::Match(tok, "> :: %type% %var% ;") || Token::Match(tok, ">> :: %type% %var% ;")))
{
isClass = true;
vartok = tok->tokAt(3);
typetok = vartok->previous();
tok = vartok->next();
}
else if (tok && (Token::Match(tok, "> * %var% ;") || Token::Match(tok, ">> * %var% ;")))
@ -1076,13 +1218,60 @@ void SymbolDatabase::SpaceInfo::getVarList()
Check::reportError(errmsg);
}
addVar(vartok, varaccess, isMutable, isStatic, isConst, isClass);
const SpaceInfo *spaceInfo = NULL;
if (isClass)
spaceInfo = check->findVarType(this, typetok);
addVar(vartok, varaccess, isMutable, isStatic, isConst, isClass, spaceInfo);
}
}
}
//---------------------------------------------------------------------------
const SymbolDatabase::SpaceInfo *SymbolDatabase::findVarType(const SpaceInfo *start, const Token *type) const
{
std::list<SpaceInfo *>::const_iterator it;
for (it = spaceInfoList.begin(); it != spaceInfoList.end(); ++it)
{
const SpaceInfo *info = *it;
// skip namespaces and functions
if (info->type == SpaceInfo::Namespace || info->type == SpaceInfo::Function || info->type == SpaceInfo::Global)
continue;
// do the names match?
if (info->className == type->str())
{
// check if type does not have a namespace
if (type->previous()->str() != "::")
{
const SpaceInfo *parent = start;
// check if in same namespace
while (parent && parent != info->nestedIn)
parent = parent->nestedIn;
if (info->nestedIn == parent)
return info;
}
// type has a namespace
else
{
// FIXME check if namespace path matches supplied path
return info;
}
}
}
return NULL;
}
//---------------------------------------------------------------------------
SymbolDatabase::SpaceInfo * SymbolDatabase::SpaceInfo::findInNestedList(const std::string & name)
{
std::list<SpaceInfo *>::iterator it;

View File

@ -41,11 +41,13 @@ public:
SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger);
~SymbolDatabase();
class SpaceInfo;
/** @brief Information about a member variable. Used when checking for uninitialized variables */
class Var
{
public:
Var(const Token *token_, unsigned int index_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_)
Var(const Token *token_, unsigned int index_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const SpaceInfo *type_)
: token(token_),
index(index_),
assign(false),
@ -54,7 +56,8 @@ public:
isMutable(mutable_),
isStatic(static_),
isConst(const_),
isClass(class_)
isClass(class_),
type(type_)
{
}
@ -84,6 +87,9 @@ public:
/** @brief is this variable a class (or unknown type)? */
bool isClass;
/** @brief pointer to user defined type info (for known types) */
const SpaceInfo *type;
};
class Func
@ -129,8 +135,6 @@ public:
Type type; // constructor, destructor, ...
};
class SpaceInfo;
struct BaseInfo
{
AccessControl access; // public/protected/private
@ -148,6 +152,7 @@ public:
{
public:
enum SpaceType { Global, Class, Struct, Union, Namespace, Function };
enum NeedInitialization { Unknown, True, False };
SpaceInfo(SymbolDatabase *check_, const Token *classDef_, SpaceInfo *nestedIn_);
@ -165,6 +170,7 @@ public:
std::list<SpaceInfo *> nestedList;
AccessControl access;
unsigned int numConstructors;
NeedInitialization needInitialization;
bool isClassOrStruct() const
{
@ -189,9 +195,9 @@ public:
*/
void initVar(const std::string &varname);
void addVar(const Token *token_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_)
void addVar(const Token *token_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const SpaceInfo *type_)
{
varlist.push_back(Var(token_, varlist.size(), access_, mutable_, static_, const_, class_));
varlist.push_back(Var(token_, varlist.size(), access_, mutable_, static_, const_, class_, type_));
}
/**
@ -246,6 +252,8 @@ private:
bool isFunction(const Token *tok, const Token **funcStart, const Token **argStart) const;
bool argsMatch(const Token *first, const Token *second, const std::string &path, unsigned int depth) const;
const SpaceInfo *findVarType(const SpaceInfo *start, const Token *type) const;
const Tokenizer *_tokenizer;
const Settings *_settings;
ErrorLogger *_errorLogger;

View File

@ -69,6 +69,8 @@ private:
TEST_CASE(uninitVarArray5);
TEST_CASE(uninitVarArray6);
TEST_CASE(uninitVarArray2D);
TEST_CASE(uninitVarStruct1); // ticket #2172
TEST_CASE(uninitVarStruct2); // ticket #838
TEST_CASE(uninitMissingFuncDef); // can't expand function in constructor
TEST_CASE(privateCtor1); // If constructor is private..
TEST_CASE(privateCtor2); // If constructor is private..
@ -1594,8 +1596,7 @@ private:
" };\n"
" Bar bars[2];\n"
"};\n");
TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable not initialized in the constructor 'Foo::bars'\n", errout.str());
ASSERT_EQUALS("", errout.str()); // So we notice if something is reported.
ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable not initialized in the constructor 'Foo::bars'\n", errout.str());
}
void uninitVar4()
@ -2083,6 +2084,102 @@ private:
ASSERT_EQUALS("", errout.str());
}
void uninitVarStruct1() // ticket #2172
{
checkUninitVar("class A\n"
"{\n"
"private:\n"
" struct B {\n"
" std::string str1;\n"
" std::string str2;\n"
" }\n"
" struct B b;\n"
"public:\n"
" A() {\n"
" }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
checkUninitVar("class A\n"
"{\n"
"private:\n"
" struct B {\n"
" char *str1;\n"
" char *str2;\n"
" }\n"
" struct B b;\n"
"public:\n"
" A() {\n"
" }\n"
"};\n");
ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable not initialized in the constructor 'A::b'\n", errout.str());
checkUninitVar("class A\n"
"{\n"
"private:\n"
" struct B {\n"
" char *str1;\n"
" char *str2;\n"
" B() : str1(NULL), str2(NULL) { }\n"
" }\n"
" struct B b;\n"
"public:\n"
" A() {\n"
" }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
}
void uninitVarStruct2() // ticket #838
{
checkUninitVar("struct POINT\n"
"{\n"
" int x;\n"
" int y;\n"
"};\n"
"class Fred\n"
"{\n"
"private:\n"
" POINT p;\n"
"public:\n"
" Fred()\n"
" { }\n"
"};\n");
ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable not initialized in the constructor 'Fred::p'\n", errout.str());
checkUninitVar("struct POINT\n"
"{\n"
" int x;\n"
" int y;\n"
" POINT();\n"
"};\n"
"class Fred\n"
"{\n"
"private:\n"
" POINT p;\n"
"public:\n"
" Fred()\n"
" { }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
checkUninitVar("struct POINT\n"
"{\n"
" int x;\n"
" int y;\n"
" POINT() :x(0), y(0) { }\n"
"};\n"
"class Fred\n"
"{\n"
"private:\n"
" POINT p;\n"
"public:\n"
" Fred()\n"
" { }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
}
void uninitMissingFuncDef()
{
// Unknown member function

View File

@ -820,12 +820,12 @@ private:
"public:\n"
" A();\n"
" struct B {\n"
" B();\n"
" B(int x);\n"
" struct C {\n"
" C();\n"
" C(int y);\n"
" struct D {\n"
" int d;\n"
" D();\n"
" D(int z);\n"
" };\n"
" int c;\n"
" };\n"
@ -836,10 +836,11 @@ private:
" B b;\n"
"};\n"
"A::A(){}\n"
"A::B::B(){}\n"
"A::B::C::C(){}\n"
"A::B::C::D::D(){}\n");
"A::B::B(int x){}\n"
"A::B::C::C(int y){}\n"
"A::B::C::D::D(int z){}\n");
ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable not initialized in the constructor 'A::a'\n"
"[test.cpp:20]: (warning) Member variable not initialized in the constructor 'A::b'\n"
"[test.cpp:21]: (warning) Member variable not initialized in the constructor 'B::b'\n"
"[test.cpp:22]: (warning) Member variable not initialized in the constructor 'C::c'\n"
"[test.cpp:23]: (warning) Member variable not initialized in the constructor 'D::d'\n", errout.str());
@ -848,9 +849,9 @@ private:
"public:\n"
" A();\n"
" struct B {\n"
" B();\n"
" B(int x);\n"
" struct C {\n"
" C();\n"
" C(int y);\n"
" struct D {\n"
" D(const D &);\n"
" int d;\n"
@ -864,10 +865,11 @@ private:
" B b;\n"
"};\n"
"A::A(){}\n"
"A::B::B(){}\n"
"A::B::C::C(){}\n"
"A::B::B(int x){}\n"
"A::B::C::C(int y){}\n"
"A::B::C::D::D(const A::B::C::D & d){}\n");
ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable not initialized in the constructor 'A::a'\n"
"[test.cpp:20]: (warning) Member variable not initialized in the constructor 'A::b'\n"
"[test.cpp:21]: (warning) Member variable not initialized in the constructor 'B::b'\n"
"[test.cpp:22]: (warning) Member variable not initialized in the constructor 'C::c'\n"
"[test.cpp:23]: (warning) Member variable not initialized in the constructor 'D::d'\n", errout.str());
@ -876,11 +878,11 @@ private:
"public:\n"
" A();\n"
" struct B {\n"
" B();\n"
" B(int x);\n"
" struct C {\n"
" C();\n"
" C(int y);\n"
" struct D {\n"
" struct E { };\n"
" struct E { int e; };\n"
" struct E d;\n"
" D(const E &);\n"
" };\n"
@ -893,10 +895,11 @@ private:
" B b;\n"
"};\n"
"A::A(){}\n"
"A::B::B(){}\n"
"A::B::C::C(){}\n"
"A::B::B(int x){}\n"
"A::B::C::C(int y){}\n"
"A::B::C::D::D(const A::B::C::D::E & e){}\n");
ASSERT_EQUALS("[test.cpp:21]: (warning) Member variable not initialized in the constructor 'A::a'\n"
"[test.cpp:21]: (warning) Member variable not initialized in the constructor 'A::b'\n"
"[test.cpp:22]: (warning) Member variable not initialized in the constructor 'B::b'\n"
"[test.cpp:23]: (warning) Member variable not initialized in the constructor 'C::c'\n"
"[test.cpp:24]: (warning) Member variable not initialized in the constructor 'D::d'\n", errout.str());