From bc283d8b99420df12f9839d3711aeb0f782e420a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 31 Oct 2010 12:31:11 +0100 Subject: [PATCH] Uninitialized variables: Broke out the checking into separate file --- Makefile | 10 +- lib/checkother.cpp | 986 -------------------------------- lib/checkother.h | 24 - lib/checkuninitvar.cpp | 1011 ++++++++++++++++++++++++++++++++ lib/checkuninitvar.h | 102 ++++ lib/lib.pri | 2 + test/testother.cpp | 1200 -------------------------------------- test/testuninitvar.cpp | 1239 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 2363 insertions(+), 2211 deletions(-) create mode 100644 lib/checkuninitvar.cpp create mode 100644 lib/checkuninitvar.h create mode 100644 test/testuninitvar.cpp diff --git a/Makefile b/Makefile index 2abff5e45..d286ab7ef 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,7 @@ LIBOBJ = lib/checkautovariables.o \ lib/checkother.o \ lib/checkpostfixoperator.o \ lib/checkstl.o \ + lib/checkuninitvar.o \ lib/checkunusedfunctions.o \ lib/cppcheck.o \ lib/errorlogger.o \ @@ -80,6 +81,7 @@ TESTOBJ = test/options.o \ test/testthreadexecutor.o \ test/testtoken.o \ test/testtokenize.o \ + test/testuninitvar.o \ test/testunusedfunctions.o \ test/testunusedprivfunc.o \ test/testunusedvar.o \ @@ -148,7 +150,7 @@ lib/checknullpointer.o: lib/checknullpointer.cpp lib/checknullpointer.h lib/chec lib/checkobsoletefunctions.o: lib/checkobsoletefunctions.cpp lib/checkobsoletefunctions.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h $(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkobsoletefunctions.o lib/checkobsoletefunctions.cpp -lib/checkother.o: lib/checkother.cpp lib/checkother.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/mathlib.h lib/executionpath.h lib/checknullpointer.h +lib/checkother.o: lib/checkother.cpp lib/checkother.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/mathlib.h $(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkother.o lib/checkother.cpp lib/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/checkpostfixoperator.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h @@ -157,6 +159,9 @@ lib/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/checkpostfixoperato lib/checkstl.o: lib/checkstl.cpp lib/checkstl.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/executionpath.h $(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkstl.o lib/checkstl.cpp +lib/checkuninitvar.o: lib/checkuninitvar.cpp lib/checkuninitvar.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h lib/mathlib.h lib/executionpath.h lib/checknullpointer.h + $(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkuninitvar.o lib/checkuninitvar.cpp + lib/checkunusedfunctions.o: lib/checkunusedfunctions.cpp lib/checkunusedfunctions.h lib/check.h lib/token.h lib/tokenize.h lib/settings.h lib/errorlogger.h $(CXX) $(CXXFLAGS) -Ilib -c -o lib/checkunusedfunctions.o lib/checkunusedfunctions.cpp @@ -295,6 +300,9 @@ test/testtoken.o: test/testtoken.cpp test/testsuite.h lib/errorlogger.h test/red test/testtokenize.o: test/testtokenize.cpp test/testsuite.h lib/errorlogger.h test/redirect.h lib/tokenize.h lib/token.h lib/settings.h $(CXX) $(CXXFLAGS) -Ilib -Icli -c -o test/testtokenize.o test/testtokenize.cpp +test/testuninitvar.o: test/testuninitvar.cpp lib/tokenize.h lib/checkuninitvar.h lib/check.h lib/token.h lib/settings.h lib/errorlogger.h test/testsuite.h test/redirect.h + $(CXX) $(CXXFLAGS) -Ilib -Icli -c -o test/testuninitvar.o test/testuninitvar.cpp + test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/tokenize.h test/testsuite.h lib/errorlogger.h test/redirect.h lib/checkunusedfunctions.h lib/check.h lib/token.h lib/settings.h $(CXX) $(CXXFLAGS) -Ilib -Icli -c -o test/testunusedfunctions.o test/testunusedfunctions.cpp diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 67f11c6ff..a18c4f479 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -20,16 +20,7 @@ //--------------------------------------------------------------------------- #include "checkother.h" #include "mathlib.h" -#include "executionpath.h" -#include "checknullpointer.h" // CheckNullPointer::parseFunctionCall -#include -#include -#include -#include -#include -#include -#include #include // fabs() //--------------------------------------------------------------------------- @@ -2249,968 +2240,6 @@ void CheckOther::strPlusChar() } } - -/// @addtogroup Checks -/// @{ - -/** - * @brief %Check that uninitialized variables aren't used (using ExecutionPath) - * */ -class CheckUninitVar : public ExecutionPath -{ -public: - /** Startup constructor */ - CheckUninitVar(Check *c) - : ExecutionPath(c, 0), pointer(false), array(false), alloc(false), strncpy_(false) - { - } - -private: - /** Create a copy of this check */ - ExecutionPath *copy() - { - return new CheckUninitVar(*this); - } - - /** no implementation => compiler error if used */ - void operator=(const CheckUninitVar &); - - /** internal constructor for creating extra checks */ - CheckUninitVar(Check *c, unsigned int v, const std::string &name, bool p, bool a) - : ExecutionPath(c, v), varname(name), pointer(p), array(a), alloc(false), strncpy_(false) - { - } - - /** is other execution path equal? */ - bool is_equal(const ExecutionPath *e) const - { - const CheckUninitVar *c = static_cast(e); - return (varname == c->varname && pointer == c->pointer && array == c->array && alloc == c->alloc && strncpy_ == c->strncpy_); - } - - /** variable name for this check */ - const std::string varname; - - /** is this variable a pointer? */ - const bool pointer; - - /** is this variable an array? */ - const bool array; - - /** is this variable allocated? */ - bool alloc; - - /** is this variable initialized with strncpy (not always zero-terminated) */ - bool strncpy_; - - /** allocating pointer. For example : p = malloc(10); */ - static void alloc_pointer(std::list &checks, unsigned int varid) - { - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) - c->alloc = true; - } - } - - /** 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; - - std::list::iterator it = checks.begin(); - while (it != checks.end()) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) - { - if (c->alloc || c->array) - { - 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; - - std::list::const_iterator it; - for (it = checks.begin(); it != checks.end(); ++it) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) - { - if (c->pointer && !c->alloc) - { - CheckOther *checkOther = dynamic_cast(c->owner); - if (checkOther) - { - checkOther->uninitvarError(tok, c->varname); - 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) - { - const unsigned int varid1(tok1->varId()); - if (varid1 == 0) - return; - - 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) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid1 && c->pointer) - { - bailOutVar(checks, varid1); - break; - } - } - - // bail out if second variable is a array/pointer - for (it = checks.begin(); it != checks.end(); ++it) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid2 && (c->pointer || c->array)) - { - 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) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varId == varid) - { - c->strncpy_ = 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) - { - CheckUninitVar *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->array || (c->pointer && c->alloc))) - continue; - - // mode 2 : bad usage of pointer. if it's not a pointer then the usage is ok. - // example: ptr->foo(); - if (mode == 2 && !c->pointer) - continue; - - // mode 3 : using dead pointer is invalid. - if (mode == 3 && (!c->pointer || c->alloc)) - continue; - - // mode 4 : reading uninitialized array or pointer is invalid. - if (mode == 4 && (!c->array && !c->pointer)) - continue; - - CheckOther *checkOther = dynamic_cast(c->owner); - if (checkOther) - { - if (c->strncpy_) - checkOther->uninitstringError(tok, c->varname); - else if (c->pointer && c->alloc) - checkOther->uninitdataError(tok, c->varname); - else - checkOther->uninitvarError(tok, c->varname); - 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); - } - - /** - * 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, 2); - } - - /** - * 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, 3); - } - - /** - * 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, 4); - } - - - /** declaring a variable */ - void declare(std::list &checks, const Token *vartok, const Token &tok, const bool p, const bool a) const - { - if (vartok->varId() == 0) - return; - - // Suppress warnings if variable in inner scope has same name as variable in outer scope - if (!tok.isStandardType()) - { - std::set dup; - for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) - { - CheckUninitVar *c = dynamic_cast(*it); - if (c && c->varname == vartok->str() && c->varId != vartok->varId()) - dup.insert(c->varId); - } - if (!dup.empty()) - { - for (std::set::const_iterator it = dup.begin(); it != dup.end(); ++it) - bailOutVar(checks, *it); - return; - } - } - - if (a || p || tok.isStandardType()) - checks.push_back(new CheckUninitVar(owner, vartok->varId(), vartok->str(), p, a)); - } - - /** parse tokens. @sa ExecutionPath::parse */ - const Token *parse(const Token &tok, std::list &checks) const - { - // Variable declaration.. - if (Token::Match(tok.previous(), "[;{}] %var%") && tok.str() != "return") - { - if (Token::Match(&tok, "enum %type% {")) - return tok.tokAt(2)->link(); - - const Token * vartok = &tok; - while (Token::Match(vartok, "const|struct")) - vartok = vartok->next(); - - if (Token::Match(vartok, "%type% *| %var% ;")) - { - vartok = vartok->next(); - const bool p(vartok->str() == "*"); - if (p) - vartok = vartok->next(); - declare(checks, vartok, tok, p, false); - return vartok; - } - - // Variable declaration for array.. - if (Token::Match(vartok, "%type% %var% [ %num% ] ;")) - { - vartok = vartok->next(); - declare(checks, vartok, tok, false, true); - return vartok->next()->link(); - } - - // Template pointer variable.. - if (Token::Match(vartok, "%type% ::|<")) - { - while (Token::Match(vartok, "%type% ::")) - vartok = vartok->tokAt(2); - if (Token::Match(vartok, "%type% < %type%")) - { - vartok = vartok->tokAt(3); - while (vartok && (vartok->str() == "*" || vartok->isName())) - vartok = vartok->next(); - if (Token::Match(vartok, "> * %var% ;")) - { - declare(checks, vartok->tokAt(2), tok, true, false); - return vartok->tokAt(2); - } - } - } - } - - if (tok.varId()) - { - // Used.. - if (Token::Match(tok.previous(), "[[(,+-*/] %var% []),+-*/]")) - { - use(checks, &tok); - return &tok; - } - - if (Token::Match(tok.previous(), "++|--") || Token::Match(tok.next(), "++|--")) - { - use(checks, &tok); - return &tok; - } - - if (Token::Match(tok.previous(), "[;{}] %var% [=[.]")) - { - if (tok.next()->str() == ".") - { - if (use_dead_pointer(checks, &tok)) - { - return &tok; - } - } - else - { - // check variable usages in rhs/index - for (const Token *tok2 = tok.tokAt(2); tok2; tok2 = tok2->next()) - { - if (Token::Match(tok2, "[;)=?]")) - break; - if (Token::Match(tok2, "%var% (")) - break; - if (tok2->varId() && - !Token::Match(tok2->previous(), "&|::") && - !Token::simpleMatch(tok2->next(), "=")) - { - // Multiple assignments.. - if (Token::simpleMatch(tok2->next(), "[")) - { - const Token * tok3 = tok2; - while (Token::simpleMatch(tok3->next(), "[")) - tok3 = tok3->next()->link(); - if (Token::simpleMatch(tok3, "] =")) - 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()); - } - } - } - } - - // pointer aliasing? - if (Token::Match(tok.tokAt(2), "%var% ;")) - { - pointer_assignment(checks, &tok, tok.tokAt(2)); - } - } - - if (Token::simpleMatch(tok.next(), "(")) - { - use_pointer(checks, &tok); - } - - if (Token::Match(tok.tokAt(-2), "[;{}] *")) - { - if (Token::simpleMatch(tok.next(), "=")) - init_pointer(checks, &tok); - else - use_pointer(checks, &tok); - return &tok; - } - - // += etc - if (Token::Match(tok.previous(), "[;{}]") || Token::Match(tok.tokAt(-2), "[;{}] *")) - { - // goto the equal.. - const Token *eq = tok.next(); - if (eq && eq->str() == "[" && eq->link() && eq->link()->next()) - eq = eq->link()->next(); - - // is it X= - if (Token::Match(eq, "+=|-=|*=|/=|&=|^=") || eq->str() == "|=") - { - if (tok.previous()->str() == "*") - use_pointer(checks, &tok); - else if (tok.next()->str() == "[") - use_array(checks, &tok); - else - use(checks, &tok); - } - } - - if (Token::Match(tok.next(), "= malloc|kmalloc") || Token::simpleMatch(tok.next(), "= new char [")) - { - alloc_pointer(checks, tok.varId()); - if (tok.tokAt(3)->str() == "(") - return tok.tokAt(3); - } - - else if (Token::simpleMatch(tok.previous(), ">>") || Token::simpleMatch(tok.next(), "=")) - { - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - - if (Token::simpleMatch(tok.next(), "[")) - { - const Token *tok2 = tok.next()->link(); - if (Token::simpleMatch(tok2 ? tok2->next() : 0, "=")) - { - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - } - - if (Token::simpleMatch(tok.previous(), "delete") || - Token::simpleMatch(tok.tokAt(-3), "delete [ ]")) - { - dealloc_pointer(checks, &tok); - return &tok; - } - } - - if (Token::Match(&tok, "%var% (") && uvarFunctions.find(tok.str()) == uvarFunctions.end()) - { - if (Token::simpleMatch(&tok, "sizeof (")) - return tok.next()->link(); - - // deallocate pointer - if (Token::Match(&tok, "free|kfree|fclose ( %var% )")) - { - dealloc_pointer(checks, tok.tokAt(2)); - return tok.tokAt(3); - } - - // parse usage.. - { - std::list var; - CheckNullPointer::parseFunctionCall(tok, var, 1); - for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) - use_array(checks, *it); - - // Using uninitialized pointer is bad if using null pointer is bad - std::list var2; - CheckNullPointer::parseFunctionCall(tok, var2, 0); - for (std::list::const_iterator it = var2.begin(); it != var2.end(); ++it) - { - if (std::find(var.begin(), var.end(), *it) == var.end()) - use_dead_pointer(checks, *it); - } - } - - // strncpy doesn't 0-terminate first parameter - if (Token::Match(&tok, "strncpy ( %var% ,")) - { - if (Token::Match(tok.tokAt(4), "%str% ,")) - { - if (Token::Match(tok.tokAt(6), "%num% )")) - { - const unsigned int len = Token::getStrLength(tok.tokAt(4)); - const long 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(); - } - } - - if (Token::simpleMatch(&tok, "asm ( )")) - { - 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::simpleMatch(tok2, "sizeof (")) - { - tok2 = tok2->next()->link(); - if (!tok2) - break; - } - - else if (tok2->varId()) - { - if (Token::Match(tok2->tokAt(-2), "[(,] *") || Token::Match(tok2->next(), ". %var%")) - { - if (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 CheckUninitVar *c = dynamic_cast(*it); - if (c && c->array) - 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, "( * %var% ) (")) - { - // is the variable passed as a parameter to some function? - unsigned int parlevel = 0; - for (const Token *tok2 = tok.link()->next(); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "(") - ++parlevel; - - else if (tok2->str() == ")") - { - if (parlevel <= 1) - break; - --parlevel; - } - - else 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(), "%var% ;")) - { - use(checks, tok.next()); - } - else if (Token::Match(tok.next(), "%var% [")) - { - use_array_or_pointer_data(checks, tok.next()); - } - } - - if (tok.varId()) - { - if (Token::simpleMatch(tok.previous(), "=")) - { - if (Token::Match(tok.tokAt(-3), "& %var% =")) - { - bailOutVar(checks, tok.varId()); - return &tok; - } - - if (!Token::Match(tok.tokAt(-3), ". %var% =")) - { - if (!Token::Match(tok.tokAt(-3), "[;{}] %var% =")) - { - use(checks, &tok); - return &tok; - } - - const unsigned int varid2 = tok.tokAt(-2)->varId(); - if (varid2) - { - { - use(checks, &tok); - return &tok; - } - } - } - } - - if (Token::simpleMatch(tok.next(), ".")) - { - const Token *tok2 = tok.next(); - while (Token::Match(tok2, ". %var%")) - tok2 = tok2->tokAt(2); - if (tok2 && tok2->str() != "=") - use_pointer(checks, &tok); - else - bailOutVar(checks, tok.varId()); - return &tok; - } - - if (Token::simpleMatch(tok.next(), "[")) - { - ExecutionPath::bailOutVar(checks, tok.varId()); - return &tok; - } - - if (Token::Match(tok.tokAt(-2), "[,(=] *")) - { - use_pointer(checks, &tok); - return &tok; - } - - if (Token::simpleMatch(tok.previous(), "&")) - { - 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; tok2 = tok2->next()) - { - if (tok2->str() == ";") - break; - if (tok2->varId()) - varid1.insert(tok2->varId()); - } - - // 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()) - { - unsigned int indentlevel = 0; - for (const Token *tok3 = tok2->tokAt(5); tok3; tok3 = tok3->next()) - { - if (tok3->str() == "{") - ++indentlevel; - else if (tok3->str() == "}") - { - if (indentlevel == 0) - break; - --indentlevel; - } - 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, "%var% <|<=|==|!=|)")) - use(checks, &tok); - - else if (Token::Match(&tok, "!| %var% [")) - use_array_or_pointer_data(checks, tok.str() == "!" ? tok.next() : &tok); - - else if (Token::Match(&tok, "!| %var% (")) - { - std::list var; - CheckNullPointer::parseFunctionCall(tok.str() == "!" ? *tok.next() : tok, var, 1); - for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) - use_array(checks, *it); - } - - else if (Token::Match(&tok, "! %var% )")) - { - 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; - const Token *next = parse(*tok, checks); - if (next) - tok = tok->next(); - } - } - -public: - - /** Functions that don't handle uninitialized variables well */ - static std::set uvarFunctions; - - static void analyseFunctions(const Token * const tokens, std::set &func) - { - for (const Token *tok = tokens; tok; tok = tok->next()) - { - if (tok->str() == "{") - { - tok = tok->link(); - continue; - } - if (tok->str() != "::" && Token::Match(tok->next(), "%var% ( %type%")) - { - if (!Token::simpleMatch(tok->tokAt(2)->link(), ") {")) - continue; - const Token *tok2 = tok->tokAt(3); - while (tok2 && tok2->str() != ")") - { - if (tok2->str() == ",") - tok2 = tok2->next(); - - if (Token::Match(tok2, "%type% %var% ,|)") && tok2->isStandardType()) - { - tok2 = tok2->tokAt(2); - continue; - } - - if (tok2->isStandardType() && Token::Match(tok2, "%type% & %var% ,|)")) - { - const unsigned int varid(tok2->tokAt(2)->varId()); - - // flags for read/write - bool r = false, w = false; - - // check how the variable is used in the function - unsigned int indentlevel = 0; - for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) - { - if (tok3->str() == "{") - ++indentlevel; - else if (tok3->str() == "}") - { - if (indentlevel <= 1) - break; - --indentlevel; - } - else if (indentlevel == 0 && tok3->str() == ";") - break; - else if (indentlevel >= 1 && tok3->varId() == varid) - { - if (Token::Match(tok3->previous(), "++|--") || - Token::Match(tok3->next(), "++|--")) - { - r = true; - } - - else - { - w = true; - break; - } - } - } - - if (!r || w) - break; - - tok2 = tok2->tokAt(3); - continue; - } - - if (Token::Match(tok2, "const %type% &|*| const| %var% ,|)") && tok2->next()->isStandardType()) - { - tok2 = tok2->tokAt(3); - while (tok2->isName()) - tok2 = tok2->next(); - continue; - } - - break; - } - - // found simple function.. - if (tok2->link() == tok->tokAt(2)) - func.insert(tok->next()->str()); - } - } - } -}; - -/** Functions that don't handle uninitialized variables well */ -std::set CheckUninitVar::uvarFunctions; - - -/// @} - - -void CheckOther::analyse(const Token * const tokens, std::set &func) const -{ - CheckUninitVar::analyseFunctions(tokens, func); -} - -void CheckOther::saveAnalysisData(const std::set &data) const -{ - CheckUninitVar::uvarFunctions.insert(data.begin(), data.end()); -} - -void CheckOther::executionPaths() -{ - // check if variable is accessed uninitialized.. - { - // no writing if multiple threads are used (TODO: thread safe analysis?) - if (_settings->_jobs == 1) - CheckUninitVar::analyseFunctions(_tokenizer->tokens(), CheckUninitVar::uvarFunctions); - - CheckUninitVar c(this); - checkExecutionPaths(_tokenizer->tokens(), &c); - } -} - void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) @@ -3445,21 +2474,6 @@ void CheckOther::strPlusChar(const Token *tok) reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic"); } -void CheckOther::uninitstringError(const Token *tok, const std::string &varname) -{ - reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "' (strncpy doesn't always 0-terminate it)"); -} - -void CheckOther::uninitdataError(const Token *tok, const std::string &varname) -{ - reportError(tok, Severity::error, "uninitdata", "Data is allocated but not initialized: " + varname); -} - -void CheckOther::uninitvarError(const Token *tok, const std::string &varname) -{ - reportError(tok, Severity::error, "uninitvar", "Uninitialized variable: " + varname); -} - void CheckOther::zerodivError(const Token *tok) { reportError(tok, Severity::error, "zerodiv", "Division by zero"); diff --git a/lib/checkother.h b/lib/checkother.h index 7aad92acb..2465f1190 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -82,23 +82,9 @@ public: checkOther.checkSelfAssignment(); checkOther.checkIncorrectLogicOperator(); - - // New type of check: Check execution paths - checkOther.executionPaths(); checkOther.checkMisusedScopedObject(); } - - /** - * @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables - * @param tokens [in] the token list - * @param func [out] names of functions that don't handle uninitialized variables well. the function names are added to the set. No clearing is made. - */ - void analyse(const Token * tokens, std::set &func) const; - - /** Save analysis results */ - void saveAnalysisData(const std::set &data) const; - /** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast(); @@ -138,9 +124,6 @@ public: /** @brief str plus char (unusual pointer arithmetic) */ void strPlusChar(); - /** @brief new type of check: check execution paths */ - void executionPaths(); - /** @brief %Check zero division*/ void checkZeroDivision(); @@ -195,9 +178,6 @@ public: void variableScopeError(const Token *tok, const std::string &varname); void conditionAlwaysTrueFalse(const Token *tok, const std::string &truefalse); void strPlusChar(const Token *tok); - void uninitstringError(const Token *tok, const std::string &varname); - void uninitdataError(const Token *tok, const std::string &varname); - void uninitvarError(const Token *tok, const std::string &varname); void zerodivError(const Token *tok); void mathfunctionCallError(const Token *tok, const unsigned int numParam = 1); void emptyStringTestError(const Token *tok, const std::string &var_name, const bool isTestForEmpty); @@ -213,9 +193,6 @@ public: // error sprintfOverlappingDataError(0, "varname"); udivError(0); - uninitstringError(0, "varname"); - uninitdataError(0, "varname"); - uninitvarError(0, "varname"); zerodivError(0); mathfunctionCallError(0); fflushOnInputStreamError(0, "stdin"); @@ -259,7 +236,6 @@ public: // error "* [[OverlappingData|bad usage of the function 'sprintf' (overlapping data)]]\n" "* division with zero\n" - "* using uninitialized variables and data\n" "* using fflush() on an input stream\n" "* scoped object destroyed immediately after construction\n" "* assignment in an assert statement\n" diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp new file mode 100644 index 000000000..923e4c753 --- /dev/null +++ b/lib/checkuninitvar.cpp @@ -0,0 +1,1011 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkuninitvar.h" +#include "mathlib.h" +#include "executionpath.h" +#include "checknullpointer.h" // CheckNullPointer::parseFunctionCall +#include +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace +{ +CheckUninitVar instance; +} + +//--------------------------------------------------------------------------- + + +/// @addtogroup Checks +/// @{ + +/** + * @brief %Check that uninitialized variables aren't used (using ExecutionPath) + * */ +class UninitVar : public ExecutionPath +{ +public: + /** Startup constructor */ + UninitVar(Check *c) + : ExecutionPath(c, 0), pointer(false), array(false), alloc(false), strncpy_(false) + { + } + +private: + /** Create a copy of this check */ + ExecutionPath *copy() + { + return new UninitVar(*this); + } + + /** no implementation => compiler error if used */ + void operator=(const UninitVar &); + + /** internal constructor for creating extra checks */ + UninitVar(Check *c, unsigned int v, const std::string &name, bool p, bool a) + : ExecutionPath(c, v), varname(name), pointer(p), array(a), alloc(false), strncpy_(false) + { + } + + /** is other execution path equal? */ + bool is_equal(const ExecutionPath *e) const + { + const UninitVar *c = static_cast(e); + return (varname == c->varname && pointer == c->pointer && array == c->array && alloc == c->alloc && strncpy_ == c->strncpy_); + } + + /** variable name for this check */ + const std::string varname; + + /** is this variable a pointer? */ + const bool pointer; + + /** is this variable an array? */ + const bool array; + + /** is this variable allocated? */ + bool alloc; + + /** is this variable initialized with strncpy (not always zero-terminated) */ + bool strncpy_; + + /** allocating pointer. For example : p = malloc(10); */ + static void alloc_pointer(std::list &checks, unsigned int varid) + { + std::list::const_iterator it; + for (it = checks.begin(); it != checks.end(); ++it) + { + UninitVar *c = dynamic_cast(*it); + if (c && c->varId == varid) + c->alloc = true; + } + } + + /** 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; + + std::list::iterator it = checks.begin(); + while (it != checks.end()) + { + UninitVar *c = dynamic_cast(*it); + if (c && c->varId == varid) + { + if (c->alloc || c->array) + { + 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; + + 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->pointer && !c->alloc) + { + CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); + if (checkUninitVar) + { + checkUninitVar->uninitvarError(tok, c->varname); + 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) + { + const unsigned int varid1(tok1->varId()); + if (varid1 == 0) + return; + + 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->pointer) + { + 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->pointer || c->array)) + { + 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; + } + } + } + + + + /** + * 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->array || (c->pointer && c->alloc))) + continue; + + // mode 2 : bad usage of pointer. if it's not a pointer then the usage is ok. + // example: ptr->foo(); + if (mode == 2 && !c->pointer) + continue; + + // mode 3 : using dead pointer is invalid. + if (mode == 3 && (!c->pointer || c->alloc)) + continue; + + // mode 4 : reading uninitialized array or pointer is invalid. + if (mode == 4 && (!c->array && !c->pointer)) + continue; + + CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); + if (checkUninitVar) + { + if (c->strncpy_) + checkUninitVar->uninitstringError(tok, c->varname); + else if (c->pointer && c->alloc) + checkUninitVar->uninitdataError(tok, c->varname); + else + checkUninitVar->uninitvarError(tok, c->varname); + 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); + } + + /** + * 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, 2); + } + + /** + * 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, 3); + } + + /** + * 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, 4); + } + + + /** declaring a variable */ + void declare(std::list &checks, const Token *vartok, const Token &tok, const bool p, const bool a) const + { + if (vartok->varId() == 0) + return; + + // Suppress warnings if variable in inner scope has same name as variable in outer scope + if (!tok.isStandardType()) + { + std::set dup; + for (std::list::const_iterator it = checks.begin(); it != checks.end(); ++it) + { + UninitVar *c = dynamic_cast(*it); + if (c && c->varname == vartok->str() && c->varId != vartok->varId()) + dup.insert(c->varId); + } + if (!dup.empty()) + { + for (std::set::const_iterator it = dup.begin(); it != dup.end(); ++it) + bailOutVar(checks, *it); + return; + } + } + + if (a || p || tok.isStandardType()) + checks.push_back(new UninitVar(owner, vartok->varId(), vartok->str(), p, a)); + } + + /** parse tokens. @sa ExecutionPath::parse */ + const Token *parse(const Token &tok, std::list &checks) const + { + // Variable declaration.. + if (Token::Match(tok.previous(), "[;{}] %var%") && tok.str() != "return") + { + if (Token::Match(&tok, "enum %type% {")) + return tok.tokAt(2)->link(); + + const Token * vartok = &tok; + while (Token::Match(vartok, "const|struct")) + vartok = vartok->next(); + + if (Token::Match(vartok, "%type% *| %var% ;")) + { + vartok = vartok->next(); + const bool p(vartok->str() == "*"); + if (p) + vartok = vartok->next(); + declare(checks, vartok, tok, p, false); + return vartok; + } + + // Variable declaration for array.. + if (Token::Match(vartok, "%type% %var% [ %num% ] ;")) + { + vartok = vartok->next(); + declare(checks, vartok, tok, false, true); + return vartok->next()->link(); + } + + // Template pointer variable.. + if (Token::Match(vartok, "%type% ::|<")) + { + while (Token::Match(vartok, "%type% ::")) + vartok = vartok->tokAt(2); + if (Token::Match(vartok, "%type% < %type%")) + { + vartok = vartok->tokAt(3); + while (vartok && (vartok->str() == "*" || vartok->isName())) + vartok = vartok->next(); + if (Token::Match(vartok, "> * %var% ;")) + { + declare(checks, vartok->tokAt(2), tok, true, false); + return vartok->tokAt(2); + } + } + } + } + + if (tok.varId()) + { + // Used.. + if (Token::Match(tok.previous(), "[[(,+-*/] %var% []),+-*/]")) + { + use(checks, &tok); + return &tok; + } + + if (Token::Match(tok.previous(), "++|--") || Token::Match(tok.next(), "++|--")) + { + use(checks, &tok); + return &tok; + } + + if (Token::Match(tok.previous(), "[;{}] %var% [=[.]")) + { + if (tok.next()->str() == ".") + { + if (use_dead_pointer(checks, &tok)) + { + return &tok; + } + } + else + { + // check variable usages in rhs/index + for (const Token *tok2 = tok.tokAt(2); tok2; tok2 = tok2->next()) + { + if (Token::Match(tok2, "[;)=?]")) + break; + if (Token::Match(tok2, "%var% (")) + break; + if (tok2->varId() && + !Token::Match(tok2->previous(), "&|::") && + !Token::simpleMatch(tok2->next(), "=")) + { + // Multiple assignments.. + if (Token::simpleMatch(tok2->next(), "[")) + { + const Token * tok3 = tok2; + while (Token::simpleMatch(tok3->next(), "[")) + tok3 = tok3->next()->link(); + if (Token::simpleMatch(tok3, "] =")) + 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()); + } + } + } + } + + // pointer aliasing? + if (Token::Match(tok.tokAt(2), "%var% ;")) + { + pointer_assignment(checks, &tok, tok.tokAt(2)); + } + } + + if (Token::simpleMatch(tok.next(), "(")) + { + use_pointer(checks, &tok); + } + + if (Token::Match(tok.tokAt(-2), "[;{}] *")) + { + if (Token::simpleMatch(tok.next(), "=")) + init_pointer(checks, &tok); + else + use_pointer(checks, &tok); + return &tok; + } + + // += etc + if (Token::Match(tok.previous(), "[;{}]") || Token::Match(tok.tokAt(-2), "[;{}] *")) + { + // goto the equal.. + const Token *eq = tok.next(); + if (eq && eq->str() == "[" && eq->link() && eq->link()->next()) + eq = eq->link()->next(); + + // is it X= + if (Token::Match(eq, "+=|-=|*=|/=|&=|^=") || eq->str() == "|=") + { + if (tok.previous()->str() == "*") + use_pointer(checks, &tok); + else if (tok.next()->str() == "[") + use_array(checks, &tok); + else + use(checks, &tok); + } + } + + if (Token::Match(tok.next(), "= malloc|kmalloc") || Token::simpleMatch(tok.next(), "= new char [")) + { + alloc_pointer(checks, tok.varId()); + if (tok.tokAt(3)->str() == "(") + return tok.tokAt(3); + } + + else if (Token::simpleMatch(tok.previous(), ">>") || Token::simpleMatch(tok.next(), "=")) + { + ExecutionPath::bailOutVar(checks, tok.varId()); + return &tok; + } + + if (Token::simpleMatch(tok.next(), "[")) + { + const Token *tok2 = tok.next()->link(); + if (Token::simpleMatch(tok2 ? tok2->next() : 0, "=")) + { + ExecutionPath::bailOutVar(checks, tok.varId()); + return &tok; + } + } + + if (Token::simpleMatch(tok.previous(), "delete") || + Token::simpleMatch(tok.tokAt(-3), "delete [ ]")) + { + dealloc_pointer(checks, &tok); + return &tok; + } + } + + if (Token::Match(&tok, "%var% (") && uvarFunctions.find(tok.str()) == uvarFunctions.end()) + { + if (Token::simpleMatch(&tok, "sizeof (")) + return tok.next()->link(); + + // deallocate pointer + if (Token::Match(&tok, "free|kfree|fclose ( %var% )")) + { + dealloc_pointer(checks, tok.tokAt(2)); + return tok.tokAt(3); + } + + // parse usage.. + { + std::list var; + CheckNullPointer::parseFunctionCall(tok, var, 1); + for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) + use_array(checks, *it); + + // Using uninitialized pointer is bad if using null pointer is bad + std::list var2; + CheckNullPointer::parseFunctionCall(tok, var2, 0); + for (std::list::const_iterator it = var2.begin(); it != var2.end(); ++it) + { + if (std::find(var.begin(), var.end(), *it) == var.end()) + use_dead_pointer(checks, *it); + } + } + + // strncpy doesn't 0-terminate first parameter + if (Token::Match(&tok, "strncpy ( %var% ,")) + { + if (Token::Match(tok.tokAt(4), "%str% ,")) + { + if (Token::Match(tok.tokAt(6), "%num% )")) + { + const unsigned int len = Token::getStrLength(tok.tokAt(4)); + const long 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(); + } + } + + if (Token::simpleMatch(&tok, "asm ( )")) + { + 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::simpleMatch(tok2, "sizeof (")) + { + tok2 = tok2->next()->link(); + if (!tok2) + break; + } + + else if (tok2->varId()) + { + if (Token::Match(tok2->tokAt(-2), "[(,] *") || Token::Match(tok2->next(), ". %var%")) + { + if (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->array) + 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, "( * %var% ) (")) + { + // is the variable passed as a parameter to some function? + unsigned int parlevel = 0; + for (const Token *tok2 = tok.link()->next(); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "(") + ++parlevel; + + else if (tok2->str() == ")") + { + if (parlevel <= 1) + break; + --parlevel; + } + + else 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(), "%var% ;")) + { + use(checks, tok.next()); + } + else if (Token::Match(tok.next(), "%var% [")) + { + use_array_or_pointer_data(checks, tok.next()); + } + } + + if (tok.varId()) + { + if (Token::simpleMatch(tok.previous(), "=")) + { + if (Token::Match(tok.tokAt(-3), "& %var% =")) + { + bailOutVar(checks, tok.varId()); + return &tok; + } + + if (!Token::Match(tok.tokAt(-3), ". %var% =")) + { + if (!Token::Match(tok.tokAt(-3), "[;{}] %var% =")) + { + use(checks, &tok); + return &tok; + } + + const unsigned int varid2 = tok.tokAt(-2)->varId(); + if (varid2) + { + { + use(checks, &tok); + return &tok; + } + } + } + } + + if (Token::simpleMatch(tok.next(), ".")) + { + const Token *tok2 = tok.next(); + while (Token::Match(tok2, ". %var%")) + tok2 = tok2->tokAt(2); + if (tok2 && tok2->str() != "=") + use_pointer(checks, &tok); + else + bailOutVar(checks, tok.varId()); + return &tok; + } + + if (Token::simpleMatch(tok.next(), "[")) + { + ExecutionPath::bailOutVar(checks, tok.varId()); + return &tok; + } + + if (Token::Match(tok.tokAt(-2), "[,(=] *")) + { + use_pointer(checks, &tok); + return &tok; + } + + if (Token::simpleMatch(tok.previous(), "&")) + { + 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; tok2 = tok2->next()) + { + if (tok2->str() == ";") + break; + if (tok2->varId()) + varid1.insert(tok2->varId()); + } + + // 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()) + { + unsigned int indentlevel = 0; + for (const Token *tok3 = tok2->tokAt(5); tok3; tok3 = tok3->next()) + { + if (tok3->str() == "{") + ++indentlevel; + else if (tok3->str() == "}") + { + if (indentlevel == 0) + break; + --indentlevel; + } + 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, "%var% <|<=|==|!=|)")) + use(checks, &tok); + + else if (Token::Match(&tok, "!| %var% [")) + use_array_or_pointer_data(checks, tok.str() == "!" ? tok.next() : &tok); + + else if (Token::Match(&tok, "!| %var% (")) + { + std::list var; + CheckNullPointer::parseFunctionCall(tok.str() == "!" ? *tok.next() : tok, var, 1); + for (std::list::const_iterator it = var.begin(); it != var.end(); ++it) + use_array(checks, *it); + } + + else if (Token::Match(&tok, "! %var% )")) + { + 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; + const Token *next = parse(*tok, checks); + if (next) + tok = tok->next(); + } + } + +public: + + /** Functions that don't handle uninitialized variables well */ + static std::set uvarFunctions; + + static void analyseFunctions(const Token * const tokens, std::set &func) + { + for (const Token *tok = tokens; tok; tok = tok->next()) + { + if (tok->str() == "{") + { + tok = tok->link(); + continue; + } + if (tok->str() != "::" && Token::Match(tok->next(), "%var% ( %type%")) + { + if (!Token::simpleMatch(tok->tokAt(2)->link(), ") {")) + continue; + const Token *tok2 = tok->tokAt(3); + while (tok2 && tok2->str() != ")") + { + if (tok2->str() == ",") + tok2 = tok2->next(); + + if (Token::Match(tok2, "%type% %var% ,|)") && tok2->isStandardType()) + { + tok2 = tok2->tokAt(2); + continue; + } + + if (tok2->isStandardType() && Token::Match(tok2, "%type% & %var% ,|)")) + { + const unsigned int varid(tok2->tokAt(2)->varId()); + + // flags for read/write + bool r = false, w = false; + + // check how the variable is used in the function + unsigned int indentlevel = 0; + for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) + { + if (tok3->str() == "{") + ++indentlevel; + else if (tok3->str() == "}") + { + if (indentlevel <= 1) + break; + --indentlevel; + } + else if (indentlevel == 0 && tok3->str() == ";") + break; + else if (indentlevel >= 1 && tok3->varId() == varid) + { + if (Token::Match(tok3->previous(), "++|--") || + Token::Match(tok3->next(), "++|--")) + { + r = true; + } + + else + { + w = true; + break; + } + } + } + + if (!r || w) + break; + + tok2 = tok2->tokAt(3); + continue; + } + + if (Token::Match(tok2, "const %type% &|*| const| %var% ,|)") && tok2->next()->isStandardType()) + { + tok2 = tok2->tokAt(3); + while (tok2->isName()) + tok2 = tok2->next(); + continue; + } + + break; + } + + // found simple function.. + if (tok2->link() == tok->tokAt(2)) + func.insert(tok->next()->str()); + } + } + } +}; + +/** Functions that don't handle uninitialized variables well */ +std::set UninitVar::uvarFunctions; + + +/// @} + + +void CheckUninitVar::analyse(const Token * const tokens, std::set &func) const +{ + UninitVar::analyseFunctions(tokens, func); +} + +void CheckUninitVar::saveAnalysisData(const std::set &data) const +{ + UninitVar::uvarFunctions.insert(data.begin(), data.end()); +} + +void CheckUninitVar::executionPaths() +{ + // check if variable is accessed uninitialized.. + { + // no writing if multiple threads are used (TODO: thread safe analysis?) + if (_settings->_jobs == 1) + UninitVar::analyseFunctions(_tokenizer->tokens(), UninitVar::uvarFunctions); + + UninitVar c(this); + checkExecutionPaths(_tokenizer->tokens(), &c); + } +} + +void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "' (strncpy doesn't always 0-terminate it)"); +} + +void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::error, "uninitdata", "Data is allocated but not initialized: " + varname); +} + +void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::error, "uninitvar", "Uninitialized variable: " + varname); +} diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h new file mode 100644 index 000000000..58f4c4ddb --- /dev/null +++ b/lib/checkuninitvar.h @@ -0,0 +1,102 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkuninitvarH +#define checkuninitvarH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "settings.h" + +class Token; + +/// @addtogroup Checks +/// @{ + + +/** @brief Checking for uninitialized variables */ + +class CheckUninitVar : public Check +{ +public: + /** @brief This constructor is used when registering the CheckUninitVar */ + CheckUninitVar() : Check() + { } + + /** @brief This constructor is used when running checks. */ + CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(tokenizer, settings, errorLogger) + { } + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + { + (void)tokenizer; + (void)settings; + (void)errorLogger; + } + + /** @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(); + } + + /** + * @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables + * @param tokens [in] the token list + * @param func [out] names of functions that don't handle uninitialized variables well. the function names are added to the set. No clearing is made. + */ + void analyse(const Token * tokens, std::set &func) const; + + /** Save analysis results */ + void saveAnalysisData(const std::set &data) const; + + /** @brief new type of check: check execution paths */ + void executionPaths(); + + void uninitstringError(const Token *tok, const std::string &varname); + void uninitdataError(const Token *tok, const std::string &varname); + void uninitvarError(const Token *tok, const std::string &varname); + + void getErrorMessages() + { + // error + uninitstringError(0, "varname"); + uninitdataError(0, "varname"); + uninitvarError(0, "varname"); + } + + std::string name() const + { + return "Uninitialized variables"; + } + + std::string classInfo() const + { + return "Uninitialized variables\n" + "* using uninitialized variables and data\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif + diff --git a/lib/lib.pri b/lib/lib.pri index 608e130bc..93b3dc6f2 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -11,6 +11,7 @@ HEADERS += $$PWD/check.h \ $$PWD/checkother.h \ $$PWD/checkpostfixoperator.h \ $$PWD/checkstl.h \ + $$PWD/checkuninitvar.h \ $$PWD/checkunusedfunctions.h \ $$PWD/cppcheck.h \ $$PWD/errorlogger.h \ @@ -36,6 +37,7 @@ SOURCES += $$PWD/checkautovariables.cpp \ $$PWD/checkother.cpp \ $$PWD/checkpostfixoperator.cpp \ $$PWD/checkstl.cpp \ + $$PWD/checkuninitvar.cpp \ $$PWD/checkunusedfunctions.cpp \ $$PWD/cppcheck.cpp \ $$PWD/errorlogger.cpp \ diff --git a/test/testother.cpp b/test/testother.cpp index 457accadf..0cf5b4110 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -59,18 +59,6 @@ private: TEST_CASE(varScope9); // classes may have extra side-effects TEST_CASE(varScope10); // Undefined macro FOR - TEST_CASE(uninitvar1); - TEST_CASE(uninitvar_alloc); // data is allocated but not initialized - TEST_CASE(uninitvar_arrays); // arrays - TEST_CASE(uninitvar_class); // class/struct - TEST_CASE(uninitvar_enum); // enum variables - TEST_CASE(uninitvar_if); // handling if - TEST_CASE(uninitvar_loops); // handling for/while - TEST_CASE(uninitvar_switch); // handling switch - TEST_CASE(uninitvar_references); // references - TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate - TEST_CASE(uninitvar_func); // analyse functions - TEST_CASE(oldStylePointerCast); TEST_CASE(dangerousStrolUsage); @@ -583,1194 +571,6 @@ private: } - void checkUninitVar(const char code[]) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for redundant code.. - Settings settings; - CheckOther checkOther(&tokenizer, &settings, this); - checkOther.executionPaths(); - } - - void uninitvar1() - { - // dereferencing uninitialized pointer.. - checkUninitVar("static void foo()\n" - "{\n" - " Foo *p;\n" - " p->abcd();\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " Foo *p;\n" - " p->abcd();\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("void f(Foo *p)\n" - "{\n" - " int a;\n" - " p->a = malloc(4 * a);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int *p;\n" - " delete p;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int *p;\n" - " delete [] p;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int *p;\n" - " *p = 135;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int *x;\n" - " int y = *x;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int *x;\n" - " int &y(*x);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " int x;\n" - " int *y = &x;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " int x = xyz::x;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("static int foo()\n" - "{\n" - " int ret;\n" - " return ret;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: ret\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int a;\n" - " a = 5 + a;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int a;\n" - " a++;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int a;\n" - " bar(4 * a);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int i;\n" - " if (i);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int i;\n" - " for (int x = 0; i < 10; x++);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int i;\n" - " for (int x = 0; x < 10; i++);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("static int foo(int x)\n" - "{\n" - " int i;\n" - " if (x)\n" - " i = 0;\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int ar[10];\n" - " int i;\n" - " ar[i] = 0;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " int x, y;\n" - " x = (y = 10);\n" - " int z = y * 2;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " Foo p;\n" - " p.abcd();\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("static void foo()\n" - "{\n" - " Foo p;\n" - " int x = p.abcd();\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("A a()\n" - "{\n" - " A ret;\n" - " return ret;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int a()\n" - "{\n" - " int x;\n" - " return x;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " int x[10];\n" - " int *y = x;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " int x;\n" - " int *y = &x;\n" - " *y = 0;\n" - " x++;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " char x[10], y[10];\n" - " char *z = x;\n" - " memset(z, 0, sizeof(x));\n" - " memcpy(y, x, sizeof(x));\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int a()\n" - "{\n" - " int ret;\n" - " std::cin >> ret;\n" - " return ret;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int a()\n" - "{\n" - " int ret;\n" - " asm();\n" - " return ret;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " int x[10];\n" - " struct xyz xyz1 = { .x = x };\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " struct S *s;\n" - " s->x = 0;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " struct S *s;\n" - " FOREACH() { }\n" - " s->x = 0;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: s\n", errout.str()); - - checkUninitVar("void a()\n" - "{\n" - " struct S *s1;\n" - " struct S *s2;\n" - " FOREACH(s1) { }\n" - " s2->x = 0;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: s2\n", errout.str()); - - // #1533 - checkUninitVar("char a()\n" - "{\n" - " char key;\n" - " struct A msg = { .buf = {&key} };\n" - " init(&msg);\n" - " return key;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " char *buf = malloc(100);\n" - " struct ABC *abc = buf;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("class Fred {\n" - "public:\n" - " FILE *f;\n" - " ~Fred();\n" - "}\n" - "Fred::~Fred()\n" - "{\n" - " fclose(f);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int c;\n" - " ab(sizeof(xyz), &c);\n" - " if (c);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int c;\n" - " a = (f2(&c));\n" - " c++;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f(int a)\n" - "{\n" - " if (a) {\n" - " char *p;\n" - " *p = 0;\n" - " }\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: p\n", errout.str()); - - // += - checkUninitVar("void f()\n" - "{\n" - " int c;\n" - " c += 2;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *s = malloc(100);\n" - " *s += 10;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int a[10];\n" - " a[0] += 10;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - // goto/setjmp/longjmp.. - checkUninitVar("void foo(int x)\n" - "{\n" - " long b;\n" - " if (g()) {\n" - " b =2;\n" - " goto found;\n" - " }\n" - "\n" - " return;\n" - "\n" - "found:\n" - " int a = b;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " jmp_buf env;\n" - " int a;\n" - " int val = setjmp(env);\n" - " if(val)\n" - " return a;\n" - " a = 1;\n" - " longjmp(env, 1);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - // macro_for.. - checkUninitVar("int foo()\n" - "{\n" - " int retval;\n" - " if (condition) {\n" - " for12(1,2) { }\n" - " retval = 1;\n" - " }\n" - " else\n" - " retval = 2;\n" - " return retval;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " int i;\n" - " goto exit;\n" - " i++;\n" - "exit:\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - // if.. - void uninitvar_if() - { - checkUninitVar("static void foo()\n" - "{\n" - " Foo *p;\n" - " if (x)\n" - " p = new Foo;\n" - " p->abcd();\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("static void foo(int x)\n" - "{\n" - " int a;\n" - " if (x==1);\n" - " if (x==2);\n" - " x = a;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " int i;\n" - " if (x)\n" - " i = 22;\n" - " else\n" - " i = 33;\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " int i;\n" - " if (x)\n" - " i = 22;\n" - " else\n" - " {\n" - " char *y = {0};\n" - " i = 33;\n" - " }\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " int i;\n" - " if (x)\n" - " {\n" - " struct abc abc1 = (struct abc) { .a=0, .b=0, .c=0 };\n" - " i = 22;\n" - " }\n" - " else\n" - " {\n" - " i = 33;\n" - " }\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("static void foo(int x)\n" - "{\n" - " Foo *p;\n" - " if (x)\n" - " p = new Foo;\n" - " if (x)\n" - " p->abcd();\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo(int a)\n" - "{\n" - " int n;\n" - " int condition;\n" - " if(a == 1) {\n" - " n=0;\n" - " condition=0;\n" - " }\n" - " else {\n" - " n=1;\n" - " }\n" - "\n" - " if( n == 0) {\n" - " a=condition;\n" - " }\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " C *c;\n" - " if (fun(&c));\n" - " c->Release();\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo(int x)\n" - "{\n" - " int i;\n" - " if (one())\n" - " i = 1;\n" - " else\n" - " return 3;\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo()\n" - "{\n" - " int ret;\n" - " if (one())\n" - " ret = 1;\n" - " else\n" - " throw 3;\n" - " return ret;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int f(int a)\n" - "{\n" - " int ret;\n" - " if (a == 1)\n" - " ret = 1;\n" - " else\n" - " XYZ ret = 2;\n" // XYZ may be an unexpanded macro so bailout the checking of "ret". - " return ret;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int f(int a, int b)\n" - "{\n" - " int x;\n" - " if (a)\n" - " x = a;\n" - " else {\n" - " do { } while (f2());\n" - " x = b;\n" - " }\n" - " return x;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo(long verbose,bool bFlag)\n" - "{\n" - " double t;\n" - " if (bFlag)\n" - " {\n" - " if (verbose)\n" - " t = 1;\n" - " if (verbose)\n" - " std::cout << (12-t);\n" - " }\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - // ? : - checkUninitVar("static void foo(int v)\n" - "{\n" - " int x;\n" - " if (v > 0)\n" - " v = func(&x);\n" - " x = v <= 0 ? -1 : x;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " const char *msgid1, *msgid2;\n" - " int ret = bar(&msgid1);\n" - " if (ret > 0) {\n" - " ret = bar(&msgid2);\n" - " }\n" - " ret = ret <= 0 ? -1 :\n" - " strcmp(msgid1, msgid2) == 0;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo(int a, int b)\n" - "{\n" - " int x; x = (anext())\n" - " {\n" - " }\n" - " } while (tok2);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - // while.. - checkUninitVar("int f()\n" - "{\n" - " int i;\n" - " while (fgets())\n" - " i = 1;\n" - " return i;" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); - - checkUninitVar("void f(int i)\n" - "{\n" - " int a;\n" - " while (i < 10)\n" - " i++;\n" - " a++;" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); - } - - // switch.. - void uninitvar_switch() - { - checkUninitVar("void f(int x)\n" - "{\n" - " short c;\n" - " switch(x) {\n" - " case 1:\n" - " c++;\n" - " break;\n" - " };\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: c\n", errout.str()); - - checkUninitVar("char * f()\n" - "{\n" - " static char ret[200];\n" - " memset(ret, 0, sizeof(ret));\n" - " switch (x)\n" - " {\n" - " case 1: return ret;\n" - " case 2: return ret;\n" - " }\n" - " return 0;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo(const int iVar, unsigned int slot, unsigned int pin)\n" - "{\n" - " int i;\n" - "\n" - " if (iVar == 0)\n" - " {\n" - " switch (slot)\n" - " {\n" - " case 4: return 5;\n" - " default: return -1;\n" - " }\n" - " }\n" - " else\n" - " {\n" - " switch (pin)\n" - " {\n" - " case 0: i = 2; break;\n" - " default: i = -1; break;\n" - " }\n" - " }\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - // No segmentation fault - checkUninitVar("void a() try\n" - "{\n" - " {\n" - " while (1) {\n" - " switch (1) {\n" - " case 1: {\n" - " int i;\n" - " }\n" - " }\n" - " }\n" - " } catch (...) {\n" - " }\n" - "}\n"); - - // #1855 - switch(foo(&x)) - checkUninitVar("int a()\n" - "{\n" - " int x;\n" - " switch (foo(&x))\n" - " {\n" - " case 1:\n" - " return x;\n" - " }\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - // arrays.. - void uninitvar_arrays() - { - checkUninitVar("int f()\n" - "{\n" - " char a[10];\n" - " a[a[0]] = 0;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("int f()\n" - "{\n" - " char a[10];\n" - " char c = *a;\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("int f()\n" - "{\n" - " char a[10];\n" - " *a = '\\0';\n" - " int i = strlen(a);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char a, b[10];\n" - " a = b[0] = 0;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char a[10], b[10];\n" - " a[0] = b[0] = 0;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char a[10], *p;\n" - " *(p = a) = 0;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char c[50] = \"\";\n" - " strcat(c, \"test\");\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char s[20];\n" - " strcpy(s2, s);\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char s[20];\n" - " strcat(s, \"abc\");\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char s[20];\n" - " strchr(s, ' ');\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " int y[2];\n" - " int s;\n" - " GetField( y + 0, \n" - " y + 1 );\n" - " s = y[0]*y[1];\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - // alloc.. - void uninitvar_alloc() - { - checkUninitVar("void f()\n" - "{\n" - " char *s = malloc(100);\n" - " strcat(s, \"abc\");\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *s1 = new char[10];\n" - " char *s2 = new char[strlen(s1)];\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s1\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *p = malloc(64);\n" - " int x = p[0];\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *p = malloc(64);\n" - " if (p[0]) { }\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *p = malloc(64);\n" - " return p[0];\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " Fred *fred = new Fred;\n" - " fred->foo();\n" - "};\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo(char *s)\n" - "{\n" - " char *a = malloc(100);\n" - " *a = *s;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " char *a;\n" - " if (a);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " char *a = malloc(100);\n" - " if (a);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " ABC *abc = malloc(100);\n" - " abc->a = 123;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " ABC *abc = malloc(100);\n" - " abc->a.word = 123;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " ABC *abc = malloc(100);\n" - " abc->a = 123;\n" - " abc->a += 123;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " ABC *abc = malloc(100);\n" - " free(abc);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char *s = malloc(100);\n" - " if (!s)\n" - " return;\n" - " char c = *s;\n" - "};\n"); - ASSERT_EQUALS("[test.cpp:6]: (error) Data is allocated but not initialized: s\n", errout.str()); - } - - // class / struct.. - void uninitvar_class() - { - checkUninitVar("class Fred\n" - "{\n" - " int i;\n" - " int a() { return i; }\n" - "};\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " struct Relative {\n" - " Surface *surface;\n" - " void MoveTo(int x, int y) {\n" - " surface->MoveTo();\n" - " }\n" - " };\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " static const struct ab {\n" - " int a,b;\n" - " int get_a() { return a; }" - " } = { 0, 0 };\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " int i;\n" - " {\n" - " union ab {\n" - " int a,b;\n" - " }\n" - " i = 0;\n" - " }\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - // enum.. - void uninitvar_enum() - { - checkUninitVar("void f()\n" - "{\n" - " enum AB { a, b };\n" - " AB ab;\n" - " if (ab);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: ab\n", errout.str()); - } - - // references.. - void uninitvar_references() - { - checkUninitVar("void f()\n" - "{\n" - " int a;\n" - " int &b = a;\n" - " b = 0;\n" - " int x = a;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f(struct blame_entry *ent)\n" - "{\n" - " struct origin *suspect = ent->suspect;\n" - " char hex[41];\n" - " strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " const std::string s(x());\n" - " strchr(s.c_str(), ',');\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - // strncpy doesn't always 0-terminate.. - void uninitvar_strncpy() - { - checkUninitVar("void f()\n" - "{\n" - " char a[100];\n" - " strncpy(a, s, 20);\n" - " strncat(a, s, 20);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always 0-terminate it)\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char a[100];\n" - " strncpy(a, \"hello\", 3);\n" - " strncat(a, \"world\", 20);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always 0-terminate it)\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " char a[100];\n" - " strncpy(a, \"hello\", sizeof(a));\n" - " strncat(a, \"world\", 20);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - - std::string analyseFunctions(const char code[]) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - std::set f; - const CheckOther checkOther((const Tokenizer *)0, (const Settings *)0, (ErrorLogger *)0); - checkOther.analyse(tokenizer.tokens(), f); - - std::string ret; - for (std::set::const_iterator it = f.begin(); it != f.end(); ++it) - ret += (ret.empty() ? "" : " ") + *it; - return ret; - } - - void uninitvar_func() - { - // function analysis.. - ASSERT_EQUALS("foo", analyseFunctions("void foo(int x) { }")); - ASSERT_EQUALS("foo", analyseFunctions("void foo(const int &x) { }")); - ASSERT_EQUALS("foo", analyseFunctions("void foo(int &x) { ++x; }")); - ASSERT_EQUALS("", analyseFunctions("void foo(int &x) { x = 0; }")); - ASSERT_EQUALS("", analyseFunctions("void foo(s x) { }")); - - // function calls.. - checkUninitVar("void assignOne(int &x)\n" - "{ x = 1; }\n" - "\n" - "int f()\n" - "{\n" - " int i;\n" - " assignOne(i);\n" - " return i;\n" - "};\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int f(int (*assign)(int *p))\n" - "{\n" - " int i;\n" - " (*assign)(&i);\n" - " return i;\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int f()\n" - "{\n" - " char s[10];\n" - " return bar(s);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " FILE *f;\n" - " fflush(f);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); - - checkUninitVar("void f()\n" - "{\n" - " Abc *p;\n" - " int sz = sizeof(*p);\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " Foo *p;\n" - " x = bar(sizeof(*p));\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void foo()\n" - "{\n" - " Foo *p;\n" - " x = bar(p->begin());\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("int foo(int x) { return x; }\n" - "void f2()\n" - "{\n" - " int x;\n" - " foo(x);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); - - checkUninitVar("void foo(const char *s)\n" - "{\n" - " char *p;\n" - " memcpy(p, s, 100);\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); - - checkUninitVar("void foo(const char *s)\n" - "{\n" - " char *p = malloc(100);\n" - " memcpy(p, s, 100);\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - - // using uninitialized function pointer.. - checkUninitVar("void foo()\n" - "{\n" - " void (*f)();\n" - " f();\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); - - // calling noreturn function.. - checkUninitVar("int foo(int a) {\n" - " int x;\n" - " if (a==1)\n" - " g();\n" // might be a noreturn function - " else\n" - " x = 3;\n" - " return x;\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("int foo(int a) {\n" - " int x;\n" - " if (a==1)\n" - " g(1);\n" // might be a noreturn function - " else\n" - " x = 3;\n" - " return x;\n" - "}"); - ASSERT_EQUALS("", errout.str()); - - checkUninitVar("void (*init)(char *str);\n" - "\n" - "char x() {\n" - " char cmd[10];\n" - " init(cmd);\n" - " return cmd[0];\n" - "}\n"); - ASSERT_EQUALS("", errout.str()); - } - - void checkOldStylePointerCast(const char code[]) { // Tokenize.. diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp new file mode 100644 index 000000000..1cb3074f9 --- /dev/null +++ b/test/testuninitvar.cpp @@ -0,0 +1,1239 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2010 Daniel Marjamäki and Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tokenize.h" +#include "checkuninitvar.h" +#include "testsuite.h" +#include + +extern std::ostringstream errout; + +class TestUninitVar : public TestFixture +{ +public: + TestUninitVar() : TestFixture("TestUninitVar") + { } + +private: + + + void run() + { + TEST_CASE(uninitvar1); + TEST_CASE(uninitvar_alloc); // data is allocated but not initialized + TEST_CASE(uninitvar_arrays); // arrays + TEST_CASE(uninitvar_class); // class/struct + TEST_CASE(uninitvar_enum); // enum variables + TEST_CASE(uninitvar_if); // handling if + TEST_CASE(uninitvar_loops); // handling for/while + TEST_CASE(uninitvar_switch); // handling switch + TEST_CASE(uninitvar_references); // references + TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate + TEST_CASE(uninitvar_func); // analyse functions + } + + void checkUninitVar(const char code[]) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for redundant code.. + Settings settings; + CheckUninitVar check(&tokenizer, &settings, this); + check.executionPaths(); + } + + void uninitvar1() + { + // dereferencing uninitialized pointer.. + checkUninitVar("static void foo()\n" + "{\n" + " Foo *p;\n" + " p->abcd();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " Foo *p;\n" + " p->abcd();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("void f(Foo *p)\n" + "{\n" + " int a;\n" + " p->a = malloc(4 * a);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int *p;\n" + " delete p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int *p;\n" + " delete [] p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int *p;\n" + " *p = 135;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int *x;\n" + " int y = *x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int *x;\n" + " int &y(*x);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " int x;\n" + " int *y = &x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " int x = xyz::x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("static int foo()\n" + "{\n" + " int ret;\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: ret\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int a;\n" + " a = 5 + a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int a;\n" + " a++;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int a;\n" + " bar(4 * a);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int i;\n" + " if (i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int i;\n" + " for (int x = 0; i < 10; x++);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int i;\n" + " for (int x = 0; x < 10; i++);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("static int foo(int x)\n" + "{\n" + " int i;\n" + " if (x)\n" + " i = 0;\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int ar[10];\n" + " int i;\n" + " ar[i] = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " int x, y;\n" + " x = (y = 10);\n" + " int z = y * 2;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " Foo p;\n" + " p.abcd();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("static void foo()\n" + "{\n" + " Foo p;\n" + " int x = p.abcd();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("A a()\n" + "{\n" + " A ret;\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int a()\n" + "{\n" + " int x;\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " int x[10];\n" + " int *y = x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " int x;\n" + " int *y = &x;\n" + " *y = 0;\n" + " x++;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " char x[10], y[10];\n" + " char *z = x;\n" + " memset(z, 0, sizeof(x));\n" + " memcpy(y, x, sizeof(x));\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int a()\n" + "{\n" + " int ret;\n" + " std::cin >> ret;\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int a()\n" + "{\n" + " int ret;\n" + " asm();\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " int x[10];\n" + " struct xyz xyz1 = { .x = x };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " struct S *s;\n" + " s->x = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " struct S *s;\n" + " FOREACH() { }\n" + " s->x = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: s\n", errout.str()); + + checkUninitVar("void a()\n" + "{\n" + " struct S *s1;\n" + " struct S *s2;\n" + " FOREACH(s1) { }\n" + " s2->x = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: s2\n", errout.str()); + + // #1533 + checkUninitVar("char a()\n" + "{\n" + " char key;\n" + " struct A msg = { .buf = {&key} };\n" + " init(&msg);\n" + " return key;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " char *buf = malloc(100);\n" + " struct ABC *abc = buf;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("class Fred {\n" + "public:\n" + " FILE *f;\n" + " ~Fred();\n" + "}\n" + "Fred::~Fred()\n" + "{\n" + " fclose(f);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int c;\n" + " ab(sizeof(xyz), &c);\n" + " if (c);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int c;\n" + " a = (f2(&c));\n" + " c++;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f(int a)\n" + "{\n" + " if (a) {\n" + " char *p;\n" + " *p = 0;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: p\n", errout.str()); + + // += + checkUninitVar("void f()\n" + "{\n" + " int c;\n" + " c += 2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: c\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *s = malloc(100);\n" + " *s += 10;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int a[10];\n" + " a[0] += 10;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + // goto/setjmp/longjmp.. + checkUninitVar("void foo(int x)\n" + "{\n" + " long b;\n" + " if (g()) {\n" + " b =2;\n" + " goto found;\n" + " }\n" + "\n" + " return;\n" + "\n" + "found:\n" + " int a = b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " jmp_buf env;\n" + " int a;\n" + " int val = setjmp(env);\n" + " if(val)\n" + " return a;\n" + " a = 1;\n" + " longjmp(env, 1);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // macro_for.. + checkUninitVar("int foo()\n" + "{\n" + " int retval;\n" + " if (condition) {\n" + " for12(1,2) { }\n" + " retval = 1;\n" + " }\n" + " else\n" + " retval = 2;\n" + " return retval;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " int i;\n" + " goto exit;\n" + " i++;\n" + "exit:\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // if.. + void uninitvar_if() + { + checkUninitVar("static void foo()\n" + "{\n" + " Foo *p;\n" + " if (x)\n" + " p = new Foo;\n" + " p->abcd();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("static void foo(int x)\n" + "{\n" + " int a;\n" + " if (x==1);\n" + " if (x==2);\n" + " x = a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " int i;\n" + " if (x)\n" + " i = 22;\n" + " else\n" + " i = 33;\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " int i;\n" + " if (x)\n" + " i = 22;\n" + " else\n" + " {\n" + " char *y = {0};\n" + " i = 33;\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " int i;\n" + " if (x)\n" + " {\n" + " struct abc abc1 = (struct abc) { .a=0, .b=0, .c=0 };\n" + " i = 22;\n" + " }\n" + " else\n" + " {\n" + " i = 33;\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("static void foo(int x)\n" + "{\n" + " Foo *p;\n" + " if (x)\n" + " p = new Foo;\n" + " if (x)\n" + " p->abcd();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo(int a)\n" + "{\n" + " int n;\n" + " int condition;\n" + " if(a == 1) {\n" + " n=0;\n" + " condition=0;\n" + " }\n" + " else {\n" + " n=1;\n" + " }\n" + "\n" + " if( n == 0) {\n" + " a=condition;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " C *c;\n" + " if (fun(&c));\n" + " c->Release();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo(int x)\n" + "{\n" + " int i;\n" + " if (one())\n" + " i = 1;\n" + " else\n" + " return 3;\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo()\n" + "{\n" + " int ret;\n" + " if (one())\n" + " ret = 1;\n" + " else\n" + " throw 3;\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int f(int a)\n" + "{\n" + " int ret;\n" + " if (a == 1)\n" + " ret = 1;\n" + " else\n" + " XYZ ret = 2;\n" // XYZ may be an unexpanded macro so bailout the checking of "ret". + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int f(int a, int b)\n" + "{\n" + " int x;\n" + " if (a)\n" + " x = a;\n" + " else {\n" + " do { } while (f2());\n" + " x = b;\n" + " }\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo(long verbose,bool bFlag)\n" + "{\n" + " double t;\n" + " if (bFlag)\n" + " {\n" + " if (verbose)\n" + " t = 1;\n" + " if (verbose)\n" + " std::cout << (12-t);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // ? : + checkUninitVar("static void foo(int v)\n" + "{\n" + " int x;\n" + " if (v > 0)\n" + " v = func(&x);\n" + " x = v <= 0 ? -1 : x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " const char *msgid1, *msgid2;\n" + " int ret = bar(&msgid1);\n" + " if (ret > 0) {\n" + " ret = bar(&msgid2);\n" + " }\n" + " ret = ret <= 0 ? -1 :\n" + " strcmp(msgid1, msgid2) == 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo(int a, int b)\n" + "{\n" + " int x; x = (anext())\n" + " {\n" + " }\n" + " } while (tok2);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // while.. + checkUninitVar("int f()\n" + "{\n" + " int i;\n" + " while (fgets())\n" + " i = 1;\n" + " return i;" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str()); + + checkUninitVar("void f(int i)\n" + "{\n" + " int a;\n" + " while (i < 10)\n" + " i++;\n" + " a++;" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: a\n", errout.str()); + } + + // switch.. + void uninitvar_switch() + { + checkUninitVar("void f(int x)\n" + "{\n" + " short c;\n" + " switch(x) {\n" + " case 1:\n" + " c++;\n" + " break;\n" + " };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: c\n", errout.str()); + + checkUninitVar("char * f()\n" + "{\n" + " static char ret[200];\n" + " memset(ret, 0, sizeof(ret));\n" + " switch (x)\n" + " {\n" + " case 1: return ret;\n" + " case 2: return ret;\n" + " }\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo(const int iVar, unsigned int slot, unsigned int pin)\n" + "{\n" + " int i;\n" + "\n" + " if (iVar == 0)\n" + " {\n" + " switch (slot)\n" + " {\n" + " case 4: return 5;\n" + " default: return -1;\n" + " }\n" + " }\n" + " else\n" + " {\n" + " switch (pin)\n" + " {\n" + " case 0: i = 2; break;\n" + " default: i = -1; break;\n" + " }\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // No segmentation fault + checkUninitVar("void a() try\n" + "{\n" + " {\n" + " while (1) {\n" + " switch (1) {\n" + " case 1: {\n" + " int i;\n" + " }\n" + " }\n" + " }\n" + " } catch (...) {\n" + " }\n" + "}\n"); + + // #1855 - switch(foo(&x)) + checkUninitVar("int a()\n" + "{\n" + " int x;\n" + " switch (foo(&x))\n" + " {\n" + " case 1:\n" + " return x;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // arrays.. + void uninitvar_arrays() + { + checkUninitVar("int f()\n" + "{\n" + " char a[10];\n" + " a[a[0]] = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("int f()\n" + "{\n" + " char a[10];\n" + " char c = *a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("int f()\n" + "{\n" + " char a[10];\n" + " *a = '\\0';\n" + " int i = strlen(a);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char a, b[10];\n" + " a = b[0] = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char a[10], b[10];\n" + " a[0] = b[0] = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char a[10], *p;\n" + " *(p = a) = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char c[50] = \"\";\n" + " strcat(c, \"test\");\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char s[20];\n" + " strcpy(s2, s);\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char s[20];\n" + " strcat(s, \"abc\");\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char s[20];\n" + " strchr(s, ' ');\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " int y[2];\n" + " int s;\n" + " GetField( y + 0, \n" + " y + 1 );\n" + " s = y[0]*y[1];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // alloc.. + void uninitvar_alloc() + { + checkUninitVar("void f()\n" + "{\n" + " char *s = malloc(100);\n" + " strcat(s, \"abc\");\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *s1 = new char[10];\n" + " char *s2 = new char[strlen(s1)];\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: s1\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *p = malloc(64);\n" + " int x = p[0];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *p = malloc(64);\n" + " if (p[0]) { }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *p = malloc(64);\n" + " return p[0];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Data is allocated but not initialized: p\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " Fred *fred = new Fred;\n" + " fred->foo();\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo(char *s)\n" + "{\n" + " char *a = malloc(100);\n" + " *a = *s;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " char *a;\n" + " if (a);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " char *a = malloc(100);\n" + " if (a);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " ABC *abc = malloc(100);\n" + " abc->a = 123;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " ABC *abc = malloc(100);\n" + " abc->a.word = 123;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " ABC *abc = malloc(100);\n" + " abc->a = 123;\n" + " abc->a += 123;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " ABC *abc = malloc(100);\n" + " free(abc);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char *s = malloc(100);\n" + " if (!s)\n" + " return;\n" + " char c = *s;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Data is allocated but not initialized: s\n", errout.str()); + } + + // class / struct.. + void uninitvar_class() + { + checkUninitVar("class Fred\n" + "{\n" + " int i;\n" + " int a() { return i; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " struct Relative {\n" + " Surface *surface;\n" + " void MoveTo(int x, int y) {\n" + " surface->MoveTo();\n" + " }\n" + " };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " static const struct ab {\n" + " int a,b;\n" + " int get_a() { return a; }" + " } = { 0, 0 };\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " int i;\n" + " {\n" + " union ab {\n" + " int a,b;\n" + " }\n" + " i = 0;\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // enum.. + void uninitvar_enum() + { + checkUninitVar("void f()\n" + "{\n" + " enum AB { a, b };\n" + " AB ab;\n" + " if (ab);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: ab\n", errout.str()); + } + + // references.. + void uninitvar_references() + { + checkUninitVar("void f()\n" + "{\n" + " int a;\n" + " int &b = a;\n" + " b = 0;\n" + " int x = a;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f(struct blame_entry *ent)\n" + "{\n" + " struct origin *suspect = ent->suspect;\n" + " char hex[41];\n" + " strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " const std::string s(x());\n" + " strchr(s.c_str(), ',');\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // strncpy doesn't always 0-terminate.. + void uninitvar_strncpy() + { + checkUninitVar("void f()\n" + "{\n" + " char a[100];\n" + " strncpy(a, s, 20);\n" + " strncat(a, s, 20);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always 0-terminate it)\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char a[100];\n" + " strncpy(a, \"hello\", 3);\n" + " strncat(a, \"world\", 20);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Dangerous usage of 'a' (strncpy doesn't always 0-terminate it)\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " char a[100];\n" + " strncpy(a, \"hello\", sizeof(a));\n" + " strncat(a, \"world\", 20);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + + std::string analyseFunctions(const char code[]) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + std::set f; + const CheckUninitVar check((const Tokenizer *)0, (const Settings *)0, (ErrorLogger *)0); + check.analyse(tokenizer.tokens(), f); + + std::string ret; + for (std::set::const_iterator it = f.begin(); it != f.end(); ++it) + ret += (ret.empty() ? "" : " ") + *it; + return ret; + } + + void uninitvar_func() + { + // function analysis.. + ASSERT_EQUALS("foo", analyseFunctions("void foo(int x) { }")); + ASSERT_EQUALS("foo", analyseFunctions("void foo(const int &x) { }")); + ASSERT_EQUALS("foo", analyseFunctions("void foo(int &x) { ++x; }")); + ASSERT_EQUALS("", analyseFunctions("void foo(int &x) { x = 0; }")); + ASSERT_EQUALS("", analyseFunctions("void foo(s x) { }")); + + // function calls.. + checkUninitVar("void assignOne(int &x)\n" + "{ x = 1; }\n" + "\n" + "int f()\n" + "{\n" + " int i;\n" + " assignOne(i);\n" + " return i;\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int f(int (*assign)(int *p))\n" + "{\n" + " int i;\n" + " (*assign)(&i);\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int f()\n" + "{\n" + " char s[10];\n" + " return bar(s);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " FILE *f;\n" + " fflush(f);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); + + checkUninitVar("void f()\n" + "{\n" + " Abc *p;\n" + " int sz = sizeof(*p);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " Foo *p;\n" + " x = bar(sizeof(*p));\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void foo()\n" + "{\n" + " Foo *p;\n" + " x = bar(p->begin());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("int foo(int x) { return x; }\n" + "void f2()\n" + "{\n" + " int x;\n" + " foo(x);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str()); + + checkUninitVar("void foo(const char *s)\n" + "{\n" + " char *p;\n" + " memcpy(p, s, 100);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: p\n", errout.str()); + + checkUninitVar("void foo(const char *s)\n" + "{\n" + " char *p = malloc(100);\n" + " memcpy(p, s, 100);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + // using uninitialized function pointer.. + checkUninitVar("void foo()\n" + "{\n" + " void (*f)();\n" + " f();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: f\n", errout.str()); + + // calling noreturn function.. + checkUninitVar("int foo(int a) {\n" + " int x;\n" + " if (a==1)\n" + " g();\n" // might be a noreturn function + " else\n" + " x = 3;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("int foo(int a) {\n" + " int x;\n" + " if (a==1)\n" + " g(1);\n" // might be a noreturn function + " else\n" + " x = 3;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + checkUninitVar("void (*init)(char *str);\n" + "\n" + "char x() {\n" + " char cmd[10];\n" + " init(cmd);\n" + " return cmd[0];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } +}; + +REGISTER_TEST(TestUninitVar) +