Uninitialized variables: Started writing a new check. Ticket: #3369
This commit is contained in:
parent
c9f5117cf5
commit
458fa0874a
|
@ -22,6 +22,7 @@
|
|||
#include "mathlib.h"
|
||||
#include "executionpath.h"
|
||||
#include "checknullpointer.h" // CheckNullPointer::parseFunctionCall
|
||||
#include "symboldatabase.h"
|
||||
#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_)
|
||||
{
|
||||
reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "'" + (strncpy_ ? " (strncpy doesn't always 0-terminate it)" : " (not 0-terminated)"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue