Uninitialized variables: Started writing a new check. Ticket: #3369

This commit is contained in:
Daniel Marjamäki 2011-12-13 21:57:27 +01:00
parent c9f5117cf5
commit 458fa0874a
3 changed files with 154 additions and 0 deletions

View File

@ -22,6 +22,7 @@
#include "mathlib.h" #include "mathlib.h"
#include "executionpath.h" #include "executionpath.h"
#include "checknullpointer.h" // CheckNullPointer::parseFunctionCall #include "checknullpointer.h" // CheckNullPointer::parseFunctionCall
#include "symboldatabase.h"
#include <algorithm> #include <algorithm>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1029,6 +1030,104 @@ void CheckUninitVar::executionPaths()
} }
} }
void CheckUninitVar::check()
{
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
std::list<Scope>::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_) 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)")); reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "'" + (strncpy_ ? " (strncpy doesn't always 0-terminate it)" : " (not 0-terminated)"));

View File

@ -55,8 +55,13 @@ public:
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger);
checkUninitVar.executionPaths(); 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 * @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables
* @param tokens [in] the token list * @param tokens [in] the token list

View File

@ -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_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(func_uninit_pointer); // analyse function calls for: 'void a(int *p) { *p = 0; }'
TEST_CASE(uninitvar_typeof); // typeof TEST_CASE(uninitvar_typeof); // typeof
// checking for uninitialized variables without using the
// ExecutionPath functionality
TEST_CASE(uninitvar2);
} }
void checkUninitVar(const char code[]) { void checkUninitVar(const char code[]) {
@ -1674,6 +1678,52 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); 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) REGISTER_TEST(TestUninitVar)