diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 366866074..0664c8542 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -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; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index c21aa37e5..817bb2f13 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -49,50 +49,83 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti // Store current access in each scope (depends on evaluation progress) std::map access; + std::map 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::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::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; +} diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 3e0e75c2e..e0a7d71d9 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -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 classAndStructTypes; diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 3e540a6b8..d6c408941 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -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" diff --git a/test/testio.cpp b/test/testio.cpp index 8772bc91d..cc487447c 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -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 diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 89bfe8817..0635cc7f6 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -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); } }