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 "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)"));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue