diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 9fc49cd0d..802c89a0e 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -22,6 +22,7 @@ #include "mathlib.h" #include "executionpath.h" #include "checknullpointer.h" // CheckNullPointer::parseFunctionCall +#include "symboldatabase.h" #include //--------------------------------------------------------------------------- @@ -1029,6 +1030,104 @@ void CheckUninitVar::executionPaths() } } + +void CheckUninitVar::check() +{ + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + std::list::const_iterator func_scope; + + // scan every function + for (func_scope = symbolDatabase->scopeList.begin(); func_scope != symbolDatabase->scopeList.end(); ++func_scope) { + // only check functions + if (func_scope->type == Scope::eFunction) { + for (const Token *tok = func_scope->classStart; tok && tok != func_scope->classEnd; tok = tok->next()) { + // Variable declaration.. + if (Token::Match(tok, "[;{}] %type% %var% ;") && tok->next()->isStandardType()) { + checkScopeForVariable(tok->tokAt(3), tok->tokAt(2)->varId()); + } + } + } + } +} + +bool CheckUninitVar::checkScopeForVariable(const Token *tok, const unsigned int varid) +{ + if (varid == 0) + return false; + + bool ret = false; + + unsigned int number_of_if = 0; + + for (; tok; tok = tok->next()) { + // End of scope.. + if (tok->str() == "}") { + // might be a noreturn function.. + if (Token::simpleMatch(tok->tokAt(-2), ") ; }") && + Token::Match(tok->linkAt(-2)->tokAt(-2), "[;{}] %var% (") && + tok->linkAt(-2)->previous()->varId() == 0) { + ret = true; + } + + break; + } + + // Inner scope.. + if (Token::Match(tok, "if (")) { + // goto the { + tok = tok->next()->link()->next(); + + const bool initif = checkScopeForVariable(tok->next(), varid); + + // goto the } + tok = tok->link(); + + if (!Token::Match(tok, "} else {")) { + if (initif) { + ++number_of_if; + if (number_of_if >= 2) + return true; + } + } else { + // goto the { + tok = tok->tokAt(2); + + const bool initelse = checkScopeForVariable(tok->next(), varid); + + // goto the } + tok = tok->link(); + + if (initif && initelse) + return true; + + if (initif || initelse) + ++number_of_if; + } + } + + if (tok->str() == "return") + ret = true; + + // variable is seen.. + if (tok->varId() == varid) { + // Assign variable + if (tok->previous()->str() == ">>" || tok->next()->str() == "=") + return true; + + // Use variable + if (tok->previous()->str() == "return") + uninitvarError(tok, tok->str()); + + else + // assume that variable is assigned + return true; + } + } + + return ret; +} + + void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_) { reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "'" + (strncpy_ ? " (strncpy doesn't always 0-terminate it)" : " (not 0-terminated)")); diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index bc371250f..734487b0a 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -55,8 +55,13 @@ public: void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); checkUninitVar.executionPaths(); + checkUninitVar.check(); } + /** Check for uninitialized variables */ + void check(); + bool checkScopeForVariable(const Token *tok, const unsigned int varid); + /** * @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables * @param tokens [in] the token list diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 411a3f22d..91622513f 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -48,6 +48,10 @@ private: TEST_CASE(func_uninit_var); // analyse function calls for: 'int a(int x) { return x+x; }' TEST_CASE(func_uninit_pointer); // analyse function calls for: 'void a(int *p) { *p = 0; }' TEST_CASE(uninitvar_typeof); // typeof + + // checking for uninitialized variables without using the + // ExecutionPath functionality + TEST_CASE(uninitvar2); } void checkUninitVar(const char code[]) { @@ -1674,6 +1678,52 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); } + + + + /** New checking that doesn't rely on ExecutionPath */ + void checkUninitVar2(const char code[]) { + // Clear the error buffer.. + errout.str(""); + + Settings settings; + + // Tokenize.. + Tokenizer tokenizer(&settings, this); + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.simplifyTokenList(); + + // Check for redundant code.. + CheckUninitVar checkuninitvar(&tokenizer, &settings, this); + checkuninitvar.check(); + } + + void uninitvar2() { + checkUninitVar2("void f() {\n" + " int x;\n" + " if (y == 1) { x = 1; }\n" + " else if (y == 2) { x = 1; }\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); + + checkUninitVar2("void f() {\n" + " int x;\n" + " if (y == 1) { return; }\n" + " else { x = 1; }\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar2("void f() {\n" + " int x;\n" + " if (y == 1) { exit(0); }\n" + " else { x = 1; }\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestUninitVar)