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

This commit is contained in:
Robert Reif 2012-10-16 06:11:28 +02:00 committed by Daniel Marjamäki
parent 562291477d
commit 1ee980184e
6 changed files with 169 additions and 18 deletions

View File

@ -2220,6 +2220,10 @@ void CheckMemoryLeakInFunction::check()
if (!var->isPointer() && var->typeStartToken()->str() != "int") if (!var->isPointer() && var->typeStartToken()->str() != "int")
continue; 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()); unsigned int sz = _tokenizer->sizeOfType(var->typeStartToken());
if (sz < 1) if (sz < 1)
sz = 1; sz = 1;

View File

@ -52,17 +52,33 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
// find all scopes // find all scopes
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
// Locate next class // Locate next class
if (Token::Match(tok, "class|struct|union|namespace %var% [{:]")) { if (Token::Match(tok, "class|struct|union|namespace ::| %var% {|:|::")) {
scopeList.push_back(Scope(this, tok, scope)); const Token *tok2;
std::list<std::string> names;
Scope *new_scope = &scopeList.back(); if (tok->strAt(1) == "::") {
names.push_front(tok->strAt(2));
tok2 = tok->tokAt(3);
} else {
tok2 = tok->tokAt(2);
names.push_front(tok->strAt(1));
}
while (tok2 && tok2->str() == "::") {
names.push_front(tok2->strAt(1));
tok2 = tok2->tokAt(2);
}
// make sure we have valid code
if (!tok2)
break;
Scope *new_scope = getScope(names, tok, scope);
if (tok->str() == "class") if (tok->str() == "class")
access[new_scope] = Private; access[new_scope] = Private;
else if (tok->str() == "struct") else if (tok->str() == "struct")
access[new_scope] = Public; access[new_scope] = Public;
const Token *tok2 = tok->tokAt(2);
// only create base list for classes and structures // only create base list for classes and structures
if (new_scope->isClassOrStruct()) { if (new_scope->isClassOrStruct()) {
// goto initial '{' // goto initial '{'
@ -89,8 +105,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
classAndStructTypes.insert(new_scope->className); classAndStructTypes.insert(new_scope->className);
// make the new scope the current scope // make the new scope the current scope
scope = &scopeList.back(); scope = applyScope(new_scope);
scope->nestedIn->nestedList.push_back(scope);
tok = tok2; tok = tok2;
} }
@ -124,13 +139,30 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
} }
// forward declaration // forward declaration
else if (Token::Match(tok, "class|struct %var% ;")) { else if (Token::Match(tok, "class|struct %var% ;") &&
tok->strAt(-1) != "friend") {
// fill the classAndStructTypes set.. // fill the classAndStructTypes set..
classAndStructTypes.insert(tok->next()->str()); classAndStructTypes.insert(tok->next()->str());
/** @todo save forward declarations in database someday */ 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;
// make the new scope the current scope
scope = &scopeList.back();
scope->nestedIn->nestedList.push_back(scope);
// save the forward declaration
_forwardDecls.push_back(scope);
// leave the scope
scope = scope->nestedIn;
tok = tok->tokAt(2); tok = tok->tokAt(2);
continue;
} }
// using namespace // using namespace
@ -205,7 +237,11 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
else { else {
// check for end of scope // check for end of scope
if (tok == scope->classEnd) { if (tok == scope->classEnd) {
scope = scope->nestedIn; if (!_back.empty() && _back.top().forward == scope) {
scope = _back.top().back;
_back.pop();
} else
scope = scope->nestedIn;
continue; continue;
} }
@ -817,6 +853,68 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
} }
} }
Scope * SymbolDatabase::getScope(const std::list<std::string> &names, const Token *tok, Scope *scope)
{
// check for forward declarations
if (_forwardDecls.empty()) {
// none so just return end of scope list
scopeList.push_back(Scope(this, tok, scope));
return &scopeList.back();
}
// got forward declarations so check for possible match
std::list<Scope *>::iterator decls;
for (decls = _forwardDecls.begin(); decls != _forwardDecls.end(); ++decls) {
Scope * decl = *decls;
Scope * next = decl;
std::list<std::string>::const_iterator it;
for (it = names.begin(); next && it != names.end(); ++it) {
if (next->className == *it)
next = next->nestedIn;
else
break;
}
// fully qualified name match
if (it == names.end()) {
_back.push(Back(decl, scope));
decl->classDef = tok;
for (std::list<Scope *>::iterator nli = decl->nestedIn->nestedList.begin();
nli != decl->nestedIn->nestedList.end(); ++nli) {
Scope *b = *nli;
if (b == decl) {
// remove forward declration
_forwardDecls.erase(decls);
break;
}
}
return decl;
}
}
// no forward declaration found so just return end of scope list
scopeList.push_back(Scope(this, tok, scope));
return &scopeList.back();
}
Scope * SymbolDatabase::applyScope(Scope * scope)
{
// check if there was a forward declaration
std::list<Scope *>::iterator it;
for (it = scope->nestedIn->nestedList.begin(); it != scope->nestedIn->nestedList.end(); ++it) {
if ((*it) == scope)
break;
}
// add it if there was no forward declaration
if (it == scope->nestedIn->nestedList.end())
scope->nestedIn->nestedList.push_back(scope);
return scope;
}
bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart) bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart)
{ {
// function returning function pointer? '... ( ... %var% ( ... ))( ... ) {' // function returning function pointer? '... ( ... %var% ( ... ))( ... ) {'
@ -1522,7 +1620,7 @@ void SymbolDatabase::printOut(const char *title) const
count = scope->nestedList.size(); count = scope->nestedList.size();
for (nsi = scope->nestedList.begin(); nsi != scope->nestedList.end(); ++nsi) { 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) if (count-- > 1)
std::cout << ","; std::cout << ",";
} }
@ -1752,11 +1850,20 @@ Scope::Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_) :
type = Scope::eGlobal; type = Scope::eGlobal;
} else if (classDef->str() == "class") { } else if (classDef->str() == "class") {
type = Scope::eClass; type = Scope::eClass;
className = classDef->next()->str(); const Token * tok = classDef->next();
while (tok->strAt(1) == "::")
tok = tok->tokAt(2);
className = tok->str();
} else if (classDef->str() == "struct") { } else if (classDef->str() == "struct") {
type = Scope::eStruct; type = Scope::eStruct;
if (classDef->next()->str() == "::") {
const Token * tok = classDef->next();
while (tok->strAt(1) == "::")
tok = tok->tokAt(2);
className = tok->str();
}
// anonymous and unnamed structs don't have a name // anonymous and unnamed structs don't have a name
if (classDef->next()->str() != "{") else if (classDef->next()->str() != "{")
className = classDef->next()->str(); className = classDef->next()->str();
} else if (classDef->str() == "union") { } else if (classDef->str() == "union") {
type = Scope::eUnion; type = Scope::eUnion;

View File

@ -25,6 +25,7 @@
#include <list> #include <list>
#include <vector> #include <vector>
#include <set> #include <set>
#include <stack>
#include "config.h" #include "config.h"
#include "token.h" #include "token.h"
@ -490,6 +491,10 @@ public:
type == eTry || type == eCatch); type == eTry || type == eCatch);
} }
bool isForwardDeclaration() const {
return isClassOrStruct() && classStart == NULL;
}
/** /**
* @brief find if name is in nested list * @brief find if name is in nested list
* @param name name of nested scope * @param name name of nested scope
@ -625,6 +630,9 @@ private:
void addNewFunction(Scope **info, const Token **tok); void addNewFunction(Scope **info, const Token **tok);
static bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart); static bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart);
Scope * getScope(const std::list<std::string> &names, const Token *tok, Scope *scope);
Scope * applyScope(Scope * scope);
/** class/struct types */ /** class/struct types */
std::set<std::string> classAndStructTypes; std::set<std::string> classAndStructTypes;
@ -634,6 +642,19 @@ private:
/** variable symbol table */ /** variable symbol table */
std::vector<const Variable *> _variableList; std::vector<const Variable *> _variableList;
/** Forward declarations list for fast searching.
Forward declarations are removed if implementation is found. */
std::list<Scope *> _forwardDecls;
/** Scope to jump back to when forward declaration implementation found.
Implementation information updates forward declaration. */
struct Back {
Scope * forward; // forward declaration
Scope * back; // scope to jump back to
Back(Scope * f, Scope * b) : forward(f), back(b) { }
};
std::stack<Back> _back;
}; };
#endif #endif

