Fixed #3190 (SymbolDatabase: Parse of sub class constructor fails)

This commit is contained in:
Robert Reif 2012-11-30 06:03:58 +01:00 committed by Daniel Marjamäki
parent a219ed313b
commit 35c2b8058d
6 changed files with 203 additions and 48 deletions

View File

@ -2222,6 +2222,10 @@ void CheckMemoryLeakInFunction::check()
if (!var->isPointer() && var->typeStartToken()->str() != "int")
continue;
// check for known class without implementation (forward declaration)
if (var->isPointer() && var->type() && var->type()->isForwardDeclaration())
continue;
unsigned int sz = _tokenizer->sizeOfType(var->typeStartToken());
if (sz < 1)
sz = 1;

View File

@ -49,50 +49,83 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
// Store current access in each scope (depends on evaluation progress)
std::map<const Scope*, AccessControl> access;
std::map<const Token *, Scope *> back;
// find all scopes
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
// Locate next class
if (Token::Match(tok, "class|struct|union|namespace %var% [{:]")) {
scopeList.push_back(Scope(this, tok, scope));
Scope *new_scope = &scopeList.back();
if (tok->str() == "class")
access[new_scope] = Private;
else if (tok->str() == "struct")
access[new_scope] = Public;
if (Token::Match(tok, "class|struct|union|namespace ::| %var% {|:|::")) {
const Token *tok2 = tok->tokAt(2);
// only create base list for classes and structures
if (new_scope->isClassOrStruct()) {
// goto initial '{'
tok2 = new_scope->initBaseInfo(tok);
if (tok->strAt(1) == "::")
tok2 = tok2->next();
while (tok2 && tok2->str() == "::")
tok2 = tok2->tokAt(2);
// make sure we have valid code
if (!tok2 || !Token::Match(tok2, "{|:"))
break;
Scope *new_scope = findScope(tok->next(), scope);
if (new_scope) {
// only create base list for classes and structures
if (new_scope->isClassOrStruct()) {
// goto initial '{'
tok2 = new_scope->initBaseInfo(tok, tok2);
// make sure we have valid code
if (!tok2) {
break;
}
}
back[tok2->link()] = scope;
new_scope->classDef = tok;
new_scope->classStart = tok2;
new_scope->classEnd = tok2->link();
scope = new_scope;
tok = tok2;
} else {
scopeList.push_back(Scope(this, tok, scope));
new_scope = &scopeList.back();
if (tok->str() == "class")
access[new_scope] = Private;
else if (tok->str() == "struct")
access[new_scope] = Public;
// only create base list for classes and structures
if (new_scope->isClassOrStruct()) {
// goto initial '{'
tok2 = new_scope->initBaseInfo(tok, tok2);
// make sure we have valid code
if (!tok2) {
scopeList.pop_back();
break;
}
}
new_scope->classStart = tok2;
new_scope->classEnd = tok2->link();
// make sure we have valid code
if (!tok2) {
if (!new_scope->classEnd) {
scopeList.pop_back();
break;
}
// fill the classAndStructTypes set..
if (new_scope->isClassOrStruct())
classAndStructTypes.insert(new_scope->className);
// make the new scope the current scope
scope = &scopeList.back();
scope->nestedIn->nestedList.push_back(scope);
tok = tok2;
}
new_scope->classStart = tok2;
new_scope->classEnd = tok2->link();
// make sure we have valid code
if (!new_scope->classEnd) {
scopeList.pop_back();
break;
}
// fill the classAndStructTypes set..
if (new_scope->isClassOrStruct())
classAndStructTypes.insert(new_scope->className);
// make the new scope the current scope
scope = &scopeList.back();
scope->nestedIn->nestedList.push_back(scope);
tok = tok2;
}
// Namespace and unknown macro (#3854)
@ -124,13 +157,20 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
}
// forward declaration
else if (Token::Match(tok, "class|struct %var% ;")) {
// fill the classAndStructTypes set..
classAndStructTypes.insert(tok->next()->str());
else if (Token::Match(tok, "class|struct %var% ;") &&
tok->strAt(-1) != "friend") {
if (!findScope(tok->next(), scope)) {
// fill the classAndStructTypes set..
classAndStructTypes.insert(tok->next()->str());
/** @todo save forward declarations in database someday */
scopeList.push_back(Scope(this, tok, scope));
Scope *new_scope = &scopeList.back();
// add scope
scope->nestedList.push_back(new_scope);
}
tok = tok->tokAt(2);
continue;
}
// using namespace
@ -205,7 +245,11 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
else {
// check for end of scope
if (tok == scope->classEnd) {
scope = scope->nestedIn;
if (back.find(tok) != back.end()) {
scope = back[tok];
back.erase(tok);
} else
scope = scope->nestedIn;
continue;
}
@ -1065,7 +1109,8 @@ void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const To
return;
// back up to head of path
while (tok1 && tok1->previous() && tok1->previous()->str() == "::") {
while (tok1 && tok1->previous() && tok1->previous()->str() == "::" &&
tok1->tokAt(-2) && tok1->tokAt(-2)->isName()) {
path = tok1->str() + " :: " + path;
tok1 = tok1->tokAt(-2);
count++;
@ -1203,10 +1248,10 @@ void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok)
}
}
const Token *Scope::initBaseInfo(const Token *tok)
const Token *Scope::initBaseInfo(const Token *tok, const Token *tok1)
{
// goto initial '{'
const Token *tok2 = tok->tokAt(2);
const Token *tok2 = tok1;
while (tok2 && tok2->str() != "{") {
// skip unsupported templates
if (tok2->str() == "<")
@ -1542,7 +1587,7 @@ void SymbolDatabase::printOut(const char *title) const
count = scope->nestedList.size();
for (nsi = scope->nestedList.begin(); nsi != scope->nestedList.end(); ++nsi) {
std::cout << " " << &(*nsi) << " " << (*nsi)->type << " " << (*nsi)->className;
std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className;
if (count-- > 1)
std::cout << ",";
}
@ -2233,6 +2278,19 @@ Scope * Scope::findInNestedList(const std::string & name)
//---------------------------------------------------------------------------
Scope * Scope::findRecordInNestedList(const std::string & name)
{
std::list<Scope *>::const_iterator it;
for (it = nestedList.begin(); it != nestedList.end(); ++it) {
if ((*it)->className == name && (*it)->type != eFunction)
return (*it);
}
return 0;
}
//---------------------------------------------------------------------------
Scope * Scope::findInNestedListRecursive(const std::string & name)
{
std::list<Scope *>::iterator it;
@ -2306,3 +2364,55 @@ bool SymbolDatabase::isCPP() const
{
return _tokenizer->isCPP();
}
//---------------------------------------------------------------------------
const Scope * SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const
{
return findScope(tok, startScope);
}
Scope * SymbolDatabase::findScope(const Token *tok, Scope *startScope)
{
// absolute path
if (tok->str() == "::") {
tok = tok->next();
Scope *scope = &scopeList.front();
while (scope && tok && tok->isName()) {
scope = scope->findRecordInNestedList(tok->str());
if (scope) {
if (tok->strAt(1) == "::")
tok = tok->tokAt(2);
else
break;
}
}
return scope;
}
// relative path
else if (tok->isName()) {
Scope *scope = startScope;
while (scope && tok && tok->isName()) {
scope = scope->findRecordInNestedList(tok->str());
if (scope) {
if (tok->strAt(1) == "::")
tok = tok->tokAt(2);
else
break;
}
}
return scope;
}
// not a valid path
else
return 0;
}

View File

@ -490,12 +490,18 @@ public:
type == eTry || type == eCatch);
}
bool isForwardDeclaration() const {
return isClassOrStruct() && classStart == NULL;
}
/**
* @brief find if name is in nested list
* @param name name of nested scope
*/
Scope * findInNestedList(const std::string & name);
Scope * findRecordInNestedList(const std::string & name);
/**
* @brief find if name is in nested list
* @param name name of nested scope
@ -512,7 +518,7 @@ public:
type_, scope_));
}
const Token *initBaseInfo(const Token *tok);
const Token *initBaseInfo(const Token *tok, const Token *tok1);
/** @brief initialize varlist */
void getVariableList();
@ -594,6 +600,8 @@ public:
const Scope* findScopeByName(const std::string& name) const;
const Scope *findScope(const Token *tok, const Scope *startScope) const;
bool isClassOrStruct(const std::string &type) const {
return bool(classAndStructTypes.find(type) != classAndStructTypes.end());
}
@ -627,6 +635,8 @@ private:
void addNewFunction(Scope **info, const Token **tok);
static bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart);
Scope *findScope(const Token *tok, Scope *startScope);
/** class/struct types */
std::set<std::string> classAndStructTypes;

View File

@ -108,6 +108,7 @@ private:
TEST_CASE(uninitVar21); // ticket #2947
TEST_CASE(uninitVar22); // ticket #3043
TEST_CASE(uninitVar23); // ticket #3702
TEST_CASE(uninitVar24); // ticket #3190
TEST_CASE(uninitVarEnum);
TEST_CASE(uninitVarStream);
TEST_CASE(uninitVarTypedef);
@ -1585,6 +1586,36 @@ private:
"[test.cpp:14]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n", errout.str());
}
void uninitVar24() { // ticket #3190
check("class Foo;\n"
"class Bar;\n"
"class Sub;\n"
"class Foo { class Sub; };\n"
"class Bar { class Sub; };\n"
"class Bar::Sub {\n"
" int b;\n"
"public:\n"
" Sub() { }\n"
" Sub(int);\n"
"};\n"
"Bar::Sub::Sub(int) { };\n"
"class ::Foo::Sub {\n"
" int f;\n"
"public:\n"
" ~Sub();\n"
" Sub();\n"
"};\n"
"::Foo::Sub::~Sub() { }\n"
"::Foo::Sub::Sub() { }\n"
"class Foo;\n"
"class Bar;\n"
"class Sub;\n");
ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n"
"[test.cpp:9]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n"
"[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n", errout.str());
}
void uninitVarArray1() {
check("class John\n"
"{\n"

View File

@ -637,9 +637,9 @@ private:
" printf(\"%f\", f);\n"
" printf(\"%p\", f);\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an integer given in the argument list.\n"
"[test.cpp:4]: (warning) %f in format string (no. 1) requires an integer given in the argument list.\n"
"[test.cpp:5]: (warning) %p in format string (no. 1) requires an integer given in the argument list.\n", "", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires an unsigned integer given in the argument list.\n"
"[test.cpp:4]: (warning) %f in format string (no. 1) requires a floating point number given in the argument list.\n"
"[test.cpp:5]: (warning) %p in format string (no. 1) requires an address given in the argument list.\n", errout.str());
// Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7)
// False positive tests

View File

@ -675,7 +675,7 @@ private:
seen_something = true;
}
}
TODO_ASSERT_EQUALS("works", "doesn't work", seen_something ? "works" : "doesn't work");
ASSERT_EQUALS(true, seen_something);
}
}