From ea933e987351b28bc496b265b5d25a5950d5e572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 23 Jul 2015 17:28:18 +0200 Subject: [PATCH] CheckUninitVar: Removed ExecutionPath based checker --- lib/checkuninitvar.cpp | 899 +---------------------------------------- lib/checkuninitvar.h | 4 - test/testuninitvar.cpp | 5 +- 3 files changed, 6 insertions(+), 902 deletions(-) diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index f756e1f62..28459301e 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -20,7 +20,6 @@ //--------------------------------------------------------------------------- #include "checkuninitvar.h" #include "mathlib.h" -#include "executionpath.h" #include "checknullpointer.h" // CheckNullPointer::parseFunctionCall #include "symboldatabase.h" #include @@ -36,891 +35,9 @@ namespace { //--------------------------------------------------------------------------- - -// Skip [ .. ] -static const Token * skipBrackets(const Token *tok) -{ - while (tok && tok->str() == "[") - tok = tok->link()->next(); - return tok; -} - - -/// @addtogroup Checks -/// @{ - -/** - * @brief %Check that uninitialized variables aren't used (using ExecutionPath) - * */ -class UninitVar : public ExecutionPath { -public: - /** Startup constructor */ - explicit UninitVar(Check *c, const SymbolDatabase* db, const Library *lib, bool isc) - : ExecutionPath(c, 0), symbolDatabase(db), library(lib), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) { - } - -private: - /** Create a copy of this check */ - ExecutionPath *copy() { - return new UninitVar(*this); - } - - /** internal constructor for creating extra checks */ - UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, const Library *lib, bool isc) - : ExecutionPath(c, v->declarationId()), symbolDatabase(db), library(lib), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) { - } - - /** is other execution path equal? */ - bool is_equal(const ExecutionPath *e) const { - const UninitVar *c = static_cast(e); - return (var == c->var && alloc == c->alloc && strncpy_ == c->strncpy_ && memset_nonzero == c->memset_nonzero); - } - - /** pointer to symbol database */ - const SymbolDatabase* symbolDatabase; - - /** pointer to library */ - const Library *library; - - const bool isC; - - /** variable for this check */ - const Variable* var; - - /** is this variable allocated? */ - bool alloc; - - /** is this variable initialized with strncpy (not always zero-terminated) */ - bool strncpy_; - - /** is this variable initialized but not zero-terminated (memset) */ - bool memset_nonzero; - - /** allocating pointer. For example : p = malloc(10); */ - static void alloc_pointer(std::list &checks, unsigned int varid) { - // loop through the checks and perform a allocation if the - // variable id matches - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - if (c->var->isPointer() && !c->var->isArray()) - c->alloc = true; - else - bailOutVar(checks, varid); - break; - } - } - } - - /** Initializing a pointer value. For example: *p = 0; */ - static void init_pointer(std::list &checks, const Token *tok) { - const unsigned int varid(tok->varId()); - if (!varid) - return; - - // loop through the checks and perform a initialization if the - // variable id matches - std::list::iterator it = checks.begin(); - while (it != checks.end()) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - if (c->alloc || c->var->isArray()) { - delete c; - checks.erase(it++); - continue; - } else { - use_pointer(checks, tok); - } - } - - ++it; - } - } - - /** Deallocate a pointer. For example: free(p); */ - static void dealloc_pointer(std::list &checks, const Token *tok) { - const unsigned int varid(tok->varId()); - if (!varid) - return; - - // loop through the checks and perform a deallocation if the - // variable id matches - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - // unallocated pointer variable => error - if (c->var->isPointer() && !c->var->isArray() && !c->alloc) { - CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); - if (checkUninitVar) { - checkUninitVar->uninitvarError(tok, c->var->name()); - break; - } - } - c->alloc = false; - } - } - } - - /** - * Pointer assignment: p = x; - * if p is a pointer and x is an array/pointer then bail out - * \param checks all available checks - * \param tok1 the "p" token - * \param tok2 the "x" token - */ - static void pointer_assignment(std::list &checks, const Token *tok1, const Token *tok2) { - // Variable id for "left hand side" variable - const unsigned int varid1(tok1->varId()); - if (varid1 == 0) - return; - - // Variable id for "right hand side" variable - const unsigned int varid2(tok2->varId()); - if (varid2 == 0) - return; - - std::list::const_iterator it; - - // bail out if first variable is a pointer - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid1 && c->var->isPointer() && !c->var->isArray()) { - bailOutVar(checks, varid1); - break; - } - } - - // bail out if second variable is a array/pointer - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid2 && (c->var->isPointer() || c->var->isArray())) { - bailOutVar(checks, varid2); - break; - } - } - } - - - /** Initialize an array with strncpy. */ - static void init_strncpy(std::list &checks, const Token *tok) { - const unsigned int varid(tok->varId()); - if (!varid) - return; - - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - c->strncpy_ = true; - } - } - } - - /** Initialize an array with memset (not zero). */ - static void init_memset_nonzero(std::list &checks, const Token *tok) { - const unsigned int varid(tok->varId()); - if (!varid) - return; - - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - c->memset_nonzero = true; - } - } - } - - - - /** - * use - called from the use* functions below. - * @param checks all available checks - * @param tok variable token - * @param mode specific behaviour - * @return if error is found, true is returned - */ - static bool use(std::list &checks, const Token *tok, const int mode) { - const unsigned int varid(tok->varId()); - if (varid == 0) - return false; - - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) { - // mode 0 : the variable is used "directly" - // example: .. = var; - // it is ok to read the address of an uninitialized array. - // it is ok to read the address of an allocated pointer - if (mode == 0 && (c->var->isArray() || (c->var->isPointer() && c->alloc))) - continue; - - // mode 2 : reading array data with mem.. function. It's ok if the - // array is not null-terminated - if (mode == 2 && c->strncpy_) - continue; - - // mode 3 : bad usage of pointer. if it's not a pointer then the usage is ok. - // example: ptr->foo(); - if (mode == 3 && (!c->var->isPointer() || c->var->isArray())) - continue; - - // mode 4 : using dead pointer is invalid. - if (mode == 4 && (!c->var->isPointer() || c->var->isArray() || c->alloc)) - continue; - - // mode 5 : reading uninitialized array or pointer is invalid. - if (mode == 5 && (!c->var->isArray() && !c->var->isPointer())) - continue; - - CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); - if (checkUninitVar) { - if (c->strncpy_ || c->memset_nonzero) { - if (!Token::Match(c->var->typeStartToken(), "char|wchar_t")) { - continue; - } - if (Token::Match(tok->next(), "[")) { // Check if it's not being accessed like: 'str[1]' - continue; - } - checkUninitVar->uninitstringError(tok, c->var->name(), c->strncpy_); - } else if (c->var->isPointer() && !c->var->isArray() && c->alloc) - checkUninitVar->uninitdataError(tok, c->var->name()); - else - checkUninitVar->uninitvarError(tok, c->var->name()); - return true; - } - } - } - - // No error found - return false; - } - - /** - * Reading variable. Use this function in situations when it is - * invalid to read the data of the variable but not the address. - * @param checks all available checks - * @param tok variable token - * @return if error is found, true is returned - */ - static bool use(std::list &checks, const Token *tok) { - return use(checks, tok, 0); - } - - /** - * Reading array elements. If the variable is not an array then the usage is ok. - * @param checks all available checks - * @param tok variable token - */ - static void use_array(std::list &checks, const Token *tok) { - use(checks, tok, 1); - } - - /** - * Reading array elements with a "mem.." function. It's ok if the array is not null-terminated. - * @param checks all available checks - * @param tok variable token - */ - static void use_array_mem(std::list &checks, const Token *tok) { - use(checks, tok, 2); - } - - /** - * Bad pointer usage. If the variable is not a pointer then the usage is ok. - * @param checks all available checks - * @param tok variable token - * @return if error is found, true is returned - */ - static bool use_pointer(std::list &checks, const Token *tok) { - return use(checks, tok, 3); - } - - /** - * Using variable.. if it's a dead pointer the usage is invalid. - * @param checks all available checks - * @param tok variable token - * @return if error is found, true is returned - */ - static bool use_dead_pointer(std::list &checks, const Token *tok) { - return use(checks, tok, 4); - } - - /** - * Using variable.. reading from uninitialized array or pointer data is invalid. - * Example: = x[0]; - * @param checks all available checks - * @param tok variable token - * @return if error is found, true is returned - */ - static bool use_array_or_pointer_data(std::list &checks, const Token *tok) { - return use(checks, tok, 5); - } - - /** - * Parse right hand side expression in statement - * @param tok2 start token of rhs - * @param checks the execution paths - */ - static void parserhs(const Token *tok2, std::list &checks) { - // check variable usages in rhs/index - while (nullptr != (tok2 = tok2->next())) { - if (Token::Match(tok2, "[;)=]")) - break; - if (Token::Match(tok2, "%name% (")) - break; - if (Token::Match(tok2, "%name% <") && Token::simpleMatch(tok2->linkAt(1), "> (")) - break; - if (tok2->varId() && - !Token::Match(tok2->previous(), "&|::") && - !Token::simpleMatch(tok2->tokAt(-2), "& (") && - !Token::Match(tok2->tokAt(1), ")| =")) { - // Multiple assignments.. - if (Token::Match(tok2->next(), ".|[")) { - const Token * tok3 = tok2; - while (tok3) { - if (Token::Match(tok3->next(), ". %name%")) - tok3 = tok3->tokAt(2); - else if (tok3->strAt(1) == "[") - tok3 = tok3->next()->link(); - else - break; - } - if (tok3 && tok3->strAt(1) == "=") - continue; - } - bool foundError; - if (tok2->previous()->str() == "*" || tok2->next()->str() == "[") - foundError = use_array_or_pointer_data(checks, tok2); - else - foundError = use(checks, tok2); - - // prevent duplicate error messages - if (foundError) { - bailOutVar(checks, tok2->varId()); - } - } - } - - } - - /** parse tokens. @sa ExecutionPath::parse */ - const Token *parse(const Token &tok, std::list &checks) const { - // Variable declaration.. - if (Token::Match(&tok, "%var% [[;]")) { - const Variable* var2 = tok.variable(); - if (var2 && var2->nameToken() == &tok && !var2->isStatic() && !var2->isExtern() && !Token::simpleMatch(tok.linkAt(1), "] [")) { - if (tok.linkAt(1)) { // array - const Token* endtok = tok.next(); - while (endtok->link()) - endtok = endtok->link()->next(); - if (endtok->str() != ";") - return &tok; - } - const Scope* parent = var2->scope()->nestedIn; - while (parent) { - for (std::list::const_iterator j = parent->varlist.begin(); j != parent->varlist.end(); ++j) { - if (j->name() == var2->name()) { - ExecutionPath::bailOutVar(checks, j->declarationId()); // If there is a variable with the same name in other scopes, this might cause false positives, if there are unexpanded macros - break; - } - } - parent = parent->nestedIn; - } - - if (var2->isPointer()) - checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC)); - else if (var2->typeEndToken()->str() != ">") { - const bool stdtype = var2->typeStartToken()->isStandardType(); // TODO: change to isC to handle unknown types better - if (stdtype && (!var2->isArray() || var2->nameToken()->linkAt(1)->strAt(1) == ";")) - checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC)); - } - return &tok; - } - } - - if (tok.str() == "return") { - // is there assignment or ternary operator in the return statement? - bool assignment = false; - for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { - if (tok2->str() == "=" || (!isC && tok2->str() == ">>") || Token::Match(tok2, "(|, &")) { - assignment = true; - break; - } - if (Token::Match(tok2, "[(,] &| %var% [,)]")) { - tok2 = tok2->next(); - if (!tok2->isName()) - tok2 = tok2->next(); - ExecutionPath::bailOutVar(checks, tok2->varId()); - } - } - - if (!assignment) { - for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { - if (tok2->isName() && tok2->strAt(1) == "(") - tok2 = tok2->next()->link(); - - else if (tok2->varId()) - use(checks, tok2); - } - } - } - - if (tok.varId()) { - // array variable passed as function parameter.. - if (Token::Match(tok.previous(), "[(,] %var% [+-,)]")) { - // #4896 : This checking was removed because of FP, - // the new uninitvar checking is used instead to catch - // these errors. - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - - // Used.. - if (Token::Match(tok.previous(), "[[(,+-*/|=] %var% ]|)|,|;|%op%") && !tok.next()->isAssignmentOp()) { - // Taking address of array.. - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) { - UninitVar *c = dynamic_cast(*it); - if (c && c->varId == tok.varId()) { - if (c->var->isArray() || c->alloc) - bailOutVar(checks, tok.varId()); - break; - } - } - - // initialize reference variable - if (Token::Match(tok.tokAt(-3), "& %var% =")) - bailOutVar(checks, tok.varId()); - else - use(checks, &tok); - return &tok; - } - - if ((tok.previous() && tok.previous()->type() == Token::eIncDecOp) || (tok.next() && tok.next()->type() == Token::eIncDecOp)) { - use(checks, &tok); - return &tok; - } - - if (Token::Match(tok.previous(), "[;{}] %name% [=[.]")) { - if (tok.next()->str() == ".") { - if (Token::Match(&tok, "%name% . %name% (")) { - const Function *function = tok.tokAt(2)->function(); - if (function && function->isStatic()) - return &tok; - } - if (use_dead_pointer(checks, &tok)) { - return &tok; - } - } else { - const Token *tok2 = tok.next(); - - if (tok2->str() == "[") { - const Token *tok3 = tok2->link(); - while (Token::simpleMatch(tok3, "] [")) - tok3 = tok3->next()->link(); - - // Possible initialization - if (Token::simpleMatch(tok3, "] >>")) - return &tok; - - if (Token::simpleMatch(tok3, "] =")) { - if (use_dead_pointer(checks, &tok)) { - return &tok; - } - - parserhs(tok2, checks); - tok2 = tok3->next(); - } - } - - parserhs(tok2, checks); - } - - // pointer aliasing? - if (Token::Match(tok.tokAt(2), "%name% ;")) { - pointer_assignment(checks, &tok, tok.tokAt(2)); - } - } - - if (tok.strAt(1) == "(") { - use_pointer(checks, &tok); - } - - if (Token::Match(tok.tokAt(-2), "[;{}] *")) { - if (tok.strAt(1) == "=") { - // is the pointer used in the rhs? - bool used = false; - for (const Token *tok2 = tok.tokAt(2); tok2; tok2 = tok2->next()) { - if (Token::Match(tok2, "[,;=(]")) - break; - else if (Token::Match(tok2, "* %varid%", tok.varId())) { - used = true; - break; - } - } - if (used) - use_pointer(checks, &tok); - else - init_pointer(checks, &tok); - } else { - use_pointer(checks, &tok); - } - return &tok; - } - - if (Token::Match(tok.next(), "= malloc|kmalloc") || (!isC && Token::simpleMatch(tok.next(), "= new char [")) || - (Token::Match(tok.next(), "= %name% (") && library->returnuninitdata.find(tok.strAt(2)) != library->returnuninitdata.end())) { - alloc_pointer(checks, tok.varId()); - if (tok.strAt(3) == "(") - return tok.tokAt(3); - } - - else if ((!isC && (Token::Match(tok.previous(), "<<|>>") || Token::Match(tok.previous(), "[;{}] %name% <<"))) || - tok.strAt(1) == "=") { - // TODO: Don't bail out for "<<" and ">>" if these are - // just computations - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - - if (tok.strAt(1) == "[" && tok.next()->link()) { - const Token *tok2 = tok.next()->link(); - if (tok2->strAt(1) == "=") { - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - } - - if (tok.strAt(-1) == "delete" || - Token::simpleMatch(tok.tokAt(-3), "delete [ ]")) { - dealloc_pointer(checks, &tok); - return &tok; - } - } - - if (Token::Match(&tok, "%name% (")) { - // sizeof/typeof doesn't dereference. A function name that is all uppercase - // might be an unexpanded macro that uses sizeof/typeof - if (Token::Match(&tok, "sizeof|typeof (")) - return tok.next()->link(); - - // deallocate pointer - if (Token::Match(&tok, "free|kfree|fclose ( %var% )") || - Token::Match(&tok, "realloc ( %name%")) { - dealloc_pointer(checks, tok.tokAt(2)); - if (tok.str() == "realloc") - ExecutionPath::bailOutVar(checks, tok.tokAt(2)->varId()); - return tok.tokAt(3); - } - - // parse usage.. - { - std::list var1; - CheckNullPointer::parseFunctionCall(tok, var1, library, 1); - for (std::list::const_iterator it = var1.begin(); it != var1.end(); ++it) { - // does iterator point at first function parameter? - const bool firstPar(*it == tok.tokAt(2)); - - // is function memset/memcpy/etc? - if (tok.str().compare(0,3,"mem") == 0) - use_array_mem(checks, *it); - - // second parameter for strncpy/strncat/etc - else if (!firstPar && tok.str().compare(0,4,"strn") == 0) - use_array_mem(checks, *it); - - else - use_array(checks, *it); - - use_dead_pointer(checks, *it); - } - - // Using uninitialized pointer is bad if using null pointer is bad - std::list var2; - CheckNullPointer::parseFunctionCall(tok, var2, library, 0); - for (std::list::const_iterator it = var2.begin(); it != var2.end(); ++it) { - if (std::find(var1.begin(), var1.end(), *it) == var1.end()) - use_dead_pointer(checks, *it); - } - } - - // strncpy doesn't null-terminate first parameter - if (Token::Match(&tok, "strncpy ( %name% ,")) { - if (Token::Match(tok.tokAt(4), "%str% ,")) { - if (Token::Match(tok.tokAt(6), "%num% )")) { - const std::size_t len = Token::getStrLength(tok.tokAt(4)); - const MathLib::bigint sz = MathLib::toLongNumber(tok.strAt(6)); - if (sz >= 0 && len >= static_cast(sz)) { - init_strncpy(checks, tok.tokAt(2)); - return tok.next()->link(); - } - } - } else { - init_strncpy(checks, tok.tokAt(2)); - return tok.next()->link(); - } - } - - // memset (not zero terminated).. - if (Token::Match(&tok, "memset ( %name% , !!0 , %num% )")) { - init_memset_nonzero(checks, tok.tokAt(2)); - return tok.next()->link(); - } - - if (Token::Match(&tok, "asm ( %str% )")) { - ExecutionPath::bailOut(checks); - return &tok; - } - - // is the variable passed as a parameter to some function? - unsigned int parlevel = 0; - std::set bailouts; - for (const Token *tok2 = tok.next(); tok2; tok2 = tok2->next()) { - if (tok2->str() == "(") - ++parlevel; - - else if (tok2->str() == ")") { - if (parlevel <= 1) - break; - --parlevel; - } - - else if (Token::Match(tok2, "sizeof|typeof (")) { - tok2 = tok2->next()->link(); - if (!tok2) - break; - } - - // ticket #2367 : unexpanded macro that uses sizeof|typeof? - else if (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) { - tok2 = tok2->next()->link(); - if (!tok2) - break; - } - - else if (tok2->varId()) { - if (Token::Match(tok2->tokAt(-2), "[(,] *") || Token::Match(tok2->next(), ". %name%")) { - // find function call.. - const Token *functionCall = tok2; - while (nullptr != (functionCall = functionCall ? functionCall->previous() : 0)) { - if (functionCall->str() == "(") - break; - if (functionCall->str() == ")") - functionCall = functionCall->link(); - } - - functionCall = functionCall ? functionCall->previous() : 0; - if (functionCall) { - if (functionCall->isName() && !functionCall->isUpperCaseName() && use_dead_pointer(checks, tok2)) - ExecutionPath::bailOutVar(checks, tok2->varId()); - } - } - - // it is possible that the variable is initialized here - if (Token::Match(tok2->previous(), "[(,] %var% [,)]")) - bailouts.insert(tok2->varId()); - - // array initialization.. - if (Token::Match(tok2->previous(), "[,(] %var% [+-]")) { - // if var is array, bailout - for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) { - if ((*it)->varId == tok2->varId()) { - const UninitVar *c = dynamic_cast(*it); - if (c && (c->var->isArray() || (c->var->isPointer() && c->alloc))) - bailouts.insert(tok2->varId()); - break; - } - } - } - } - } - - for (std::set::const_iterator it = bailouts.begin(); it != bailouts.end(); ++it) - ExecutionPath::bailOutVar(checks, *it); - } - - // function call via function pointer - if (Token::Match(&tok, "( * %name% ) (") || - (Token::Match(&tok, "( *| %name% .|::") && Token::Match(tok.link()->tokAt(-2), ".|:: %name% ) ("))) { - // is the variable passed as a parameter to some function? - const Token *tok2 = tok.link()->next(); - for (const Token* const end2 = tok2->link(); tok2 != end2; tok2 = tok2->next()) { - if (tok2->varId()) { - // it is possible that the variable is initialized here - ExecutionPath::bailOutVar(checks, tok2->varId()); - } - } - } - - if (tok.str() == "return") { - // Todo: if (!array && .. - if (Token::Match(tok.next(), "%name% ;")) { - use(checks, tok.next()); - } else if (Token::Match(tok.next(), "%name% [")) { - use_array_or_pointer_data(checks, tok.next()); - } - } - - if (tok.varId()) { - if (tok.strAt(-1) == "=") { - if (Token::Match(tok.tokAt(-3), "& %var% =")) { - bailOutVar(checks, tok.varId()); - return &tok; - } - - if (!Token::Match(tok.tokAt(-3), ". %name% =")) { - if (!Token::Match(tok.tokAt(-3), "[;{}] %name% =")) { - use(checks, &tok); - return &tok; - } - - const unsigned int varid2 = tok.tokAt(-2)->varId(); - if (varid2) { - { - use(checks, &tok); - return &tok; - } - } - } - } - - if (tok.strAt(1) == ".") { - bailOutVar(checks, tok.varId()); - return &tok; - } - - if (tok.strAt(1) == "[") { - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - - if (Token::Match(tok.tokAt(-2), "[,(=] *")) { - use_pointer(checks, &tok); - return &tok; - } - - if (tok.strAt(-1) == "&") { - ExecutionPath::bailOutVar(checks, tok.varId()); - } - } - - // Parse "for" - if (Token::Match(&tok, "[;{}] for (")) { - // initialized variables - std::set varid1; - varid1.insert(0); - - // Parse token - const Token *tok2; - - // parse setup - for (tok2 = tok.tokAt(3); tok2 != tok.link(); tok2 = tok2->next()) { - if (tok2->str() == ";") - break; - if (tok2->varId()) - varid1.insert(tok2->varId()); - } - if (tok2 == tok.link()) - return &tok; - - // parse condition - if (Token::Match(tok2, "; %var% <|<=|>=|> %num% ;")) { - // If the variable hasn't been initialized then call "use" - if (varid1.find(tok2->next()->varId()) == varid1.end()) - use(checks, tok2->next()); - } - - // goto stepcode - tok2 = tok2->next(); - while (tok2 && tok2->str() != ";") - tok2 = tok2->next(); - - // parse the stepcode - if (Token::Match(tok2, "; ++|-- %var% ) {") || - Token::Match(tok2, "; %var% ++|-- ) {")) { - // get id of variable.. - unsigned int varid = tok2->next()->varId(); - if (!varid) - varid = tok2->tokAt(2)->varId(); - - // Check that the variable hasn't been initialized and - // that it isn't initialized in the body.. - if (varid1.find(varid) == varid1.end()) { - for (const Token *tok3 = tok2->tokAt(5); tok3 && tok3 != tok2->linkAt(4); tok3 = tok3->next()) { - if (tok3->varId() == varid) { - varid = 0; // variable is used.. maybe it's initialized. clear the variable id. - break; - } - } - - // If the variable isn't initialized in the body call "use" - if (varid != 0) { - // goto variable - tok2 = tok2->next(); - if (!tok2->varId()) - tok2 = tok2->next(); - - // call "use" - use(checks, tok2); - } - } - } - } - - return &tok; - } - - bool parseCondition(const Token &tok, std::list &checks) { - if (tok.varId() && Token::Match(&tok, "%name% <|<=|==|!=|)")) - use(checks, &tok); - - else if (Token::Match(&tok, "!| %name% [") && !Token::simpleMatch(skipBrackets(tok.next()), "=")) - use_array_or_pointer_data(checks, tok.str() == "!" ? tok.next() : &tok); - - else if (Token::Match(&tok, "!| %name% (")) { - const Token * const ftok = (tok.str() == "!") ? tok.next() : &tok; - std::list var1; - CheckNullPointer::parseFunctionCall(*ftok, var1, library, 1); - for (std::list::const_iterator it = var1.begin(); it != var1.end(); ++it) { - // is function memset/memcpy/etc? - if (ftok->str().compare(0,3,"mem") == 0) - use_array_mem(checks, *it); - else - use_array(checks, *it); - } - } - - else if (Token::Match(&tok, "! %name% )")) { - use(checks, &tok); - return false; - } - - return ExecutionPath::parseCondition(tok, checks); - } - - void parseLoopBody(const Token *tok, std::list &checks) const { - while (tok) { - if (tok->str() == "{" || tok->str() == "}" || tok->str() == "for") - return; - if (Token::simpleMatch(tok, "if (")) { - // bail out all variables that are used in the condition - const Token* const end2 = tok->linkAt(1); - for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { - if (tok2->varId()) - ExecutionPath::bailOutVar(checks, tok2->varId()); - } - } - const Token *next = parse(*tok, checks); - tok = next->next(); - } - } - -public: - - static void analyseFunctions(const Token * const tokens, std::set &func) { +namespace UninitVar { + void analyseFunctions(const Token * const tokens, std::set &func) + { for (const Token *tok = tokens; tok; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); @@ -1008,9 +125,8 @@ public: } } } -}; +} -/// @} Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const @@ -1033,13 +149,6 @@ void CheckUninitVar::analyseFunctions(const Tokenizer *tokenizer, std::settokens(), f); } -void CheckUninitVar::executionPaths() -{ - // check if variable is accessed uninitialized.. - UninitVar c(this, _tokenizer->getSymbolDatabase(), &_settings->library, _tokenizer->isC()); - checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c); -} - void CheckUninitVar::check() { diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index 4960ac99e..ae28c7ad5 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -48,7 +48,6 @@ public: /** @brief Run checks against the simplified token list */ void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); - checkUninitVar.executionPaths(); checkUninitVar.check(); checkUninitVar.deadPointer(); } @@ -89,9 +88,6 @@ public: void analyseFunctions(const Tokenizer *tokenizer, std::set &f) const; - /** @brief new type of check: check execution paths */ - void executionPaths(); - void uninitstringError(const Token *tok, const std::string &varname, bool strncpy_); void uninitdataError(const Token *tok, const std::string &varname); void uninitvarError(const Token *tok, const std::string &varname); diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 94d002010..1822aa1ff 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -117,7 +117,6 @@ private: // Check code.. CheckUninitVar check(&tokenizer, &settings, this); check.check(); - check.executionPaths(); } void uninitvar1() { @@ -1516,7 +1515,7 @@ private: "}"); ASSERT_EQUALS("", errout.str()); - checkUninitVarB("struct StgStrm {\n" + checkUninitVar2("struct StgStrm {\n" " StgIo& rIo;\n" " StgStrm(StgIo&);\n" " virtual sal_Int32 Write();\n" @@ -1527,7 +1526,7 @@ private: " pNewStrm = new StgStrm(rIo);\n" " pNewStrm->Write();\n" "}"); - ASSERT_EQUALS("[test.cpp:10]: (error) Uninitialized variable: pNewStrm\n", errout.str()); + TODO_ASSERT_EQUALS("[test.cpp:10]: (error) Uninitialized variable: pNewStrm\n", "", errout.str()); // #6450 - calling a member function is allowed if memory was allocated by new checkUninitVarB("struct EMFPFont {\n"