View File

@ -107,6 +107,7 @@ private:
TEST_CASE(uninitVar20); // ticket #2867 TEST_CASE(uninitVar20); // ticket #2867
TEST_CASE(uninitVar21); // ticket #2947 TEST_CASE(uninitVar21); // ticket #2947
TEST_CASE(uninitVar22); // ticket #3043 TEST_CASE(uninitVar22); // ticket #3043
TEST_CASE(uninitVar23); // ticket #3190
TEST_CASE(uninitVarEnum); TEST_CASE(uninitVarEnum);
TEST_CASE(uninitVarStream); TEST_CASE(uninitVarStream);
TEST_CASE(uninitVarTypedef); TEST_CASE(uninitVarTypedef);
@ -1561,6 +1562,24 @@ private:
ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::x' is not assigned a value in 'Fred::operator='.\n", errout.str()); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::x' is not assigned a value in 'Fred::operator='.\n", errout.str());
} }
void uninitVar23() { // ticket #3190
check("class Foo { class Sub; };\n"
"class Bar { class Sub; };\n"
"class Bar::Sub {\n"
"public:\n"
" Sub() { }\n"
" int n;\n"
"};\n"
"class ::Foo::Sub {\n"
"public:\n"
" Sub() { }\n"
" int n;\n"
"};");
ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Sub::n' is not initialized in the constructor.\n"
"[test.cpp:5]: (warning) Member variable 'Sub::n' is not initialized in the constructor.\n", errout.str());
}
void uninitVarArray1() { void uninitVarArray1() {
check("class John\n" check("class John\n"
"{\n" "{\n"

View File

@ -637,9 +637,9 @@ private:
" printf(\"%f\", f);\n" " printf(\"%f\", f);\n"
" printf(\"%p\", 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" 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 an 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 integer given in the argument list.\n", "", errout.str()); "[test.cpp:5]: (warning) %p in format string (no. 1) requires an address given in the argument list.\n", errout.str());
} }
}; };

View File

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