diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 85963c36e..ecd7aac8c 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -992,7 +992,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector 0) { // No varid => function calls are not handled if (varid == 0) @@ -1405,7 +1405,7 @@ void CheckBufferOverrun::checkStructVariable() const std::string &structname = tok->next()->str(); const Token *tok2 = tok; - while (tok2->str() != "{") + while (tok2 && tok2->str() != "{") tok2 = tok2->next(); // Found a struct declaration. Search for arrays.. diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index ddbef4288..4d19f2c91 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -640,9 +640,7 @@ void CheckClass::privateFunctions() // Check that all private functions are used.. for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { - const Token *ftok = func->arg->link()->next(); - while (ftok->str() != "{") - ftok = ftok->next(); + const Token *ftok = func->start; const Token *etok = ftok->link(); for (; ftok != etok; ftok = ftok->next()) @@ -869,10 +867,12 @@ void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, co } } - // check of *this is returned + // check if *this is returned else if (!(Token::Match(tok->tokAt(1), "(| * this ;|=") || Token::Match(tok->tokAt(1), "(| * this +=") || - Token::simpleMatch(tok->tokAt(1), "operator= ("))) + Token::simpleMatch(tok->tokAt(1), "operator= (") || + (Token::Match(tok->tokAt(1), "%type% :: operator= (") && + tok->next()->str() == scope->className))) operatorEqRetRefThisError(func->token); } } @@ -1351,7 +1351,7 @@ bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) { const Token *tok1 = tok; - while (tok->previous() && !Token::Match(tok->previous(), "}|{|;|public:|protected:|private:|return|:|?")) + while (tok->previous() && !Token::Match(tok->previous(), "}|{|;(||public:|protected:|private:|return|:|?")) { if (Token::simpleMatch(tok->previous(), "* this")) return true; @@ -1489,6 +1489,12 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Token *tok) isconst = false; break; } + else if (Token::simpleMatch(tok1->previous(), ") <<") && + isMemberVar(scope, tok1->tokAt(-2))) + { + isconst = false; + break; + } // increment/decrement (member variable?).. else if (Token::Match(tok1, "++|--")) diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 698476b12..6241dceae 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -350,8 +350,19 @@ void CheckNullPointer::nullPointerStructByDeRefAndChec() continue; } + /** + * @todo There are lots of false negatives here. A dereference + * is only investigated if a few specific conditions are met. + */ + // dereference in assignment - if (Token::Match(tok1, "[{};] %var% = %var% . %var%")) + if (Token::Match(tok1, "[;{}] %var% . %var%")) + { + tok1 = tok1->next(); + } + + // dereference in assignment + else if (Token::Match(tok1, "[{};] %var% = %var% . %var%")) { if (std::string(tok1->strAt(1)) == tok1->strAt(3)) continue; @@ -433,8 +444,8 @@ void CheckNullPointer::nullPointerStructByDeRefAndChec() break; // Check if pointer is null. - // TODO: false negatives for something like: "if (p &&.."? - else if (Token::Match(tok2, "if ( !| %varid% )", varid1)) + // TODO: false negatives for "if (!p || .." + else if (Token::Match(tok2, "if ( !| %varid% )|&&", varid1)) { // Is this variable a pointer? if (isPointer(varid1)) @@ -563,14 +574,13 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() // vartok : token for the variable const Token *vartok = 0; - if (Token::Match(tok, "if ( ! %var% ) {")) + if (Token::Match(tok, "if ( ! %var% )|&&")) vartok = tok->tokAt(3); - else if (Token::Match(tok, "if ( NULL|0 == %var% ) {")) + else if (Token::Match(tok, "if ( NULL|0 == %var% )|&&")) vartok = tok->tokAt(4); - else if (Token::Match(tok, "if ( %var% == NULL|0 ) {")) + else if (Token::Match(tok, "if ( %var% == NULL|0 )|&&")) vartok = tok->tokAt(2); - else if (Token::Match(tok, "if|while ( %var% ) {") && - !Token::simpleMatch(tok->tokAt(4)->link(), "} else")) + else if (Token::Match(tok, "if|while ( %var% )|&&")) vartok = tok->tokAt(2); else continue; @@ -590,7 +600,7 @@ void CheckNullPointer::nullPointerByCheckAndDeRef() // start token = inside the if-body const Token *tok1 = tok->next()->link()->tokAt(2); - if (Token::Match(tok, "if|while ( %var% )")) + if (Token::Match(tok, "if|while ( %var% )|&&")) { // pointer might be null null = false; diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 233f562a8..07132a2d0 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -321,7 +321,7 @@ void CheckOther::checkSelfAssignment() std::set pod; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { - if (tok->isStandardType() && tok->next()->varId() && Token::Match(tok->tokAt(2), "[,);]")) + if (tok->isStandardType() && Token::Match(tok->tokAt(2), "[,);]") && tok->next()->varId()) pod.insert(tok->next()->varId()); } diff --git a/lib/executionpath.cpp b/lib/executionpath.cpp index 7e8b6c4c7..774b9c28d 100644 --- a/lib/executionpath.cpp +++ b/lib/executionpath.cpp @@ -275,6 +275,40 @@ void ExecutionPath::checkScope(const Token *tok, std::list &che ++it; } + // #2231 - loop body only contains a conditional initialization.. + if (Token::simpleMatch(tok2->next(), "if (")) + { + // Start { for the if block + const Token *tok3 = tok2->tokAt(2)->link(); + if (Token::simpleMatch(tok3,") {")) + { + tok3 = tok3->next(); + + // End } for the if block + const Token *tok4 = tok3->link(); + if (Token::Match(tok3, "{ %var% =") && + Token::simpleMatch(tok4, "} }") && + Token::simpleMatch(tok4->tokAt(-2), "break ;")) + { + // Is there a assignment and then a break? + const Token *t = Token::findmatch(tok3, ";"); + if (t && t->tokAt(3) == tok4) + { + for (std::list::iterator it = checks.begin(); it != checks.end(); ++it) + { + if ((*it)->varId == tok3->next()->varId()) + { + (*it)->numberOfIf++; + break; + } + } + tok = tok2->link(); + continue; + } + } + } + } + // parse loop bodies check->parseLoopBody(tok2->next(), checks); } diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index fe35a7f81..2f5c33795 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -338,20 +338,32 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri continue; } - // We have finished a line that didn't contain any comment - // (the '\n' is swallowed when a // comment is detected) - if ((ch == '\n' || str.compare(i,2,"//")==0) && !suppressionIDs.empty()) + // First skip over any whitespace that may be present + if (std::isspace(ch)) { - // Add the suppressions. - for (size_t j(0); j < suppressionIDs.size(); ++j) + if (ch == ' ' && previous == ' ') { - const std::string errmsg(settings->nomsg.addSuppression(suppressionIDs[j], filename, lineno)); - if (!errmsg.empty()) + // Skip double white space + } + else + { + code << char(ch); + previous = ch; + } + + // if there has been sequences, add extra newlines.. + if (ch == '\n') + { + ++lineno; + if (newlines > 0) { - writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg); + code << std::string(newlines, '\n'); + newlines = 0; + previous = '\n'; } } - suppressionIDs.clear(); + + continue; } // Remove comments.. @@ -382,6 +394,7 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri } else if (str.compare(i, 2, "/*", 0, 2) == 0) { + size_t commentStart = i + 2; unsigned char chPrev = 0; ++i; while (i < str.length() && (chPrev != '*' || ch != '/')) @@ -395,126 +408,139 @@ std::string Preprocessor::removeComments(const std::string &str, const std::stri ++lineno; } } - } - // String or char constants.. - else if (ch == '\"' || ch == '\'') - { - code << char(ch); - char chNext; - do + if (settings && settings->_inlineSuppressions) { - ++i; - chNext = str[i]; - if (chNext == '\\') + std::string comment(str, commentStart, i - commentStart); + std::istringstream iss(comment); + std::string word; + iss >> word; + if (word == "cppcheck-suppress") + { + iss >> word; + if (iss) + suppressionIDs.push_back(word); + } + } + } + else + { + // Not whitespace and not a comment. Must be code here! + // Add any pending inline suppressions that have accumulated. + if (!suppressionIDs.empty()) + { + if (settings != NULL) + { + // Add the suppressions. + for (size_t j(0); j < suppressionIDs.size(); ++j) + { + const std::string errmsg(settings->nomsg.addSuppression(suppressionIDs[j], filename, lineno)); + if (!errmsg.empty()) + { + writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg); + } + } + } + suppressionIDs.clear(); + } + + // String or char constants.. + if (ch == '\"' || ch == '\'') + { + code << char(ch); + char chNext; + do { ++i; - char chSeq = str[i]; - if (chSeq == '\n') - ++newlines; + chNext = str[i]; + if (chNext == '\\') + { + ++i; + char chSeq = str[i]; + if (chSeq == '\n') + ++newlines; + else + { + code << chNext; + code << chSeq; + previous = static_cast(chSeq); + } + } else { code << chNext; - code << chSeq; - previous = static_cast(chSeq); + previous = static_cast(chNext); } } + while (i < str.length() && chNext != ch && chNext != '\n'); + } + + // Rawstring.. + else if (str.compare(i,2,"R\"")==0) + { + std::string delim; + for (std::string::size_type i2 = i+2; i2 < str.length(); ++i2) + { + if (i2 > 16 || + std::isspace(str[i2]) || + std::iscntrl(str[i2]) || + str[i2] == ')' || + str[i2] == '\\') + { + delim = " "; + break; + } + else if (str[i2] == '(') + break; + + delim += str[i2]; + } + const std::string::size_type endpos = str.find(")" + delim + "\"", i); + if (delim != " " && endpos != std::string::npos) + { + unsigned int rawstringnewlines = 0; + code << '\"'; + for (std::string::size_type p = i + 3 + delim.size(); p < endpos; ++p) + { + if (str[p] == '\n') + { + rawstringnewlines++; + code << "\\n"; + } + else if (std::iscntrl((unsigned char)str[p]) || + std::isspace((unsigned char)str[p])) + { + code << " "; + } + else if (str[p] == '\\') + { + code << "\\"; + } + else if (str[p] == '\"' || str[p] == '\'') + { + code << "\\" << (char)str[p]; + } + else + { + code << (char)str[p]; + } + } + code << "\""; + if (rawstringnewlines > 0) + code << std::string(rawstringnewlines, '\n'); + i = endpos + delim.size() + 2; + } else { - code << chNext; - previous = static_cast(chNext); + code << "R"; + previous = 'R'; } } - while (i < str.length() && chNext != ch && chNext != '\n'); - } - - // Rawstring.. - else if (str.compare(i,2,"R\"")==0) - { - std::string delim; - for (std::string::size_type i2 = i+2; i2 < str.length(); ++i2) - { - if (i2 > 16 || - std::isspace(str[i2]) || - std::iscntrl(str[i2]) || - str[i2] == ')' || - str[i2] == '\\') - { - delim = " "; - break; - } - else if (str[i2] == '(') - break; - - delim += str[i2]; - } - const std::string::size_type endpos = str.find(")" + delim + "\"", i); - if (delim != " " && endpos != std::string::npos) - { - unsigned int rawstringnewlines = 0; - code << '\"'; - for (std::string::size_type p = i + 3 + delim.size(); p < endpos; ++p) - { - if (str[p] == '\n') - { - rawstringnewlines++; - code << "\\n"; - } - else if (std::iscntrl((unsigned char)str[p]) || - std::isspace((unsigned char)str[p])) - { - code << " "; - } - else if (str[p] == '\\') - { - code << "\\"; - } - else if (str[p] == '\"' || str[p] == '\'') - { - code << "\\" << (char)str[p]; - } - else - { - code << (char)str[p]; - } - } - code << "\""; - if (rawstringnewlines > 0) - code << std::string(rawstringnewlines, '\n'); - i = endpos + delim.size() + 2; - } - else - { - code << "R"; - previous = 'R'; - } - } - - // Just some code.. - else - { - if (ch == ' ' && previous == ' ') - { - // Skip double white space - } else { code << char(ch); previous = ch; } - - - // if there has been sequences, add extra newlines.. - if (ch == '\n') - { - ++lineno; - if (newlines > 0) - { - code << std::string(newlines, '\n'); - newlines = 0; - previous = '\n'; - } - } } } @@ -1304,7 +1330,33 @@ void Preprocessor::simplifyCondition(const std::map &v if (it != variables.end()) { if (!it->second.empty()) - tok->str(it->second); + { + // Tokenize the value + Tokenizer tokenizer2(&settings,NULL); + std::istringstream istr2(it->second); + tokenizer2.tokenize(istr2,"","",true); + + // Copy the value tokens + std::stack link; + for (const Token *tok2 = tokenizer2.tokens(); tok2; tok2 = tok2->next()) + { + tok->str(tok2->str()); + + if (Token::Match(tok2,"[{([]")) + link.push(tok); + else if (!link.empty() && Token::Match(tok2,"[})]]")) + { + Token::createMutualLinks(link.top(), tok); + link.pop(); + } + + if (tok2->next()) + { + tok->insertToken(""); + tok = tok->next(); + } + } + } else if ((!tok->previous() || tok->strAt(-1) == "||" || tok->strAt(-1) == "&&" || tok->strAt(-1) == "(") && (!tok->next() || tok->strAt(1) == "||" || tok->strAt(1) == "&&" || tok->strAt(1) == ")")) tok->str("1"); diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 05a0aa0a5..0eafe3ebb 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -57,6 +57,13 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti // goto initial '{' tok2 = initBaseInfo(new_scope, tok); + + // make sure we have valid code + if (!tok2) + { + delete new_scope; + break; + } } new_scope->classStart = tok2; @@ -213,7 +220,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti function.isConst = true; // pure virtual function - if (Token::Match(end, ") const| = %any% ;")) + if (Token::Match(end, ") const| = %any%")) function.isPure = true; // count the number of constructors @@ -241,6 +248,13 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti function.isInline = true; function.hasBody = true; + // find start of function '{' + while (end && end->str() != "{") + end = end->next(); + + // save start of function + function.start = end; + scope->functionList.push_back(function); const Token *tok2 = funcStart; @@ -761,6 +775,10 @@ void SymbolDatabase::addFunction(Scope **scope, const Token **tok, const Token * func->hasBody = true; func->token = *tok; func->arg = argStart; + const Token *start = argStart->link()->next(); + while (start && start->str() != "{") + start = start->next(); + func->start = start; } } else if (func->tokenDef->str() == (*tok)->str() && (*tok)->previous()->str() != "~") @@ -776,6 +794,10 @@ void SymbolDatabase::addFunction(Scope **scope, const Token **tok, const Token * func->hasBody = true; func->token = *tok; func->arg = argStart; + const Token *start = argStart->link()->next(); + while (start && start->str() != "{") + start = start->next(); + func->start = start; } } @@ -786,6 +808,10 @@ void SymbolDatabase::addFunction(Scope **scope, const Token **tok, const Token * func->hasBody = true; func->token = *tok; func->arg = argStart; + const Token *start = argStart->link()->next()->next()->link()->next(); + while (start && start->str() != "{") + start = start->next(); + func->start = start; } } } @@ -872,6 +898,10 @@ const Token *SymbolDatabase::initBaseInfo(Scope *scope, const Token *tok) tok2 = tok2->next(); + // check for invalid code + if (!tok2->next()) + return NULL; + if (tok2->str() == "public") { base.access = Public; @@ -1142,6 +1172,7 @@ void Scope::getVariableList() // This is the start of a statement const Token *vartok = NULL; const Token *typetok = NULL; + const Token *typestart = tok; // Is it const..? bool isConst = false; @@ -1211,7 +1242,7 @@ void Scope::getVariableList() if (typetok) scope = check->findVariableType(this, typetok); - addVariable(vartok, varaccess, isMutable, isStatic, isConst, isClass, scope); + addVariable(vartok, typestart, varaccess, isMutable, isStatic, isConst, isClass, scope); } } } @@ -1422,7 +1453,8 @@ Scope * Scope::findInNestedListRecursive(const std::string & name) for (it = nestedList.begin(); it != nestedList.end(); ++it) { Scope *child = (*it)->findInNestedListRecursive(name); - return child; + if (child) + return child; } return 0; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 4e04f4d5a..b21417c51 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -73,10 +73,11 @@ class Variable } public: - Variable(const Token *name_, std::size_t index_, AccessControl access_, - bool mutable_, bool static_, bool const_, bool class_, - const Scope *type_) + Variable(const Token *name_, const Token *start_, std::size_t index_, + AccessControl access_, bool mutable_, bool static_, bool const_, + bool class_, const Scope *type_) : _name(name_), + _start(start_), _index(index_), _access(access_), _flags(0), @@ -97,6 +98,15 @@ public: return _name; } + /** + * Get type start token. + * @return type start token + */ + const Token *typeStartToken() const + { + return _start; + } + /** * Get name string. * @return name string @@ -200,6 +210,9 @@ private: /** @brief variable name token */ const Token *_name; + /** @brief variable type start token */ + const Token *_start; + /** @brief order declared */ std::size_t _index; @@ -223,6 +236,7 @@ public: argDef(NULL), token(NULL), arg(NULL), + start(NULL), access(Public), hasBody(false), isInline(false), @@ -245,6 +259,7 @@ public: const Token *argDef; // function argument start '(' in class definition const Token *token; // function name token in implementation const Token *arg; // function argument start '(' + const Token *start; // function start '{' AccessControl access; // public/protected/private bool hasBody; // has implementation bool isInline; // implementation in class definition @@ -317,9 +332,9 @@ public: */ Scope * findInNestedListRecursive(const std::string & name); - void addVariable(const Token *token_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const Scope *type_) + void addVariable(const Token *token_, const Token *start_, AccessControl access_, bool mutable_, bool static_, bool const_, bool class_, const Scope *type_) { - varlist.push_back(Variable(token_, varlist.size(), access_, mutable_, static_, const_, class_, type_)); + varlist.push_back(Variable(token_, start_, varlist.size(), access_, mutable_, static_, const_, class_, type_)); } /** @brief initialize varlist */ diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d578c9ad9..cb23f4c39 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1,4 +1,4 @@ -/* +/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2011 Daniel Marjamäki and Cppcheck team. * @@ -837,19 +837,29 @@ static Token *splitDefinitionFromTypedef(Token *tok) tok1->insertToken(";"); tok1 = tok1->next(); - tok1->insertToken("typedef"); - tok1 = tok1->next(); - Token * tok3 = tok1; - if (isConst) + + if (tok1->next()->str() == ";" && tok1 && tok1->previous()->str() == "}") { - tok1->insertToken("const"); - tok1 = tok1->next(); + tok->deleteThis(); + tok1->deleteThis(); + return NULL; + } + else + { + tok1->insertToken("typedef"); + tok1 = tok1->next(); + Token * tok3 = tok1; + if (isConst) + { + tok1->insertToken("const"); + tok1 = tok1->next(); + } + tok1->insertToken(tok->next()->str()); // struct, union or enum + tok1 = tok1->next(); + tok1->insertToken(name.c_str()); + tok->deleteThis(); + tok = tok3; } - tok1->insertToken(tok->next()->str()); // struct, union or enum - tok1 = tok1->next(); - tok1->insertToken(name.c_str()); - tok->deleteThis(); - tok = tok3; return tok; } @@ -2457,21 +2467,31 @@ void Tokenizer::labels() * is the token pointing at a template parameters block * < int , 3 > => yes * \param tok start token that must point at "<" - * \return true if the tokens look like template parameters + * \return number of parameters (invalid parameters => 0) */ -static bool templateParameters(const Token *tok) +static unsigned int templateParameters(const Token *tok) { + unsigned int numberOfParameters = 0; + if (!tok) - return false; + return 0; if (tok->str() != "<") - return false; + return 0; tok = tok->next(); while (tok) { + ++numberOfParameters; + + // skip std:: + while (Token::Match(tok, "%var% ::")) + tok = tok->tokAt(2); + if (!tok) + return 0; + // num/type .. if (!tok->isNumber() && !tok->isName()) - return false; + return 0; tok = tok->next(); // optional "*" @@ -2480,12 +2500,12 @@ static bool templateParameters(const Token *tok) // ,/> if (tok->str() == ">") - return true; + return numberOfParameters; if (tok->str() != ",") break; tok = tok->next(); } - return false; + return 0; } @@ -2768,6 +2788,33 @@ void Tokenizer::simplifyTemplatesUseDefaultArgumentValues(const std::list" + * @return match => true + */ +static bool simplifyTemplatesInstantiateMatch(const Token *instance, const std::string &name, unsigned int numberOfArguments, const char patternAfter[]) +{ + if (!Token::simpleMatch(instance, (name + " <").c_str())) + return false; + + if (numberOfArguments != templateParameters(instance->next())) + return false; + + if (patternAfter) + { + const Token *tok = Token::findmatch(instance, ">"); + if (!tok || !Token::Match(tok->next(), patternAfter)) + return false; + } + + // nothing mismatching was found.. + return true; +} + void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, std::list &used, std::set &expandedtemplates) @@ -2826,16 +2873,6 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, const bool isfunc(tok->strAt(namepos + 1) == "("); // locate template usage.. - - std::string s(name + " <"); - for (unsigned int i = 0; i < type.size(); ++i) - { - if (i > 0) - s += ","; - s += " %any% "; - } - const std::string pattern(s + "> "); - std::string::size_type sz1 = used.size(); unsigned int recursiveCount = 0; @@ -2855,17 +2892,16 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, } Token * const tok2 = *iter2; - if (tok2->str() != name) continue; if (Token::Match(tok2->previous(), "[;{}=]") && - !Token::Match(tok2, (pattern + (isfunc ? "(" : "*| %var%")).c_str())) + !simplifyTemplatesInstantiateMatch(*iter2, name, type.size(), isfunc ? "(" : "*| %var%")) continue; // New type.. - std::vector types2; - s = ""; + std::vector types2; + std::string s; std::string s1(name + " < "); for (const Token *tok3 = tok2->tokAt(2); tok3 && tok3->str() != ">"; tok3 = tok3->next()) { @@ -2876,8 +2912,8 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, } s1 += tok3->str(); s1 += " "; - if (tok3->str() != ",") - types2.push_back(*tok3); + if (Token::Match(tok3->previous(), "[<,]")) + types2.push_back(tok3); // add additional type information if (tok3->isUnsigned()) s += "unsigned"; @@ -2939,7 +2975,9 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, } // member function implemented outside class definition - else if (_indentlevel == 0 && _parlevel == 0 && Token::Match(tok3, (pattern + " :: ~| %var% (").c_str())) + else if (_indentlevel == 0 && + _parlevel == 0 && + simplifyTemplatesInstantiateMatch(tok3, name, type.size(), ":: ~| %var% (")) { addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex()); while (tok3->str() != "::") @@ -2987,7 +3025,12 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, // replace type with given type.. if (itype < type.size()) { - addtoken(&types2[itype], tok3->linenr(), tok3->fileIndex()); + for (const Token *typetok = types2[itype]; + typetok && !Token::Match(typetok, "[,>]"); + typetok = typetok->next()) + { + addtoken(typetok, tok3->linenr(), tok3->fileIndex()); + } continue; } } @@ -3055,18 +3098,26 @@ void Tokenizer::simplifyTemplatesInstantiate(const Token *tok, bool match = true; Token * tok5 = tok4->tokAt(2); unsigned int count = 0; + const Token *typetok = (!types2.empty()) ? types2[0] : 0; while (tok5->str() != ">") { if (tok5->str() != ",") { - if (tok5->isUnsigned() != types2[count].isUnsigned() || - tok5->isSigned() != types2[count].isSigned() || - tok5->isLong() != types2[count].isLong()) + if (!typetok || + tok5->isUnsigned() != typetok->isUnsigned() || + tok5->isSigned() != typetok->isSigned() || + tok5->isLong() != typetok->isLong()) { match = false; break; } - count++; + + typetok = typetok ? typetok->next() : 0; + } + else + { + ++count; + typetok = (count < types2.size()) ? types2[count] : 0; } tok5 = tok5->next(); } @@ -6257,7 +6308,7 @@ bool Tokenizer::simplifyKnownVariables() if (tok2->str() == tok2->strAt(2)) continue; - const bool pointeralias(tok2->tokAt(2)->isName() || tok2->tokAt(2)->str() == "&"); + const Token * const valueToken = tok2->tokAt(2); std::string value; unsigned int valueVarId = 0; @@ -6268,7 +6319,7 @@ bool Tokenizer::simplifyKnownVariables() if (!simplifyKnownVariablesGetData(varid, &tok2, &tok3, value, valueVarId, valueIsPointer, floatvars.find(tok2->varId()) != floatvars.end())) continue; - ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, pointeralias, indentlevel); + ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, valueToken, indentlevel); } } @@ -6360,8 +6411,10 @@ bool Tokenizer::simplifyKnownVariablesGetData(unsigned int varid, Token **_tok2, return true; } -bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned int valueVarId, bool valueIsPointer, bool pointeralias, int indentlevel) +bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) { + const bool pointeralias(valueToken->isName() || Token::Match(valueToken, "& %var% [")); + bool ret = false; Token* bailOutFromLoop = 0; @@ -6540,6 +6593,11 @@ bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsign tok3->deleteNext(); tok3->deleteNext(); } + if (Token::Match(valueToken, "& %var% ;")) + { + tok3->insertToken("&"); + tok3 = tok3->next(); + } tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); @@ -8078,7 +8136,7 @@ void Tokenizer::cppcheckError(const Token *tok) const const ErrorLogger::ErrorMessage errmsg(locationList, Severity::error, - "### Internal error in Cppcheck. Please report it.", + "Analysis failed. If the code is valid then please report this failure.", "cppcheckError"); if (_errorLogger) @@ -9015,7 +9073,7 @@ void Tokenizer::simplifyBitfields() { Token *last = 0; - if (Token::Match(tok, ";|{|}|public:|protected:|private: const| %type% %var% : %num% ;|,")) + if (Token::Match(tok, ";|{|}|public:|protected:|private: const| %type% %var% : %any% ;|,")) { int offset = 0; if (tok->next()->str() == "const") @@ -9024,7 +9082,7 @@ void Tokenizer::simplifyBitfields() last = tok->tokAt(5 + offset); Token::eraseTokens(tok->tokAt(2 + offset), tok->tokAt(5 + offset)); } - else if (Token::Match(tok, ";|{|}|public:|protected:|private: const| %type% : %num% ;")) + else if (Token::Match(tok, ";|{|}|public:|protected:|private: const| %type% : %any% ;")) { int offset = 0; if (tok->next()->str() == "const") @@ -9179,9 +9237,11 @@ void Tokenizer::simplifyBorland() // Remove Qt signals and slots void Tokenizer::simplifyQtSignalsSlots() { - Token *tok = _tokens; - while ((tok = const_cast(Token::findmatch(tok, "class %var% :")))) + for (Token *tok = _tokens; tok; tok = tok->next()) { + if (!Token::Match(tok, "class %var% :")) + continue; + if (tok->previous() && tok->previous()->str() == "enum") { tok = tok->tokAt(2); diff --git a/lib/tokenize.h b/lib/tokenize.h index cc076e120..d5faa0812 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -320,7 +320,7 @@ public: * utility function for simplifyKnownVariables. Perform simplification * of a given variable */ - bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned int valueVarId, bool valueIsPointer, bool pointeralias, int indentlevel); + bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, unsigned int varid, const std::string &structname, std::string &value, unsigned int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel); /** Replace a "goto" with the statements */ void simplifyGoto(); diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index ba3cf9151..ecc3c7554 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -135,6 +135,7 @@ private: TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_17); // ticket #2548 TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable + TEST_CASE(buffer_overrun_19); // #2597 - class member with unknown type TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch // It is undefined behaviour to point out of bounds of an array @@ -1891,6 +1892,20 @@ private: errout.str()); } + void buffer_overrun_19() // #2597 - class member with unknown type + { + check("class A {\n" + "public:\n" + " u8 buf[10];\n" + " A();" + "};\n" + "\n" + "A::A() {\n" + " memset(buf, 0, 10);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void buffer_overrun_bailoutIfSwitch() { // No false positive diff --git a/test/testclass.cpp b/test/testclass.cpp index 9ea23b8fb..7f80a053b 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -113,6 +113,7 @@ private: TEST_CASE(operatorEqToSelf6); // ticket # 1550 TEST_CASE(operatorEqToSelf7); TEST_CASE(operatorEqToSelf8); // ticket #2179 + TEST_CASE(operatorEqToSelf9); // ticket #2592 TEST_CASE(memsetOnStruct); TEST_CASE(memsetVector); TEST_CASE(memsetOnClass); @@ -163,6 +164,7 @@ private: TEST_CASE(const41); // ticket #2255 TEST_CASE(const42); // ticket #2282 TEST_CASE(const43); // ticket #2377 + TEST_CASE(const44); // ticket #2595 TEST_CASE(assigningPointerToPointerIsNotAConstOperation); TEST_CASE(assigningArrayElementIsNotAConstOperation); TEST_CASE(constoperator1); // operator< can often be const @@ -192,6 +194,7 @@ private: TEST_CASE(symboldatabase11); // ticket #2539 TEST_CASE(symboldatabase12); // ticket #2547 TEST_CASE(symboldatabase13); // ticket #2577 + TEST_CASE(symboldatabase14); // ticket #2589 } // Check the operator Equal @@ -1298,6 +1301,26 @@ private: ASSERT_EQUALS("", errout.str()); } + void operatorEqToSelf9() + { + checkOpertorEqToSelf( + "class Foo\n" + "{\n" + "public:\n" + " Foo& operator=(Foo* pOther);\n" + " Foo& operator=(Foo& other);\n" + "};\n" + "Foo& Foo::operator=(Foo* pOther)\n" + "{\n" + " return *this;\n" + "}\n" + "Foo& Foo::operator=(Foo& other)\n" + "{\n" + " return Foo::operator=(&other);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + // Check that base classes have virtual destructors void checkVirtualDestructor(const char code[]) { @@ -4959,6 +4982,21 @@ private: ASSERT_EQUALS("", errout.str()); } + void const44() // ticket 2595 + { + checkConst("class A\n" + "{\n" + "public:\n" + " bool bOn;\n" + " bool foo()\n" + " {\n" + " return 0 != (bOn = bOn && true);\n" + " }\n" + "};\n"); + + ASSERT_EQUALS("", errout.str()); + } + void assigningPointerToPointerIsNotAConstOperation() { checkConst("struct s\n" @@ -5550,6 +5588,14 @@ private: ASSERT_EQUALS("", errout.str()); } + void symboldatabase14() + { + // ticket #2589 - segmentation fault + checkConst("struct B : A\n"); + + ASSERT_EQUALS("", errout.str()); + } + }; REGISTER_TEST(TestClass) diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 03a7d0e1b..635973e0c 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -193,13 +193,42 @@ private: "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 4\n", errout.str()); - check("void foo(struct ABC *abc)\n" - "{\n" + check("void foo(struct ABC *abc) {\n" " bar(abc->a);\n" " if (!abc)\n" " ;\n" "}\n"); - ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 4\n", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 3\n", errout.str()); + + check("void foo(ABC *abc) {\n" + " abc->do_something();\n" + " if (abc)\n" + " ;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 3\n", errout.str()); + + check("void foo(ABC *abc) {\n" + " if (abc->a == 3) {\n" + " return;\n" + " }\n" + " if (abc) {}\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 5\n", "", errout.str()); + + // TODO: False negative if member of member is dereferenced + check("void foo(ABC *abc) {\n" + " abc->next->a = 0;\n" + " if (abc->next)\n" + " ;\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 3\n", "", errout.str()); + + check("void foo(ABC *abc) {\n" + " abc->a = 0;\n" + " if (abc && abc->b == 0)\n" + " ;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Possible null pointer dereference: abc - otherwise it is redundant to check if abc is null at line 3\n", errout.str()); // ok dereferencing in a condition check("void foo(struct ABC *abc)\n" @@ -718,6 +747,21 @@ private: "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); + + // #2231 - error if assignment in loop is not used + check("void f() {\n" + " char *p = 0;\n" + "\n" + " for (int x = 0; x < 3; ++x) {\n" + " if (y[x] == 0) {\n" + " p = malloc(10);\n" + " break;\n" + " }\n" + " }\n" + "\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (error) Possible null pointer dereference: p\n", errout.str()); } void nullpointer7() @@ -787,6 +831,19 @@ private: "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: p\n", errout.str()); + check("void foo(char *p) {\n" + " if (p && *p == 0) {\n" + " }\n" + " printf(\"%c\", *p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: p\n", errout.str()); + + check("void foo(char *p) {\n" + " if (p && *p == 0) {\n" + " } else { *p = 0; }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n", errout.str()); + check("void foo(abc *p) {\n" " if (!p) {\n" " }\n" diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 7f032b3e6..faba401a5 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -119,6 +119,7 @@ private: TEST_CASE(if_cond9); TEST_CASE(if_cond10); TEST_CASE(if_cond11); + TEST_CASE(if_cond12); TEST_CASE(if_or_1); TEST_CASE(if_or_2); @@ -1340,6 +1341,15 @@ private: ASSERT_EQUALS("", errout.str()); } + void if_cond12() + { + const char filedata[] = "#define A (1)\n" + "#if A == 1\n" + ";\n" + "#endif\n"; + ASSERT_EQUALS("\n\n;\n\n", Preprocessor::getcode(filedata,"","",NULL,NULL)); + } + void if_or_1() diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index a4c507cd4..58273fa4a 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -110,6 +110,7 @@ private: TEST_CASE(template19); TEST_CASE(template20); TEST_CASE(template21); + TEST_CASE(template22); TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); TEST_CASE(template_default_type); @@ -237,6 +238,7 @@ private: TEST_CASE(simplifyTypedef77); // ticket #2554 TEST_CASE(simplifyTypedef78); // ticket #2568 TEST_CASE(simplifyTypedef79); // ticket #2348 + TEST_CASE(simplifyTypedef80); // ticket #2587 TEST_CASE(simplifyTypedefFunction1); TEST_CASE(simplifyTypedefFunction2); // ticket #1685 @@ -1984,6 +1986,19 @@ private: } } + void template22() + { + const char code[] = "template struct Fred { T a; };\n" + "Fred fred;"; + + const std::string expected("; " + "Fred fred ; " + "struct Fred { std :: string a ; }"); + + ASSERT_EQUALS(expected, sizeof_(code)); + } + + void template_unhandled() { // An unhandled template usage should be simplified.. @@ -4876,6 +4891,23 @@ private: ASSERT_EQUALS(expected, sizeof_(code)); } + void simplifyTypedef80() // ticket #2587 + { + const char code[] = "typedef struct s { };\n" + "void f() {\n" + " sizeof(struct s);\n" + "};\n"; + const std::string expected = "struct s { } ; " + "void f ( ) { " + "sizeof ( struct s ) ; " + "} ;"; + ASSERT_EQUALS(expected, sizeof_(code)); + + // Check for output.. + checkSimplifyTypedef(code); + ASSERT_EQUALS("", errout.str()); + } + void simplifyTypedefFunction1() { { @@ -5767,11 +5799,7 @@ private: "}\n"; const char expected[] = "int f ( ) " "{" - " int i ;" - " int * p ;" - " p = & i ;" - " i = 5 ;" - " return 5 ; " + " ; return 5 ; " "}"; ASSERT_EQUALS(expected, tok(code)); } diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp index df6ac2627..e26a7042d 100644 --- a/test/testsuppressions.cpp +++ b/test/testsuppressions.cpp @@ -172,6 +172,35 @@ private: ""); ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " // cppcheck-suppress uninitvar\n" + "\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " /* cppcheck-suppress uninitvar */\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + + // suppress uninitvar inline + (this->*check)("void f() {\n" + " int a;\n" + " /* cppcheck-suppress uninitvar */\n" + "\n" + " a++;\n" + "}\n", + ""); + ASSERT_EQUALS("", errout.str()); + // suppress uninitvar inline, without error present (this->*check)("void f() {\n" " int a;\n" diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 504e1c769..7b35ea570 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -127,6 +127,7 @@ private: TEST_CASE(simplifyKnownVariables38); // ticket #2399 - simplify conditions TEST_CASE(simplifyKnownVariables39); TEST_CASE(simplifyKnownVariables40); + TEST_CASE(simplifyKnownVariables41); // p=&x; if (p) .. TEST_CASE(simplifyKnownVariablesBailOutAssign); TEST_CASE(simplifyKnownVariablesBailOutFor1); TEST_CASE(simplifyKnownVariablesBailOutFor2); @@ -278,6 +279,7 @@ private: TEST_CASE(bitfields3); TEST_CASE(bitfields4); // ticket #1956 TEST_CASE(bitfields5); // ticket #1956 + TEST_CASE(bitfields6); // ticket #2595 TEST_CASE(microsoftMFC); @@ -2020,6 +2022,16 @@ private: ASSERT_EQUALS("void f ( ) {\n;\nchar c2 ; c2 = { 'a' } ;\n}", tokenizeAndStringify(code, true)); } + void simplifyKnownVariables41() + { + const char code[] = "void f() {\n" + " int x = 0;\n" + " const int *p; p = &x;\n" + " if (p) { return 0; }\n" + "}"; + ASSERT_EQUALS("void f ( ) {\nint x ; x = 0 ;\nconst int * p ; p = & x ;\nif ( & x ) { return 0 ; }\n}", tokenizeAndStringify(code, true)); + } + void simplifyKnownVariablesBailOutAssign() { const char code[] = "int foo() {\n" @@ -5072,6 +5084,18 @@ private: ASSERT_EQUALS("struct A { virtual void f ( ) { } int f1 ; } ;", tokenizeAndStringify(code3,false)); } + void bitfields6() // ticket #2595 + { + const char code1[] = "struct A { bool b : true; };"; + ASSERT_EQUALS("struct A { bool b ; } ;", tokenizeAndStringify(code1,false)); + + const char code2[] = "struct A { bool b : true, c : true; };"; + ASSERT_EQUALS("struct A { bool b ; bool c ; } ;", tokenizeAndStringify(code2,false)); + + const char code3[] = "struct A { bool : true; };"; + ASSERT_EQUALS("struct A { } ;", tokenizeAndStringify(code3,false)); + } + void microsoftMFC() { const char code1[] = "class MyDialog : public CDialog { DECLARE_MESSAGE_MAP() private: CString text; };"; diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 1aee1c480..fbd29681d 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -801,6 +801,21 @@ private: "}\n"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: pItem\n", "", errout.str()); + + // #2231 - conditional initialization in loop.. + checkUninitVar("int foo(char *a) {\n" + " int x;\n" + "\n" + " for (int i = 0; i < 10; ++i) {\n" + " if (a[i] == 'x') {\n" + " x = i;\n" + " break;\n" + " }\n" + " }\n" + "\n" + " return x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (error) Uninitialized variable: x\n", errout.str()); } // switch..