diff --git a/src/checkbufferoverrun.cpp b/src/checkbufferoverrun.cpp index 1546367ed..c95388ae3 100644 --- a/src/checkbufferoverrun.cpp +++ b/src/checkbufferoverrun.cpp @@ -1,599 +1,599 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 -#include -#include - - -#include // <- strtoul - -//--------------------------------------------------------------------------- - -// _callStack used when parsing into subfunctions. - - -CheckBufferOverrunClass::CheckBufferOverrunClass(const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger) - : _settings(settings) -{ - _tokenizer = tokenizer; - _errorLogger = errorLogger; -} - -CheckBufferOverrunClass::~CheckBufferOverrunClass() -{ - -} - -// Modified version of 'ReportError' that also reports the callstack -void CheckBufferOverrunClass::ReportError(const std::string &errmsg) -{ - std::ostringstream ostr; - std::list::const_iterator it; - for (it = _callStack.begin(); it != _callStack.end(); it++) - ostr << _tokenizer->fileLine(*it) << " -> "; - ostr << errmsg; - _errorLogger->reportErr(ostr.str()); -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Check array usage.. -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::CheckBufferOverrun_CheckScope(const Token *tok, const char *varname[], const int size, const int total_size, unsigned int varid) -{ - unsigned int varc = 0; - - std::string varnames; - while (varname[varc]) - { - if (varc > 0) - varnames += " . "; - - varnames += varname[varc]; - - ++varc; - } - - if (varc == 0) - varc = 1; - - varc = 2 * (varc - 1); - - // Array index.. - if (varid > 0) - { - if (Token::Match(tok, "%varid% [ %num% ]", varid)) - { - const char *num = tok->strAt(2); - if (strtol(num, NULL, 10) >= size) - { - ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); - } - } - } - else if (Token::Match(tok, std::string(varnames + " [ %num% ]").c_str())) - { - const char *num = tok->strAt(2 + varc); - if (strtol(num, NULL, 10) >= size) - { - ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); - } - } - - - int indentlevel = 0; - for (; tok; tok = tok->next()) - { - if (tok->str() == "{") - { - ++indentlevel; - } - - else if (tok->str() == "}") - { - --indentlevel; - if (indentlevel < 0) - return; - } - - // Array index.. - if (varid > 0) - { - if (!tok->isName() && !Token::Match(tok, "[.&]") && Token::Match(tok->next(), "%varid% [ %num% ]", varid)) - { - const char *num = tok->strAt(3); - if (strtol(num, NULL, 10) >= size) - { - ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); - } - } - } - else if (!tok->isName() && !Token::Match(tok, "[.&]") && Token::Match(tok->next(), std::string(varnames + " [ %num% ]").c_str())) - { - const char *num = tok->next()->strAt(2 + varc); - if (strtol(num, NULL, 10) >= size) - { - ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); - } - tok = tok->tokAt(4); - continue; - } - - - // memset, memcmp, memcpy, strncpy, fgets.. - if (varid > 0) - { - if (Token::Match(tok, "memset|memcpy|memmove|memcmp|strncpy|fgets")) - { - if (Token::Match(tok->next(), "( %varid% , %num% , %num% )", varid) || - Token::Match(tok->next(), "( %var% , %varid% , %num% )", varid)) - { - const char *num = tok->strAt(6); - if (atoi(num) > total_size) - { - ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); - } - } - continue; - } - } - else if (Token::Match(tok, "memset|memcpy|memmove|memcmp|strncpy|fgets")) - { - if (Token::Match(tok->next(), std::string("( " + varnames + " , %num% , %num% )").c_str()) || - Token::Match(tok->next(), std::string("( %var% , " + varnames + " , %num% )").c_str())) - { - const char *num = tok->strAt(varc + 6); - if (atoi(num) > total_size) - { - ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); - } - } - continue; - } - - - // Loop.. - if (Token::simpleMatch(tok, "for (")) - { - const Token *tok2 = tok->tokAt(2); - - // for - setup.. - if (Token::Match(tok2, "%var% = 0 ;")) - tok2 = tok2->tokAt(4); - else if (Token::Match(tok2, "%type% %var% = 0 ;")) - tok2 = tok2->tokAt(5); - else if (Token::Match(tok2, "%type% %type% %var% = 0 ;")) - tok2 = tok2->tokAt(6); - else - continue; - - // for - condition.. - if (!Token::Match(tok2, "%var% < %num% ;") && !Token::Match(tok2, "%var% <= %num% ;")) - continue; - - // Get index variable and stopsize. - const char *strindex = tok2->aaaa(); - int value = ((tok2->next()->aaaa1() == '=') ? 1 : 0) + atoi(tok2->strAt(2)); - if (value <= size) - continue; - - // Goto the end of the for loop.. - while (tok2 && tok2->str() != ")") - tok2 = tok2->next(); - if (!tok2 || !tok2->tokAt(5)) - break; - - std::ostringstream pattern; - pattern << varnames << " [ " << strindex << " ]"; - - int indentlevel2 = 0; - while ((tok2 = tok2->next())) - { - if (tok2->str() == ";" && indentlevel2 == 0) - break; - - if (tok2->str() == "{") - ++indentlevel2; - - if (tok2->str() == "}") - { - --indentlevel2; - if (indentlevel2 <= 0) - break; - } - - if (Token::Match(tok2, pattern.str().c_str())) - { - ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok2)); - break; - } - - } - continue; - } - - - // Writing data into array.. - if (Token::Match(tok, std::string("strcpy ( " + varnames + " , %str% )").c_str())) - { - int len = 0; - const char *str = tok->strAt(varc + 4); - while (*str) - { - if (*str == '\\') - ++str; - ++str; - ++len; - } - if (len > 2 && len >= (int)size + 2) - { - ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); - } - continue; - } - - // sprintf.. - if (varid > 0 && Token::Match(tok, "sprintf ( %varid% , %str% ,", varid)) - { - int len = 0; - for (const Token *tok2 = tok->tokAt(6); tok2 && tok2->str() != ")"; tok2 = tok2->next()) - { - if (tok2->aaaa0() == '\"') - { - len -= 2; - const char *str = tok->strAt(0); - while (*str) - { - if (*str == '\\') - ++str; - ++str; - ++len; - } - } - } - if (len > (int)size) - { - ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); - } - } - - // snprintf.. - if (varid > 0 && Token::Match(tok, "snprintf ( %varid% , %num%", varid)) - { - int n = atoi(tok->strAt(4)); - if (n > size) - ReportError(ErrorMessage::outOfBounds(_tokenizer, tok->tokAt(4), "snprintf size")); - } - - - // Function call.. - // It's not interesting to check what happens when the whole struct is - // sent as the parameter, that is checked separately anyway. - if (Token::Match(tok, "%var% (")) - { - // Don't make recursive checking.. - if (std::find(_callStack.begin(), _callStack.end(), tok) != _callStack.end()) - continue; - - // Only perform this checking if showAll setting is enabled.. - if (!_settings._showAll) - continue; - - unsigned int parlevel = 0, par = 0; - for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == "(") - { - ++parlevel; - } - - else if (tok2->str() == ")") - { - --parlevel; - if (parlevel < 1) - { - par = 0; - break; - } - } - - else if (parlevel == 1 && (tok2->str() == ",")) - { - ++par; - } - - if (parlevel == 1 && Token::Match(tok2, std::string("[(,] " + varnames + " [,)]").c_str())) - { - ++par; - break; - } - } - - if (par == 0) - continue; - - // Find function.. - const Token *ftok = _tokenizer->GetFunctionTokenByName(tok->aaaa()); - if (!ftok) - continue; - - // Parse head of function.. - ftok = ftok->tokAt(2); - parlevel = 1; - while (ftok && parlevel == 1 && par >= 1) - { - if (ftok->str() == "(") - ++parlevel; - - else if (ftok->str() == ")") - --parlevel; - - else if (ftok->str() == ",") - --par; - - else if (par == 1 && parlevel == 1 && Token::Match(ftok, "%var% [,)]")) - { - // Parameter name.. - const char *parname[2]; - parname[0] = ftok->aaaa(); - parname[1] = 0; - - // Goto function body.. - while (ftok && (ftok->str() != "{")) - ftok = ftok->next(); - ftok = ftok ? ftok->next() : 0; - - // Check variable usage in the function.. - _callStack.push_back(tok); - CheckBufferOverrun_CheckScope(ftok, parname, size, total_size, 0); - _callStack.pop_back(); - - // break out.. - break; - } - - ftok = ftok->next(); - } - } - } -} - - -//--------------------------------------------------------------------------- -// Checking local variables in a scope -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::CheckBufferOverrun_LocalVariable() -{ - int indentlevel = 0; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->str() == "{") - ++indentlevel; - - else if (tok->str() == "}") - --indentlevel; - - else if (indentlevel > 0) - { - const char *varname[2] = {0}; - unsigned int size = 0; - const char *type = 0; - unsigned int varid = 0; - int nextTok = 0; - - if (Token::Match(tok, "%type% %var% [ %num% ] ;")) - { - varname[0] = tok->strAt(1); - size = strtoul(tok->strAt(3), NULL, 10); - type = tok->aaaa(); - varid = tok->tokAt(1)->varId(); - nextTok = 6; - } - else if (Token::Match(tok, "[*;{}] %var% = new %type% [ %num% ]")) - { - varname[0] = tok->strAt(1); - size = strtoul(tok->strAt(6), NULL, 10); - type = tok->strAt(4); - varid = tok->tokAt(1)->varId(); - nextTok = 8; - } - else - { - continue; - } - - int total_size = size * _tokenizer->SizeOfType(type); - if (total_size == 0) - continue; - - // The callstack is empty - _callStack.clear(); - CheckBufferOverrun_CheckScope(tok->tokAt(nextTok), varname, size, total_size, varid); - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Checking member variables of structs.. -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::CheckBufferOverrun_StructVariable() -{ - const char declstruct[] = "struct|class %var% {"; - for (const Token *tok = Token::findmatch(_tokenizer->tokens(), declstruct); - tok; tok = Token::findmatch(tok->next(), declstruct)) - { - const std::string &structname = tok->next()->str(); - - // Found a struct declaration. Search for arrays.. - for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "}") - break; - - int ivar = 0; - if (Token::Match(tok2->next(), "%type% %var% [ %num% ] ;")) - ivar = 2; - else if (Token::Match(tok2->next(), "%type% %type% %var% [ %num% ] ;")) - ivar = 3; - else if (Token::Match(tok2->next(), "%type% * %var% [ %num% ] ;")) - ivar = 3; - else if (Token::Match(tok2->next(), "%type% %type% * %var% [ %num% ] ;")) - ivar = 4; - else - continue; - - const char *varname[3] = {0, 0, 0}; - varname[1] = tok2->strAt(ivar); - int arrsize = atoi(tok2->strAt(ivar + 2)); - int total_size = arrsize * _tokenizer->SizeOfType(tok2->next()->aaaa()); - if (total_size == 0) - continue; - - - // Class member variable => Check functions - if (tok->str() == "class") - { - std::string func_pattern(structname + " :: %var% ("); - const Token *tok3 = Token::findmatch(_tokenizer->tokens(), func_pattern.c_str()); - while (tok3) - { - for (const Token *tok4 = tok3; tok4; tok4 = tok4->next()) - { - if (Token::Match(tok4, "[;{}]")) - break; - - if (Token::simpleMatch(tok4, ") {")) - { - const char *names[2] = {varname[1], 0}; - CheckBufferOverrun_CheckScope(tok4->tokAt(2), names, arrsize, total_size, 0); - break; - } - } - tok3 = Token::findmatch(tok3->next(), func_pattern.c_str()); - } - } - - for (const Token *tok3 = _tokenizer->tokens(); tok3; tok3 = tok3->next()) - { - if (tok3->str() != structname) - continue; - - // Declare variable: Fred fred1; - if (Token::Match(tok3->next(), "%var% ;")) - varname[0] = tok3->strAt(1); - - // Declare pointer: Fred *fred1 - else if (Token::Match(tok3->next(), "* %var% [,);=]")) - varname[0] = tok3->strAt(2); - - else - continue; - - - // Goto end of statement. - const Token *CheckTok = NULL; - while (tok3) - { - // End of statement. - if (tok3->str() == ";") - { - CheckTok = tok3; - break; - } - - // End of function declaration.. - if (Token::simpleMatch(tok3, ") ;")) - break; - - // Function implementation.. - if (Token::simpleMatch(tok3, ") {")) - { - CheckTok = tok3->tokAt(2); - break; - } - - tok3 = tok3->next(); - } - - if (!tok3) - break; - - if (!CheckTok) - continue; - - // Check variable usage.. - CheckBufferOverrun_CheckScope(CheckTok, varname, arrsize, total_size, 0); - } - } - } -} -//--------------------------------------------------------------------------- - - - -void CheckBufferOverrunClass::bufferOverrun() -{ - CheckBufferOverrun_LocalVariable(); - CheckBufferOverrun_StructVariable(); -} -//--------------------------------------------------------------------------- - - - - - - - - -//--------------------------------------------------------------------------- -// Dangerous functions -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::dangerousFunctions() -{ - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (Token::Match(tok, "gets|scanf (")) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Found '" << tok->str() << "'. You should use 'fgets' instead"; - _errorLogger->reportErr(ostr.str()); - } - } -} -//--------------------------------------------------------------------------- - - - - +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 +#include +#include + + +#include // <- strtoul + +//--------------------------------------------------------------------------- + +// _callStack used when parsing into subfunctions. + + +CheckBufferOverrunClass::CheckBufferOverrunClass(const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger) + : _settings(settings) +{ + _tokenizer = tokenizer; + _errorLogger = errorLogger; +} + +CheckBufferOverrunClass::~CheckBufferOverrunClass() +{ + +} + +// Modified version of 'ReportError' that also reports the callstack +void CheckBufferOverrunClass::ReportError(const std::string &errmsg) +{ + std::ostringstream ostr; + std::list::const_iterator it; + for (it = _callStack.begin(); it != _callStack.end(); it++) + ostr << _tokenizer->fileLine(*it) << " -> "; + ostr << errmsg; + _errorLogger->reportErr(ostr.str()); +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Check array usage.. +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::CheckBufferOverrun_CheckScope(const Token *tok, const char *varname[], const int size, const int total_size, unsigned int varid) +{ + unsigned int varc = 0; + + std::string varnames; + while (varname[varc]) + { + if (varc > 0) + varnames += " . "; + + varnames += varname[varc]; + + ++varc; + } + + if (varc == 0) + varc = 1; + + varc = 2 * (varc - 1); + + // Array index.. + if (varid > 0) + { + if (Token::Match(tok, "%varid% [ %num% ]", varid)) + { + const char *num = tok->strAt(2); + if (std::strtol(num, NULL, 10) >= size) + { + ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); + } + } + } + else if (Token::Match(tok, std::string(varnames + " [ %num% ]").c_str())) + { + const char *num = tok->strAt(2 + varc); + if (std::strtol(num, NULL, 10) >= size) + { + ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); + } + } + + + int indentlevel = 0; + for (; tok; tok = tok->next()) + { + if (tok->str() == "{") + { + ++indentlevel; + } + + else if (tok->str() == "}") + { + --indentlevel; + if (indentlevel < 0) + return; + } + + // Array index.. + if (varid > 0) + { + if (!tok->isName() && !Token::Match(tok, "[.&]") && Token::Match(tok->next(), "%varid% [ %num% ]", varid)) + { + const char *num = tok->strAt(3); + if (std::strtol(num, NULL, 10) >= size) + { + ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); + } + } + } + else if (!tok->isName() && !Token::Match(tok, "[.&]") && Token::Match(tok->next(), std::string(varnames + " [ %num% ]").c_str())) + { + const char *num = tok->next()->strAt(2 + varc); + if (std::strtol(num, NULL, 10) >= size) + { + ReportError(ErrorMessage::arrayIndexOutOfBounds(_tokenizer, tok->next())); + } + tok = tok->tokAt(4); + continue; + } + + + // memset, memcmp, memcpy, strncpy, fgets.. + if (varid > 0) + { + if (Token::Match(tok, "memset|memcpy|memmove|memcmp|strncpy|fgets")) + { + if (Token::Match(tok->next(), "( %varid% , %num% , %num% )", varid) || + Token::Match(tok->next(), "( %var% , %varid% , %num% )", varid)) + { + const char *num = tok->strAt(6); + if (std::atoi(num) > total_size) + { + ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); + } + } + continue; + } + } + else if (Token::Match(tok, "memset|memcpy|memmove|memcmp|strncpy|fgets")) + { + if (Token::Match(tok->next(), std::string("( " + varnames + " , %num% , %num% )").c_str()) || + Token::Match(tok->next(), std::string("( %var% , " + varnames + " , %num% )").c_str())) + { + const char *num = tok->strAt(varc + 6); + if (std::atoi(num) > total_size) + { + ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); + } + } + continue; + } + + + // Loop.. + if (Token::simpleMatch(tok, "for (")) + { + const Token *tok2 = tok->tokAt(2); + + // for - setup.. + if (Token::Match(tok2, "%var% = 0 ;")) + tok2 = tok2->tokAt(4); + else if (Token::Match(tok2, "%type% %var% = 0 ;")) + tok2 = tok2->tokAt(5); + else if (Token::Match(tok2, "%type% %type% %var% = 0 ;")) + tok2 = tok2->tokAt(6); + else + continue; + + // for - condition.. + if (!Token::Match(tok2, "%var% < %num% ;") && !Token::Match(tok2, "%var% <= %num% ;")) + continue; + + // Get index variable and stopsize. + const char *strindex = tok2->aaaa(); + int value = ((tok2->next()->aaaa1() == '=') ? 1 : 0) + std::atoi(tok2->strAt(2)); + if (value <= size) + continue; + + // Goto the end of the for loop.. + while (tok2 && tok2->str() != ")") + tok2 = tok2->next(); + if (!tok2 || !tok2->tokAt(5)) + break; + + std::ostringstream pattern; + pattern << varnames << " [ " << strindex << " ]"; + + int indentlevel2 = 0; + while ((tok2 = tok2->next())) + { + if (tok2->str() == ";" && indentlevel2 == 0) + break; + + if (tok2->str() == "{") + ++indentlevel2; + + if (tok2->str() == "}") + { + --indentlevel2; + if (indentlevel2 <= 0) + break; + } + + if (Token::Match(tok2, pattern.str().c_str())) + { + ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok2)); + break; + } + + } + continue; + } + + + // Writing data into array.. + if (Token::Match(tok, std::string("strcpy ( " + varnames + " , %str% )").c_str())) + { + int len = 0; + const char *str = tok->strAt(varc + 4); + while (*str) + { + if (*str == '\\') + ++str; + ++str; + ++len; + } + if (len > 2 && len >= (int)size + 2) + { + ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); + } + continue; + } + + // sprintf.. + if (varid > 0 && Token::Match(tok, "sprintf ( %varid% , %str% ,", varid)) + { + int len = 0; + for (const Token *tok2 = tok->tokAt(6); tok2 && tok2->str() != ")"; tok2 = tok2->next()) + { + if (tok2->aaaa0() == '\"') + { + len -= 2; + const char *str = tok->strAt(0); + while (*str) + { + if (*str == '\\') + ++str; + ++str; + ++len; + } + } + } + if (len > (int)size) + { + ReportError(ErrorMessage::bufferOverrun(_tokenizer, tok)); + } + } + + // snprintf.. + if (varid > 0 && Token::Match(tok, "snprintf ( %varid% , %num%", varid)) + { + int n = std::atoi(tok->strAt(4)); + if (n > size) + ReportError(ErrorMessage::outOfBounds(_tokenizer, tok->tokAt(4), "snprintf size")); + } + + + // Function call.. + // It's not interesting to check what happens when the whole struct is + // sent as the parameter, that is checked separately anyway. + if (Token::Match(tok, "%var% (")) + { + // Don't make recursive checking.. + if (std::find(_callStack.begin(), _callStack.end(), tok) != _callStack.end()) + continue; + + // Only perform this checking if showAll setting is enabled.. + if (!_settings._showAll) + continue; + + unsigned int parlevel = 0, par = 0; + for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + if (tok2->str() == "(") + { + ++parlevel; + } + + else if (tok2->str() == ")") + { + --parlevel; + if (parlevel < 1) + { + par = 0; + break; + } + } + + else if (parlevel == 1 && (tok2->str() == ",")) + { + ++par; + } + + if (parlevel == 1 && Token::Match(tok2, std::string("[(,] " + varnames + " [,)]").c_str())) + { + ++par; + break; + } + } + + if (par == 0) + continue; + + // Find function.. + const Token *ftok = _tokenizer->GetFunctionTokenByName(tok->aaaa()); + if (!ftok) + continue; + + // Parse head of function.. + ftok = ftok->tokAt(2); + parlevel = 1; + while (ftok && parlevel == 1 && par >= 1) + { + if (ftok->str() == "(") + ++parlevel; + + else if (ftok->str() == ")") + --parlevel; + + else if (ftok->str() == ",") + --par; + + else if (par == 1 && parlevel == 1 && Token::Match(ftok, "%var% [,)]")) + { + // Parameter name.. + const char *parname[2]; + parname[0] = ftok->aaaa(); + parname[1] = 0; + + // Goto function body.. + while (ftok && (ftok->str() != "{")) + ftok = ftok->next(); + ftok = ftok ? ftok->next() : 0; + + // Check variable usage in the function.. + _callStack.push_back(tok); + CheckBufferOverrun_CheckScope(ftok, parname, size, total_size, 0); + _callStack.pop_back(); + + // break out.. + break; + } + + ftok = ftok->next(); + } + } + } +} + + +//--------------------------------------------------------------------------- +// Checking local variables in a scope +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::CheckBufferOverrun_LocalVariable() +{ + int indentlevel = 0; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() == "{") + ++indentlevel; + + else if (tok->str() == "}") + --indentlevel; + + else if (indentlevel > 0) + { + const char *varname[2] = {0}; + unsigned int size = 0; + const char *type = 0; + unsigned int varid = 0; + int nextTok = 0; + + if (Token::Match(tok, "%type% %var% [ %num% ] ;")) + { + varname[0] = tok->strAt(1); + size = std::strtoul(tok->strAt(3), NULL, 10); + type = tok->aaaa(); + varid = tok->tokAt(1)->varId(); + nextTok = 6; + } + else if (Token::Match(tok, "[*;{}] %var% = new %type% [ %num% ]")) + { + varname[0] = tok->strAt(1); + size = std::strtoul(tok->strAt(6), NULL, 10); + type = tok->strAt(4); + varid = tok->tokAt(1)->varId(); + nextTok = 8; + } + else + { + continue; + } + + int total_size = size * _tokenizer->SizeOfType(type); + if (total_size == 0) + continue; + + // The callstack is empty + _callStack.clear(); + CheckBufferOverrun_CheckScope(tok->tokAt(nextTok), varname, size, total_size, varid); + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Checking member variables of structs.. +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::CheckBufferOverrun_StructVariable() +{ + const char declstruct[] = "struct|class %var% {"; + for (const Token *tok = Token::findmatch(_tokenizer->tokens(), declstruct); + tok; tok = Token::findmatch(tok->next(), declstruct)) + { + const std::string &structname = tok->next()->str(); + + // Found a struct declaration. Search for arrays.. + for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "}") + break; + + int ivar = 0; + if (Token::Match(tok2->next(), "%type% %var% [ %num% ] ;")) + ivar = 2; + else if (Token::Match(tok2->next(), "%type% %type% %var% [ %num% ] ;")) + ivar = 3; + else if (Token::Match(tok2->next(), "%type% * %var% [ %num% ] ;")) + ivar = 3; + else if (Token::Match(tok2->next(), "%type% %type% * %var% [ %num% ] ;")) + ivar = 4; + else + continue; + + const char *varname[3] = {0, 0, 0}; + varname[1] = tok2->strAt(ivar); + int arrsize = std::atoi(tok2->strAt(ivar + 2)); + int total_size = arrsize * _tokenizer->SizeOfType(tok2->next()->aaaa()); + if (total_size == 0) + continue; + + + // Class member variable => Check functions + if (tok->str() == "class") + { + std::string func_pattern(structname + " :: %var% ("); + const Token *tok3 = Token::findmatch(_tokenizer->tokens(), func_pattern.c_str()); + while (tok3) + { + for (const Token *tok4 = tok3; tok4; tok4 = tok4->next()) + { + if (Token::Match(tok4, "[;{}]")) + break; + + if (Token::simpleMatch(tok4, ") {")) + { + const char *names[2] = {varname[1], 0}; + CheckBufferOverrun_CheckScope(tok4->tokAt(2), names, arrsize, total_size, 0); + break; + } + } + tok3 = Token::findmatch(tok3->next(), func_pattern.c_str()); + } + } + + for (const Token *tok3 = _tokenizer->tokens(); tok3; tok3 = tok3->next()) + { + if (tok3->str() != structname) + continue; + + // Declare variable: Fred fred1; + if (Token::Match(tok3->next(), "%var% ;")) + varname[0] = tok3->strAt(1); + + // Declare pointer: Fred *fred1 + else if (Token::Match(tok3->next(), "* %var% [,);=]")) + varname[0] = tok3->strAt(2); + + else + continue; + + + // Goto end of statement. + const Token *CheckTok = NULL; + while (tok3) + { + // End of statement. + if (tok3->str() == ";") + { + CheckTok = tok3; + break; + } + + // End of function declaration.. + if (Token::simpleMatch(tok3, ") ;")) + break; + + // Function implementation.. + if (Token::simpleMatch(tok3, ") {")) + { + CheckTok = tok3->tokAt(2); + break; + } + + tok3 = tok3->next(); + } + + if (!tok3) + break; + + if (!CheckTok) + continue; + + // Check variable usage.. + CheckBufferOverrun_CheckScope(CheckTok, varname, arrsize, total_size, 0); + } + } + } +} +//--------------------------------------------------------------------------- + + + +void CheckBufferOverrunClass::bufferOverrun() +{ + CheckBufferOverrun_LocalVariable(); + CheckBufferOverrun_StructVariable(); +} +//--------------------------------------------------------------------------- + + + + + + + + +//--------------------------------------------------------------------------- +// Dangerous functions +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::dangerousFunctions() +{ + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (Token::Match(tok, "gets|scanf (")) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Found '" << tok->str() << "'. You should use 'fgets' instead"; + _errorLogger->reportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + + + + diff --git a/src/checkbufferoverrun.h b/src/checkbufferoverrun.h index 17d4c13d6..e4ffb0a28 100644 --- a/src/checkbufferoverrun.h +++ b/src/checkbufferoverrun.h @@ -1,64 +1,64 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 _callStack; -}; - -//--------------------------------------------------------------------------- -#endif - +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 _callStack; +}; + +//--------------------------------------------------------------------------- +#endif + diff --git a/src/checkother.cpp b/src/checkother.cpp index d832f866d..75fdfd251 100644 --- a/src/checkother.cpp +++ b/src/checkother.cpp @@ -1,813 +1,813 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 -#include -#include // <- atoi -#include -#include -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// Warning on C-Style casts.. p = (kalle *)foo; -//--------------------------------------------------------------------------- - -CheckOther::CheckOther(const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger) - : _settings(settings) -{ - _tokenizer = tokenizer; - _errorLogger = errorLogger; -} - -CheckOther::~CheckOther() -{ - -} - -void CheckOther::WarningOldStylePointerCast() -{ - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Old style pointer casting.. - if (!Token::Match(tok, "( %type% * ) %var%")) - continue; - - // Is "type" a class? - const std::string pattern("class " + tok->next()->str()); - if (!Token::findmatch(_tokenizer->tokens(), pattern.c_str())) - continue; - - _errorLogger->reportErr(ErrorMessage::cstyleCast(_tokenizer, tok)); - } -} - - - - -//--------------------------------------------------------------------------- -// Redundant code.. -//--------------------------------------------------------------------------- - -void CheckOther::WarningRedundantCode() -{ - - // if (p) delete p - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->str() != "if") - continue; - - const char *varname1 = NULL; - const Token *tok2 = NULL; - - if (Token::Match(tok, "if ( %var% )")) - { - varname1 = tok->strAt(2); - tok2 = tok->tokAt(4); - } - else if (Token::Match(tok, "if ( %var% != NULL )")) - { - varname1 = tok->strAt(2); - tok2 = tok->tokAt(6); - } - - if (varname1 == NULL || tok2 == NULL) - continue; - - bool err = false; - if (tok2->str() == "{") - { - tok2 = tok2->next(); - - if (Token::Match(tok2, "delete %var% ; }")) - { - err = (strcmp(tok2->strAt(1), varname1) == 0); - } - else if (Token::Match(tok2, "delete [ ] %var% ; }")) - { - err = (strcmp(tok2->strAt(1), varname1) == 0); - } - else if (Token::Match(tok2, "free ( %var% ) ; }")) - { - err = (strcmp(tok2->strAt(2), varname1) == 0); - } - else if (Token::Match(tok2, "kfree ( %var% ) ; }")) - { - err = (strcmp(tok2->strAt(2), varname1) == 0); - } - } - else - { - if (Token::Match(tok2, "delete %var% ;")) - { - err = (strcmp(tok2->strAt(1), varname1) == 0); - } - else if (Token::Match(tok2, "delete [ ] %var% ;")) - { - err = (strcmp(tok2->strAt(1), varname1) == 0); - } - else if (Token::Match(tok2, "free ( %var% ) ;")) - { - err = (strcmp(tok2->strAt(2), varname1) == 0); - } - else if (Token::Match(tok2, "kfree ( %var% ) ;")) - { - err = (strcmp(tok2->strAt(2), varname1) == 0); - } - } - - if (err) - { - _errorLogger->reportErr(ErrorMessage::redundantIfDelete0(_tokenizer, tok)); - } - } - - - - // Redundant condition - // if (haystack.find(needle) != haystack.end()) - // haystack.remove(needle); - redundantCondition2(); -} -//--------------------------------------------------------------------------- - -void CheckOther::redundantCondition2() -{ - const char pattern[] = "if ( %var% . find ( %any% ) != %var% . end ( ) ) " - "{|{|" - " %var% . remove ( %any% ) ; " - "}|}|"; - const Token *tok = Token::findmatch(_tokenizer->tokens(), pattern); - while (tok) - { - bool b = Token::Match(tok->tokAt(15), "{"); - - // Get tokens for the fields %var% and %any% - const Token *var1 = tok->tokAt(2); - const Token *any1 = tok->tokAt(6); - const Token *var2 = tok->tokAt(9); - const Token *var3 = tok->tokAt(b ? 16 : 15); - const Token *any2 = tok->tokAt(b ? 20 : 19); - - // Check if all the "%var%" fields are the same and if all the "%any%" are the same.. - if (var1->str() == var2->str() && - var2->str() == var3->str() && - any1->str() == any2->str()) - { - _errorLogger->reportErr(ErrorMessage::redundantIfRemove(_tokenizer, tok)); - } - - tok = Token::findmatch(tok->next(), pattern); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// if (condition) .... -//--------------------------------------------------------------------------- - -void CheckOther::WarningIf() -{ - if (ErrorMessage::ifNoAction(_settings)) - { - // Search for 'if (condition);' - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (!Token::simpleMatch(tok, "if (")) - continue; - - // Search for the end paranthesis for the condition.. - int parlevel = 0; - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "(") - ++parlevel; - else if (tok2->str() == ")") - { - --parlevel; - if (parlevel <= 0) - { - if (Token::Match(tok2, ") ; !!else")) - { - _errorLogger->reportErr(ErrorMessage::ifNoAction(_tokenizer, tok)); - } - break; - } - } - } - } - } - - if (ErrorMessage::conditionAlwaysTrueFalse(_settings)) - { - // Search for 'a=b; if (a==b)' - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Begin statement? - if (! Token::Match(tok, "[;{}]")) - continue; - tok = tok->next(); - if (! tok) - break; - - if (!Token::Match(tok, "%var% = %var% ; if ( %var%")) - continue; - - if (strcmp(tok->strAt(9), ")") != 0) - continue; - - // var1 = var2 ; if ( var3 cond var4 ) - const char *var1 = tok->strAt(0); - const char *var2 = tok->strAt(2); - const char *var3 = tok->strAt(6); - const char *cond = tok->strAt(7); - const char *var4 = tok->strAt(8); - - // Check that var3 is equal with either var1 or var2 - if (strcmp(var1, var3) && strcmp(var2, var3)) - continue; - - // Check that var4 is equal with either var1 or var2 - if (strcmp(var1, var4) && strcmp(var2, var4)) - continue; - - // Check that there is a condition.. - const char *p[6] = {"==", "<=", ">=", "!=", "<", ">"}; - bool iscond = false; - for (int i = 0; i < 6; i++) - { - if (strcmp(cond, p[i]) == 0) - { - iscond = true; - break; - } - } - if (!iscond) - break; - - // we found the error. Report. - bool b = false; - for (int i = 0; i < 6; i++) - { - if (strcmp(cond, p[i]) == 0) - b = (i < 3); - } - _errorLogger->reportErr(ErrorMessage::conditionAlwaysTrueFalse(_tokenizer, tok->tokAt(4), b ? "True" : "False")); - } - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// strtol(str, 0, radix) <- radix must be 0 or 2-36 -//--------------------------------------------------------------------------- - -void CheckOther::InvalidFunctionUsage() -{ - // strtol and strtoul.. - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if ((tok->str() != "strtol") && (tok->str() != "strtoul")) - continue; - - // Locate the third parameter of the function call.. - int parlevel = 0; - int param = 1; - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) - { - if (Token::Match(tok2, "(")) - ++parlevel; - else if (Token::Match(tok2, ")")) - --parlevel; - else if (parlevel == 1 && Token::Match(tok2, ",")) - { - ++param; - if (param == 3) - { - if (Token::Match(tok2, ", %num% )")) - { - int radix = atoi(tok2->strAt(1)); - if (!(radix == 0 || (radix >= 2 && radix <= 36))) - { - _errorLogger->reportErr(ErrorMessage::dangerousUsageStrtol(_tokenizer, tok2)); - } - } - break; - } - } - } - } - - // sprintf|snprintf overlapping data - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Get variable id of target buffer.. - unsigned int varid = 0; - - if (Token::Match(tok, "sprintf|snprintf ( %var% ,")) - varid = tok->tokAt(2)->varId(); - - else if (Token::Match(tok, "sprintf|snprintf ( %var% . %var% ,")) - varid = tok->tokAt(4)->varId(); - - if (varid == 0) - continue; - - // goto "," - const Token *tok2 = tok->tokAt(3); - while (tok2 && tok2->str() != ",") - tok2 = tok2->next(); - - // is any source buffer overlapping the target buffer? - int parlevel = 0; - while ((tok2 = tok2->next()) != NULL) - { - if (tok2->str() == "(") - ++parlevel; - else if (tok2->str() == ")") - { - --parlevel; - if (parlevel < 0) - break; - } - else if (parlevel == 0 && tok2->varId() == varid) - { - _errorLogger->reportErr(ErrorMessage::sprintfOverlappingData(_tokenizer, tok2, tok2->str())); - break; - } - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Check for unsigned divisions -//--------------------------------------------------------------------------- - -void CheckOther::CheckUnsignedDivision() -{ - // Check for "ivar / uvar" and "uvar / ivar" - std::map varsign; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) - { - const char *type = tok->strAt(1); - if (strcmp(type, "char") == 0 || strcmp(type, "short") == 0 || strcmp(type, "int") == 0) - varsign[tok->strAt(2)] = 's'; - } - - else if (Token::Match(tok, "[{};(,] unsigned %type% %var% [;=,)]")) - varsign[tok->strAt(3)] = 'u'; - - else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %var%")) - { - if (ErrorMessage::udivWarning(_settings)) - { - const char *varname1 = tok->strAt(1); - const char *varname2 = tok->strAt(3); - char sign1 = varsign[varname1]; - char sign2 = varsign[varname2]; - - if (sign1 && sign2 && sign1 != sign2) - { - // One of the operands are signed, the other is unsigned.. - _errorLogger->reportErr(ErrorMessage::udivWarning(_tokenizer, tok->next())); - } - } - } - - else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / - %num%")) - { - if (ErrorMessage::udivError()) - { - const char *varname1 = tok->strAt(1); - char sign1 = varsign[varname1]; - if (sign1 == 'u') - { - _errorLogger->reportErr(ErrorMessage::udivError(_tokenizer, tok->next())); - } - } - } - - else if (Token::Match(tok, "[([=*/+-] - %num% / %var%")) - { - if (ErrorMessage::udivError()) - { - const char *varname2 = tok->strAt(4); - char sign2 = varsign[varname2]; - if (sign2 == 'u') - { - _errorLogger->reportErr(ErrorMessage::udivError(_tokenizer, tok->next())); - } - } - } - } -} -//--------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------- -// Check scope of variables.. -//--------------------------------------------------------------------------- - - -void CheckOther::CheckVariableScope() -{ - // Walk through all tokens.. - bool func = false; - int indentlevel = 0; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Skip class and struct declarations.. - if ((tok->str() == "class") || (tok->str() == "struct")) - { - for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == "{") - { - int indentlevel2 = 0; - for (tok = tok2; tok; tok = tok->next()) - { - if (tok->str() == "{") - { - ++indentlevel2; - } - if (tok->str() == "}") - { - --indentlevel2; - if (indentlevel2 <= 0) - { - tok = tok->next(); - break; - } - } - } - break; - } - if (Token::Match(tok2, "[,);]")) - { - break; - } - } - if (! tok) - break; - } - - if (tok->str() == "{") - { - ++indentlevel; - } - if (tok->str() == "}") - { - --indentlevel; - if (indentlevel == 0) - func = false; - } - if (indentlevel == 0 && Token::Match(tok, ") {")) - { - func = true; - } - if (indentlevel > 0 && func && Token::Match(tok, "[{};]")) - { - // First token of statement.. - const Token *tok1 = tok->next(); - if (! tok1) - continue; - - if ((tok1->str() == "return") || - (tok1->str() == "delete") || - (tok1->str() == "goto") || - (tok1->str() == "else")) - continue; - - // Variable declaration? - if (Token::Match(tok1, "%var% %var% ;") || - Token::Match(tok1, "%var% %var% =")) - { - CheckVariableScope_LookupVar(tok1, tok1->strAt(1)); - } - } - } - -} -//--------------------------------------------------------------------------- - -void CheckOther::CheckVariableScope_LookupVar(const Token *tok1, const char varname[]) -{ - const Token *tok = tok1; - - // Skip the variable declaration.. - while (tok && !Token::Match(tok, ";")) - tok = tok->next(); - - // Check if the variable is used in this indentlevel.. - bool used = false, used1 = false; - int indentlevel = 0; - int parlevel = 0; - bool for_or_while = false; - while (indentlevel >= 0 && tok) - { - if (tok->str() == "{") - { - ++indentlevel; - } - - else if (tok->str() == "}") - { - --indentlevel; - if (indentlevel == 0) - { - if (for_or_while && used) - return; - used1 = used; - used = false; - } - } - - else if (tok->str() == "(") - { - ++parlevel; - } - - else if (tok->str() == ")") - { - --parlevel; - } - - - else if (tok->str() == varname) - { - if (indentlevel == 0 || used1) - return; - used = true; - } - - else if (indentlevel == 0) - { - if ((tok->str() == "for") || (tok->str() == "while")) - for_or_while = true; - if (parlevel == 0 && (tok->str() == ";")) - for_or_while = false; - } - - tok = tok->next(); - } - - // Warning if "used" is true - _errorLogger->reportErr(ErrorMessage::variableScope(_tokenizer, tok1, varname)); -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Check for constant function parameters -//--------------------------------------------------------------------------- - -void CheckOther::CheckConstantFunctionParameter() -{ - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (Token::Match(tok, "[,(] const std :: %type% %var% [,)]")) - { - _errorLogger->reportErr(ErrorMessage::passedByValue(_tokenizer, tok, tok->strAt(5))); - } - - else if (Token::Match(tok, "[,(] const %type% %var% [,)]")) - { - // Check if type is a struct or class. - const std::string pattern(std::string("class|struct ") + tok->strAt(2)); - if (Token::findmatch(_tokenizer->tokens(), pattern.c_str())) - { - _errorLogger->reportErr(ErrorMessage::passedByValue(_tokenizer, tok, tok->strAt(3))); - } - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Check that all struct members are used -//--------------------------------------------------------------------------- - -void CheckOther::CheckStructMemberUsage() -{ - const char *structname = 0; - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->fileIndex() != 0) - continue; - - if (Token::Match(tok, "struct|union %type% {")) - { - structname = tok->strAt(1); - - // Bail out if struct/union contain any functions - for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "(") - { - structname = 0; - break; - } - - if (tok2->str() == "}") - break; - } - } - - if (tok->str() == "}") - structname = 0; - - if (structname && Token::Match(tok, "[{;]")) - { - const char *varname = 0; - if (Token::Match(tok->next(), "%type% %var% [;[]")) - varname = tok->strAt(2); - else if (Token::Match(tok->next(), "%type% %type% %var% [;[]")) - varname = tok->strAt(3); - else if (Token::Match(tok->next(), "%type% * %var% [;[]")) - varname = tok->strAt(3); - else if (Token::Match(tok->next(), "%type% %type% * %var% [;[]")) - varname = tok->strAt(4); - else - continue; - - const std::string usagePattern(". " + std::string(varname)); - bool used = false; - for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) - { - if (Token::simpleMatch(tok2, usagePattern.c_str())) - { - used = true; - break; - } - } - - if (! used) - { - _errorLogger->reportErr(ErrorMessage::unusedStructMember(_tokenizer, tok->next(), structname, varname)); - } - } - } -} - - - - - -//--------------------------------------------------------------------------- -// Check usage of char variables.. -//--------------------------------------------------------------------------- - -void CheckOther::CheckCharVariable() -{ - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Declaring the variable.. - if (Token::Match(tok, "[{};(,] char %var% [;=,)]")) - { - // Set tok to point to the variable name - tok = tok->tokAt(2); - - // Check usage of char variable.. - int indentlevel = 0; - for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "{") - ++indentlevel; - - else if (tok2->str() == "}") - { - --indentlevel; - if (indentlevel <= 0) - break; - } - - std::string temp = "%var% [ " + tok->str() + " ]"; - if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str())) - { - _errorLogger->reportErr(ErrorMessage::charArrayIndex(_tokenizer, tok2->next())); - break; - } - - std::string tempFirst = "%var% [&|] " + tok->str(); - std::string tempSecond = tok->str() + " [&|]"; - if (Token::Match(tok2, tempFirst.c_str()) || Token::Match(tok2, tempSecond.c_str())) - { - _errorLogger->reportErr(ErrorMessage::charBitOp(_tokenizer, tok2)); - break; - } - } - } - } -} -//--------------------------------------------------------------------------- - - - - - - -//--------------------------------------------------------------------------- -// Incomplete statement.. -//--------------------------------------------------------------------------- - -void CheckOther::CheckIncompleteStatement() -{ - int parlevel = 0; - - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->str() == "(") - ++parlevel; - else if (tok->str() == ")") - --parlevel; - - if (parlevel != 0) - continue; - - if ((tok->str() != "#") && Token::Match(tok->next(), "; %str%") && !Token::Match(tok->tokAt(3), ",")) - { - _errorLogger->reportErr(ErrorMessage::constStatement(_tokenizer, tok->next(), "string")); - } - - if (!Token::Match(tok, "#") && Token::Match(tok->next(), "; %num%") && !Token::Match(tok->tokAt(3), ",")) - { - _errorLogger->reportErr(ErrorMessage::constStatement(_tokenizer, tok->next(), "numeric")); - } - } -} -//--------------------------------------------------------------------------- - - - - - - -//--------------------------------------------------------------------------- -// str plus char -//--------------------------------------------------------------------------- - -void CheckOther::strPlusChar() -{ - bool charVars[10000] = {0}; - - for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Declaring char variable.. - if (Token::Match(tok, "char %var% [;=]")) - { - unsigned int varid = tok->next()->varId(); - if (varid > 0 && varid < 10000) - charVars[varid] = true; - } - - // - else if (Token::Match(tok, "[=(] %str% + %any%")) - { - // char constant.. - const char *s = tok->strAt(3); - if (*s == '\'') - _errorLogger->reportErr(ErrorMessage::strPlusChar(_tokenizer, tok->next())); - - // char variable.. - unsigned int varid = tok->tokAt(3)->varId(); - if (varid > 0 && varid < 10000 && charVars[varid]) - _errorLogger->reportErr(ErrorMessage::strPlusChar(_tokenizer, tok->next())); - } - } -} +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 +#include +#include // <- atoi +#include +#include +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// Warning on C-Style casts.. p = (kalle *)foo; +//--------------------------------------------------------------------------- + +CheckOther::CheckOther(const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger) + : _settings(settings) +{ + _tokenizer = tokenizer; + _errorLogger = errorLogger; +} + +CheckOther::~CheckOther() +{ + +} + +void CheckOther::WarningOldStylePointerCast() +{ + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Old style pointer casting.. + if (!Token::Match(tok, "( %type% * ) %var%")) + continue; + + // Is "type" a class? + const std::string pattern("class " + tok->next()->str()); + if (!Token::findmatch(_tokenizer->tokens(), pattern.c_str())) + continue; + + _errorLogger->reportErr(ErrorMessage::cstyleCast(_tokenizer, tok)); + } +} + + + + +//--------------------------------------------------------------------------- +// Redundant code.. +//--------------------------------------------------------------------------- + +void CheckOther::WarningRedundantCode() +{ + + // if (p) delete p + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() != "if") + continue; + + const char *varname1 = NULL; + const Token *tok2 = NULL; + + if (Token::Match(tok, "if ( %var% )")) + { + varname1 = tok->strAt(2); + tok2 = tok->tokAt(4); + } + else if (Token::Match(tok, "if ( %var% != NULL )")) + { + varname1 = tok->strAt(2); + tok2 = tok->tokAt(6); + } + + if (varname1 == NULL || tok2 == NULL) + continue; + + bool err = false; + if (tok2->str() == "{") + { + tok2 = tok2->next(); + + if (Token::Match(tok2, "delete %var% ; }")) + { + err = (strcmp(tok2->strAt(1), varname1) == 0); + } + else if (Token::Match(tok2, "delete [ ] %var% ; }")) + { + err = (strcmp(tok2->strAt(1), varname1) == 0); + } + else if (Token::Match(tok2, "free ( %var% ) ; }")) + { + err = (strcmp(tok2->strAt(2), varname1) == 0); + } + else if (Token::Match(tok2, "kfree ( %var% ) ; }")) + { + err = (strcmp(tok2->strAt(2), varname1) == 0); + } + } + else + { + if (Token::Match(tok2, "delete %var% ;")) + { + err = (strcmp(tok2->strAt(1), varname1) == 0); + } + else if (Token::Match(tok2, "delete [ ] %var% ;")) + { + err = (strcmp(tok2->strAt(1), varname1) == 0); + } + else if (Token::Match(tok2, "free ( %var% ) ;")) + { + err = (strcmp(tok2->strAt(2), varname1) == 0); + } + else if (Token::Match(tok2, "kfree ( %var% ) ;")) + { + err = (strcmp(tok2->strAt(2), varname1) == 0); + } + } + + if (err) + { + _errorLogger->reportErr(ErrorMessage::redundantIfDelete0(_tokenizer, tok)); + } + } + + + + // Redundant condition + // if (haystack.find(needle) != haystack.end()) + // haystack.remove(needle); + redundantCondition2(); +} +//--------------------------------------------------------------------------- + +void CheckOther::redundantCondition2() +{ + const char pattern[] = "if ( %var% . find ( %any% ) != %var% . end ( ) ) " + "{|{|" + " %var% . remove ( %any% ) ; " + "}|}|"; + const Token *tok = Token::findmatch(_tokenizer->tokens(), pattern); + while (tok) + { + bool b = Token::Match(tok->tokAt(15), "{"); + + // Get tokens for the fields %var% and %any% + const Token *var1 = tok->tokAt(2); + const Token *any1 = tok->tokAt(6); + const Token *var2 = tok->tokAt(9); + const Token *var3 = tok->tokAt(b ? 16 : 15); + const Token *any2 = tok->tokAt(b ? 20 : 19); + + // Check if all the "%var%" fields are the same and if all the "%any%" are the same.. + if (var1->str() == var2->str() && + var2->str() == var3->str() && + any1->str() == any2->str()) + { + _errorLogger->reportErr(ErrorMessage::redundantIfRemove(_tokenizer, tok)); + } + + tok = Token::findmatch(tok->next(), pattern); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// if (condition) .... +//--------------------------------------------------------------------------- + +void CheckOther::WarningIf() +{ + if (ErrorMessage::ifNoAction(_settings)) + { + // Search for 'if (condition);' + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (!Token::simpleMatch(tok, "if (")) + continue; + + // Search for the end paranthesis for the condition.. + int parlevel = 0; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "(") + ++parlevel; + else if (tok2->str() == ")") + { + --parlevel; + if (parlevel <= 0) + { + if (Token::Match(tok2, ") ; !!else")) + { + _errorLogger->reportErr(ErrorMessage::ifNoAction(_tokenizer, tok)); + } + break; + } + } + } + } + } + + if (ErrorMessage::conditionAlwaysTrueFalse(_settings)) + { + // Search for 'a=b; if (a==b)' + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Begin statement? + if (! Token::Match(tok, "[;{}]")) + continue; + tok = tok->next(); + if (! tok) + break; + + if (!Token::Match(tok, "%var% = %var% ; if ( %var%")) + continue; + + if (strcmp(tok->strAt(9), ")") != 0) + continue; + + // var1 = var2 ; if ( var3 cond var4 ) + const char *var1 = tok->strAt(0); + const char *var2 = tok->strAt(2); + const char *var3 = tok->strAt(6); + const char *cond = tok->strAt(7); + const char *var4 = tok->strAt(8); + + // Check that var3 is equal with either var1 or var2 + if (strcmp(var1, var3) && strcmp(var2, var3)) + continue; + + // Check that var4 is equal with either var1 or var2 + if (strcmp(var1, var4) && strcmp(var2, var4)) + continue; + + // Check that there is a condition.. + const char *p[6] = {"==", "<=", ">=", "!=", "<", ">"}; + bool iscond = false; + for (int i = 0; i < 6; i++) + { + if (strcmp(cond, p[i]) == 0) + { + iscond = true; + break; + } + } + if (!iscond) + break; + + // we found the error. Report. + bool b = false; + for (int i = 0; i < 6; i++) + { + if (strcmp(cond, p[i]) == 0) + b = (i < 3); + } + _errorLogger->reportErr(ErrorMessage::conditionAlwaysTrueFalse(_tokenizer, tok->tokAt(4), b ? "True" : "False")); + } + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// strtol(str, 0, radix) <- radix must be 0 or 2-36 +//--------------------------------------------------------------------------- + +void CheckOther::InvalidFunctionUsage() +{ + // strtol and strtoul.. + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if ((tok->str() != "strtol") && (tok->str() != "strtoul")) + continue; + + // Locate the third parameter of the function call.. + int parlevel = 0; + int param = 1; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) + { + if (Token::Match(tok2, "(")) + ++parlevel; + else if (Token::Match(tok2, ")")) + --parlevel; + else if (parlevel == 1 && Token::Match(tok2, ",")) + { + ++param; + if (param == 3) + { + if (Token::Match(tok2, ", %num% )")) + { + int radix = std::atoi(tok2->strAt(1)); + if (!(radix == 0 || (radix >= 2 && radix <= 36))) + { + _errorLogger->reportErr(ErrorMessage::dangerousUsageStrtol(_tokenizer, tok2)); + } + } + break; + } + } + } + } + + // sprintf|snprintf overlapping data + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Get variable id of target buffer.. + unsigned int varid = 0; + + if (Token::Match(tok, "sprintf|snprintf ( %var% ,")) + varid = tok->tokAt(2)->varId(); + + else if (Token::Match(tok, "sprintf|snprintf ( %var% . %var% ,")) + varid = tok->tokAt(4)->varId(); + + if (varid == 0) + continue; + + // goto "," + const Token *tok2 = tok->tokAt(3); + while (tok2 && tok2->str() != ",") + tok2 = tok2->next(); + + // is any source buffer overlapping the target buffer? + int parlevel = 0; + while ((tok2 = tok2->next()) != NULL) + { + if (tok2->str() == "(") + ++parlevel; + else if (tok2->str() == ")") + { + --parlevel; + if (parlevel < 0) + break; + } + else if (parlevel == 0 && tok2->varId() == varid) + { + _errorLogger->reportErr(ErrorMessage::sprintfOverlappingData(_tokenizer, tok2, tok2->str())); + break; + } + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Check for unsigned divisions +//--------------------------------------------------------------------------- + +void CheckOther::CheckUnsignedDivision() +{ + // Check for "ivar / uvar" and "uvar / ivar" + std::map varsign; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) + { + const char *type = tok->strAt(1); + if (strcmp(type, "char") == 0 || strcmp(type, "short") == 0 || strcmp(type, "int") == 0) + varsign[tok->strAt(2)] = 's'; + } + + else if (Token::Match(tok, "[{};(,] unsigned %type% %var% [;=,)]")) + varsign[tok->strAt(3)] = 'u'; + + else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %var%")) + { + if (ErrorMessage::udivWarning(_settings)) + { + const char *varname1 = tok->strAt(1); + const char *varname2 = tok->strAt(3); + char sign1 = varsign[varname1]; + char sign2 = varsign[varname2]; + + if (sign1 && sign2 && sign1 != sign2) + { + // One of the operands are signed, the other is unsigned.. + _errorLogger->reportErr(ErrorMessage::udivWarning(_tokenizer, tok->next())); + } + } + } + + else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / - %num%")) + { + if (ErrorMessage::udivError()) + { + const char *varname1 = tok->strAt(1); + char sign1 = varsign[varname1]; + if (sign1 == 'u') + { + _errorLogger->reportErr(ErrorMessage::udivError(_tokenizer, tok->next())); + } + } + } + + else if (Token::Match(tok, "[([=*/+-] - %num% / %var%")) + { + if (ErrorMessage::udivError()) + { + const char *varname2 = tok->strAt(4); + char sign2 = varsign[varname2]; + if (sign2 == 'u') + { + _errorLogger->reportErr(ErrorMessage::udivError(_tokenizer, tok->next())); + } + } + } + } +} +//--------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// Check scope of variables.. +//--------------------------------------------------------------------------- + + +void CheckOther::CheckVariableScope() +{ + // Walk through all tokens.. + bool func = false; + int indentlevel = 0; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Skip class and struct declarations.. + if ((tok->str() == "class") || (tok->str() == "struct")) + { + for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + if (tok2->str() == "{") + { + int indentlevel2 = 0; + for (tok = tok2; tok; tok = tok->next()) + { + if (tok->str() == "{") + { + ++indentlevel2; + } + if (tok->str() == "}") + { + --indentlevel2; + if (indentlevel2 <= 0) + { + tok = tok->next(); + break; + } + } + } + break; + } + if (Token::Match(tok2, "[,);]")) + { + break; + } + } + if (! tok) + break; + } + + if (tok->str() == "{") + { + ++indentlevel; + } + if (tok->str() == "}") + { + --indentlevel; + if (indentlevel == 0) + func = false; + } + if (indentlevel == 0 && Token::Match(tok, ") {")) + { + func = true; + } + if (indentlevel > 0 && func && Token::Match(tok, "[{};]")) + { + // First token of statement.. + const Token *tok1 = tok->next(); + if (! tok1) + continue; + + if ((tok1->str() == "return") || + (tok1->str() == "delete") || + (tok1->str() == "goto") || + (tok1->str() == "else")) + continue; + + // Variable declaration? + if (Token::Match(tok1, "%var% %var% ;") || + Token::Match(tok1, "%var% %var% =")) + { + CheckVariableScope_LookupVar(tok1, tok1->strAt(1)); + } + } + } + +} +//--------------------------------------------------------------------------- + +void CheckOther::CheckVariableScope_LookupVar(const Token *tok1, const char varname[]) +{ + const Token *tok = tok1; + + // Skip the variable declaration.. + while (tok && !Token::Match(tok, ";")) + tok = tok->next(); + + // Check if the variable is used in this indentlevel.. + bool used = false, used1 = false; + int indentlevel = 0; + int parlevel = 0; + bool for_or_while = false; + while (indentlevel >= 0 && tok) + { + if (tok->str() == "{") + { + ++indentlevel; + } + + else if (tok->str() == "}") + { + --indentlevel; + if (indentlevel == 0) + { + if (for_or_while && used) + return; + used1 = used; + used = false; + } + } + + else if (tok->str() == "(") + { + ++parlevel; + } + + else if (tok->str() == ")") + { + --parlevel; + } + + + else if (tok->str() == varname) + { + if (indentlevel == 0 || used1) + return; + used = true; + } + + else if (indentlevel == 0) + { + if ((tok->str() == "for") || (tok->str() == "while")) + for_or_while = true; + if (parlevel == 0 && (tok->str() == ";")) + for_or_while = false; + } + + tok = tok->next(); + } + + // Warning if "used" is true + _errorLogger->reportErr(ErrorMessage::variableScope(_tokenizer, tok1, varname)); +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Check for constant function parameters +//--------------------------------------------------------------------------- + +void CheckOther::CheckConstantFunctionParameter() +{ + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (Token::Match(tok, "[,(] const std :: %type% %var% [,)]")) + { + _errorLogger->reportErr(ErrorMessage::passedByValue(_tokenizer, tok, tok->strAt(5))); + } + + else if (Token::Match(tok, "[,(] const %type% %var% [,)]")) + { + // Check if type is a struct or class. + const std::string pattern(std::string("class|struct ") + tok->strAt(2)); + if (Token::findmatch(_tokenizer->tokens(), pattern.c_str())) + { + _errorLogger->reportErr(ErrorMessage::passedByValue(_tokenizer, tok, tok->strAt(3))); + } + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Check that all struct members are used +//--------------------------------------------------------------------------- + +void CheckOther::CheckStructMemberUsage() +{ + const char *structname = 0; + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->fileIndex() != 0) + continue; + + if (Token::Match(tok, "struct|union %type% {")) + { + structname = tok->strAt(1); + + // Bail out if struct/union contain any functions + for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "(") + { + structname = 0; + break; + } + + if (tok2->str() == "}") + break; + } + } + + if (tok->str() == "}") + structname = 0; + + if (structname && Token::Match(tok, "[{;]")) + { + const char *varname = 0; + if (Token::Match(tok->next(), "%type% %var% [;[]")) + varname = tok->strAt(2); + else if (Token::Match(tok->next(), "%type% %type% %var% [;[]")) + varname = tok->strAt(3); + else if (Token::Match(tok->next(), "%type% * %var% [;[]")) + varname = tok->strAt(3); + else if (Token::Match(tok->next(), "%type% %type% * %var% [;[]")) + varname = tok->strAt(4); + else + continue; + + const std::string usagePattern(". " + std::string(varname)); + bool used = false; + for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) + { + if (Token::simpleMatch(tok2, usagePattern.c_str())) + { + used = true; + break; + } + } + + if (! used) + { + _errorLogger->reportErr(ErrorMessage::unusedStructMember(_tokenizer, tok->next(), structname, varname)); + } + } + } +} + + + + + +//--------------------------------------------------------------------------- +// Check usage of char variables.. +//--------------------------------------------------------------------------- + +void CheckOther::CheckCharVariable() +{ + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Declaring the variable.. + if (Token::Match(tok, "[{};(,] char %var% [;=,)]")) + { + // Set tok to point to the variable name + tok = tok->tokAt(2); + + // Check usage of char variable.. + int indentlevel = 0; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "{") + ++indentlevel; + + else if (tok2->str() == "}") + { + --indentlevel; + if (indentlevel <= 0) + break; + } + + std::string temp = "%var% [ " + tok->str() + " ]"; + if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str())) + { + _errorLogger->reportErr(ErrorMessage::charArrayIndex(_tokenizer, tok2->next())); + break; + } + + std::string tempFirst = "%var% [&|] " + tok->str(); + std::string tempSecond = tok->str() + " [&|]"; + if (Token::Match(tok2, tempFirst.c_str()) || Token::Match(tok2, tempSecond.c_str())) + { + _errorLogger->reportErr(ErrorMessage::charBitOp(_tokenizer, tok2)); + break; + } + } + } + } +} +//--------------------------------------------------------------------------- + + + + + + +//--------------------------------------------------------------------------- +// Incomplete statement.. +//--------------------------------------------------------------------------- + +void CheckOther::CheckIncompleteStatement() +{ + int parlevel = 0; + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() == "(") + ++parlevel; + else if (tok->str() == ")") + --parlevel; + + if (parlevel != 0) + continue; + + if ((tok->str() != "#") && Token::Match(tok->next(), "; %str%") && !Token::Match(tok->tokAt(3), ",")) + { + _errorLogger->reportErr(ErrorMessage::constStatement(_tokenizer, tok->next(), "string")); + } + + if (!Token::Match(tok, "#") && Token::Match(tok->next(), "; %num%") && !Token::Match(tok->tokAt(3), ",")) + { + _errorLogger->reportErr(ErrorMessage::constStatement(_tokenizer, tok->next(), "numeric")); + } + } +} +//--------------------------------------------------------------------------- + + + + + + +//--------------------------------------------------------------------------- +// str plus char +//--------------------------------------------------------------------------- + +void CheckOther::strPlusChar() +{ + bool charVars[10000] = {0}; + + for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Declaring char variable.. + if (Token::Match(tok, "char %var% [;=]")) + { + unsigned int varid = tok->next()->varId(); + if (varid > 0 && varid < 10000) + charVars[varid] = true; + } + + // + else if (Token::Match(tok, "[=(] %str% + %any%")) + { + // char constant.. + const char *s = tok->strAt(3); + if (*s == '\'') + _errorLogger->reportErr(ErrorMessage::strPlusChar(_tokenizer, tok->next())); + + // char variable.. + unsigned int varid = tok->tokAt(3)->varId(); + if (varid > 0 && varid < 10000 && charVars[varid]) + _errorLogger->reportErr(ErrorMessage::strPlusChar(_tokenizer, tok->next())); + } + } +} diff --git a/src/checkother.h b/src/checkother.h index 6d54e62b9..a5b899825 100644 --- a/src/checkother.h +++ b/src/checkother.h @@ -1,86 +1,86 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 #include +#include Preprocessor::Preprocessor() { @@ -72,7 +73,7 @@ std::string Preprocessor::read(std::istream &istr) ++lineno; // Replace assorted special chars with spaces.. - if ((ch != '\n') && (isspace(ch) || iscntrl(ch))) + if ((ch != '\n') && (std::isspace(ch) || std::iscntrl(ch))) ch = ' '; // Skip spaces after ' ' and after '#' @@ -491,9 +492,9 @@ void Preprocessor::handleIncludes(std::string &code, const std::string &filename filename = getHeaderFileName(filename); if (filename.length() == 0) continue; - + std::string tempFile = filename; - std::transform(tempFile.begin(), tempFile.end(), tempFile.begin(), static_cast < int(*)(int) > (std::tolower)); + std::transform(tempFile.begin(), tempFile.end(), tempFile.begin(), static_cast < int(*)(int) > (std::tolower)); if (handledFiles.find(tempFile) != handledFiles.end()) { // We have processed this file already once, skip @@ -697,7 +698,7 @@ std::string Preprocessor::expandMacros(std::string code) std::string::size_type pos = pos1 + macro.name().length(); if (pos < code.length() && code.substr(pos1, macro.name().length()) == macro.name() - && !isalnum(code[pos]) && code[pos] != '_') + && !std::isalnum(code[pos]) && code[pos] != '_') break; @@ -720,7 +721,7 @@ std::string Preprocessor::expandMacros(std::string code) // TODO, this code is here, because there is currently a bug in cppcheck // Once it has been sorted out, this if can be removed std::cout << "\n\n####### There is a bug in preprocessor.cpp that can cause crash, shutting down.\n\n" << std::endl; - exit(0); + std::exit(0); } } continue; @@ -732,14 +733,14 @@ std::string Preprocessor::expandMacros(std::string code) continue; // Previous char must not be alphanumeric or '_' - if (pos1 != 0 && (isalnum(code[pos1-1]) || code[pos1-1] == '_')) + if (pos1 != 0 && (std::isalnum(code[pos1-1]) || code[pos1-1] == '_')) continue; // The char after the macroname must not be alphanumeric or '_' if (pos1 + macro.name().length() < code.length()) { std::string::size_type pos2 = pos1 + macro.name().length(); - if (isalnum(code[pos2]) || code[pos2] == '_') + if (std::isalnum(code[pos2]) || code[pos2] == '_') continue; } diff --git a/src/preprocessor.h b/src/preprocessor.h index 455a62e67..42dd34785 100644 --- a/src/preprocessor.h +++ b/src/preprocessor.h @@ -1,132 +1,132 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 -#include -#include - - -class Preprocessor -{ -public: - Preprocessor(); - - /** - * Extract the code for each configuration - * @param istr The (file/string) stream to read from. - * @param result The map that will get the results - * @param filename The name of the file to check e.g. "src/main.cpp" - * @param includePaths List of paths where incude files should be searched from, - * single path can be e.g. in format "include/". - * There must be a path separator at the end. Default parameter is empty list. - * Note that if path from given filename is also extracted and that is used as - * a last include path if include file was not found from earlier paths. - */ - void preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths = std::list()); - - /** - * Extract the code for each configuration. Use this with getcode() to get the - * file data for each individual configuration. - * - * @param istr The (file/string) stream to read from. - * @param processedFile Give reference to empty string as a parameter, - * function will fill processed file here. Use this also as a filedata parameter - * to getcode() if you recieved more than once configurations. - * @param resultConfigurations List of configurations. Pass these one by one - * to getcode() with processedFile. - * @param filename The name of the file to check e.g. "src/main.cpp" - * @param includePaths List of paths where incude files should be searched from, - * single path can be e.g. in format "include/". - * There must be a path separator at the end. Default parameter is empty list. - * Note that if path from given filename is also extracted and that is used as - * a last include path if include file was not found from earlier paths. - */ - void preprocess(std::istream &istr, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths); - - /** Just read the code into a string. Perform simple cleanup of the code */ - static std::string read(std::istream &istr); - - /** - * Get preprocessed code for a given configuration - */ - static std::string getcode(const std::string &filedata, std::string cfg); - -#ifndef UNIT_TESTING -private: -#endif - - /** - * Remove space that has new line character on left or right side of it. - * - * @param str The string to be converted - * @return The string where space characters have been removed. - */ - static std::string removeSpaceNearNL(const std::string &str); - - /** - * Replace "#if defined" with "#ifdef" where possible - * - * @param str The string to be converted - * @return The replaced string - */ - static std::string replaceIfDefined(const std::string &str); - - /** - * Get all possible configurations. By looking at the ifdefs and ifndefs in filedata - */ - std::list getcfgs(const std::string &filedata); - - static std::string getdef(std::string line, bool def); - - static bool match_cfg_def(std::string cfg, const std::string &def); - - static std::string expandMacros(std::string code); - - /** - * Search includes from code and append code from the included - * file - * @param code The source code to modify - * @param filename The name of the file to check e.g. "src/main.cpp" - * @param includePaths List of paths where incude files should be searched from, - * single path can be e.g. in format "include/". - * There must be a path separator at the end. Default parameter is empty list. - * Note that if path from given filename is also extracted and that is used as - * a last include path if include file was not found from earlier paths. - * @return modified source code - */ - static void handleIncludes(std::string &code, const std::string &filename, const std::list &includePaths); - - /** - * Returns the string between double quote characters. - * @param str e.g. '#include "menu.h"' - * @return e.g. 'menu.h' or empty string if double quotes were - * not found. - */ - static std::string getHeaderFileName(const std::string &str); -}; - -//--------------------------------------------------------------------------- -#endif - +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 +#include +#include + + +class Preprocessor +{ +public: + Preprocessor(); + + /** + * Extract the code for each configuration + * @param istr The (file/string) stream to read from. + * @param result The map that will get the results + * @param filename The name of the file to check e.g. "src/main.cpp" + * @param includePaths List of paths where incude files should be searched from, + * single path can be e.g. in format "include/". + * There must be a path separator at the end. Default parameter is empty list. + * Note that if path from given filename is also extracted and that is used as + * a last include path if include file was not found from earlier paths. + */ + void preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths = std::list()); + + /** + * Extract the code for each configuration. Use this with getcode() to get the + * file data for each individual configuration. + * + * @param istr The (file/string) stream to read from. + * @param processedFile Give reference to empty string as a parameter, + * function will fill processed file here. Use this also as a filedata parameter + * to getcode() if you recieved more than once configurations. + * @param resultConfigurations List of configurations. Pass these one by one + * to getcode() with processedFile. + * @param filename The name of the file to check e.g. "src/main.cpp" + * @param includePaths List of paths where incude files should be searched from, + * single path can be e.g. in format "include/". + * There must be a path separator at the end. Default parameter is empty list. + * Note that if path from given filename is also extracted and that is used as + * a last include path if include file was not found from earlier paths. + */ + void preprocess(std::istream &istr, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths); + + /** Just read the code into a string. Perform simple cleanup of the code */ + static std::string read(std::istream &istr); + + /** + * Get preprocessed code for a given configuration + */ + static std::string getcode(const std::string &filedata, std::string cfg); + +#ifndef UNIT_TESTING +private: +#endif + + /** + * Remove space that has new line character on left or right side of it. + * + * @param str The string to be converted + * @return The string where space characters have been removed. + */ + static std::string removeSpaceNearNL(const std::string &str); + + /** + * Replace "#if defined" with "#ifdef" where possible + * + * @param str The string to be converted + * @return The replaced string + */ + static std::string replaceIfDefined(const std::string &str); + + /** + * Get all possible configurations. By looking at the ifdefs and ifndefs in filedata + */ + std::list getcfgs(const std::string &filedata); + + static std::string getdef(std::string line, bool def); + + static bool match_cfg_def(std::string cfg, const std::string &def); + + static std::string expandMacros(std::string code); + + /** + * Search includes from code and append code from the included + * file + * @param code The source code to modify + * @param filename The name of the file to check e.g. "src/main.cpp" + * @param includePaths List of paths where incude files should be searched from, + * single path can be e.g. in format "include/". + * There must be a path separator at the end. Default parameter is empty list. + * Note that if path from given filename is also extracted and that is used as + * a last include path if include file was not found from earlier paths. + * @return modified source code + */ + static void handleIncludes(std::string &code, const std::string &filename, const std::list &includePaths); + + /** + * Returns the string between double quote characters. + * @param str e.g. '#include "menu.h"' + * @return e.g. 'menu.h' or empty string if double quotes were + * not found. + */ + static std::string getHeaderFileName(const std::string &str); +}; + +//--------------------------------------------------------------------------- +#endif + diff --git a/src/tokenize.cpp b/src/tokenize.cpp index 9f2b637b5..2dba533d7 100644 --- a/src/tokenize.cpp +++ b/src/tokenize.cpp @@ -1,1521 +1,1522 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -//--------------------------------------------------------------------------- - -Tokenizer::Tokenizer() -{ - _tokens = 0; - _tokensBack = 0; -} - -Tokenizer::~Tokenizer() -{ - DeallocateTokens(); -} - -//--------------------------------------------------------------------------- - -// Helper functions.. - - -//--------------------------------------------------------------------------- - -const Token *Tokenizer::tokens() const -{ - return _tokens; -} - - -const std::vector *Tokenizer::getFiles() const -{ - return &_files; -} - -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// addtoken -// add a token. Used by 'Tokenizer' -//--------------------------------------------------------------------------- - -void Tokenizer::addtoken(const char str[], const unsigned int lineno, const unsigned int fileno) -{ - if (str[0] == 0) - return; - - // Replace hexadecimal value with decimal - std::ostringstream str2; - if (strncmp(str, "0x", 2) == 0) - { - str2 << strtoul(str + 2, NULL, 16); - } - else - { - str2 << str; - } - - if (_tokensBack) - { - _tokensBack->insertToken(str2.str().c_str()); - _tokensBack = _tokensBack->next(); - } - else - { - _tokens = new Token; - _tokensBack = _tokens; - _tokensBack->str(str2.str().c_str()); - } - - _tokensBack->linenr(lineno); - _tokensBack->fileIndex(fileno); -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// SizeOfType - gives the size of a type -//--------------------------------------------------------------------------- - - - -int Tokenizer::SizeOfType(const char type[]) const -{ - if (!type) - return 0; - - std::map::const_iterator it = _typeSize.find(type); - if (it == _typeSize.end()) - return 0; - - return it->second; -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// InsertTokens - Copy and insert tokens -//--------------------------------------------------------------------------- - -void Tokenizer::InsertTokens(Token *dest, Token *src, unsigned int n) -{ - while (n > 0) - { - dest->insertToken(src->aaaa()); - dest = dest->next(); - dest->fileIndex(src->fileIndex()); - dest->linenr(src->linenr()); - dest->varId(src->varId()); - src = src->next(); - --n; - } -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// Tokenize - tokenizes a given file. -//--------------------------------------------------------------------------- - -void Tokenizer::tokenize(std::istream &code, const char FileName[]) -{ - // The "_files" vector remembers what files have been tokenized.. - _files.push_back(FileLister::simplifyPath(FileName)); - - // line number in parsed code - unsigned int lineno = 1; - - // The current token being parsed - std::string CurrentToken; - - // lineNumbers holds line numbers for files in fileIndexes - // every time an include file is complitely parsed, last item in the vector - // is removed and lineno is set to point to that value. - std::vector lineNumbers; - - // fileIndexes holds index for _files vector about currently parsed files - // every time an include file is complitely parsed, last item in the vector - // is removed and FileIndex is set to point to that value. - std::vector fileIndexes; - - // FileIndex. What file in the _files vector is read now? - unsigned int FileIndex = 0; - - // Read one byte at a time from code and create tokens - for (char ch = (char)code.get(); code.good(); ch = (char)code.get()) - { - // We are not handling UTF and stuff like that. Code is supposed to plain simple text. - if (ch < 0) - continue; - - if (ch == '\n') - { - // Add current token.. - addtoken(CurrentToken.c_str(), lineno++, FileIndex); - CurrentToken.clear(); - continue; - } - - // char.. - if (ch == '\'') - { - // Add previous token - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - - // Read this .. - CurrentToken += ch; - CurrentToken += (char)code.get(); - CurrentToken += (char)code.get(); - if (CurrentToken[1] == '\\') - CurrentToken += (char)code.get(); - - // Add token and start on next.. - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - - continue; - } - - // String.. - if (ch == '\"') - { - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - bool special = false; - char c = ch; - do - { - // Append token.. - CurrentToken += c; - - if (c == '\n') - ++lineno; - - // Special sequence '\.' - if (special) - special = false; - else - special = (c == '\\'); - - // Get next character - c = (char)code.get(); - } - while (code.good() && (special || c != '\"')); - CurrentToken += '\"'; - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - continue; - } - - if (ch == '#' && CurrentToken.empty()) - { - // If previous token was "#" then append this to create a "##" token - if (Token::simpleMatch(_tokensBack, "#")) - { - _tokensBack->str("##"); - continue; - } - - std::string line("#"); - { - char chPrev = '#'; - while (code.good()) - { - ch = (char)code.get(); - if (chPrev != '\\' && ch == '\n') - break; - if (ch != ' ') - chPrev = ch; - if (ch != '\\' && ch != '\n') - { - line += ch; - } - if (ch == '\n') - ++lineno; - } - } - if (strncmp(line.c_str(), "#file", 5) == 0 && - line.find("\"") != std::string::npos) - { - // Extract the filename - line.erase(0, line.find("\"") + 1); - if (line.find("\"") != std::string::npos) - line.erase(line.find("\"")); - - // Relative path.. - if (_files.back().find_first_of("\\/") != std::string::npos) - { - std::string path = _files.back(); - path.erase(1 + path.find_last_of("\\/")); - line = path + line; - } - - // Has this file been tokenized already? - ++lineno; - bool foundOurfile = false; - fileIndexes.push_back(FileIndex); - for (unsigned int i = 0; i < _files.size(); i++) - { - if (FileLister::SameFileName(_files[i].c_str(), line.c_str())) - { - // Use this index - foundOurfile = true; - FileIndex = i; - } - } - - if (!foundOurfile) - { - // The "_files" vector remembers what files have been tokenized.. - _files.push_back(FileLister::simplifyPath(line.c_str())); - FileIndex = _files.size() - 1; - } - - lineNumbers.push_back(lineno); - lineno = 1; - - continue; - } - - else if (strncmp(line.c_str(), "#endfile", 8) == 0) - { - lineno = lineNumbers.back(); - lineNumbers.pop_back(); - FileIndex = fileIndexes.back(); - fileIndexes.pop_back(); - continue; - } - - else - { - addtoken(line.c_str(), lineno, FileIndex); - } - } - - if (strchr("#+-*/%&|^?!=<>[](){};:,.~", ch)) - { - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - CurrentToken += ch; - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - continue; - } - - - if (isspace(ch) || iscntrl(ch)) - { - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - continue; - } - - CurrentToken += ch; - } - addtoken(CurrentToken.c_str(), lineno, FileIndex); - - // Combine tokens.. - for (Token *tok = _tokens; tok && tok->next(); tok = tok->next()) - { - static const char* combineWithNext[][3] = - { - { "<", "<", "<<" }, - { ">", ">", ">>" }, - - { "&", "&", "&&" }, - { "|", "|", "||" }, - - { "+", "=", "+=" }, - { "-", "=", "-=" }, - { "*", "=", "*=" }, - { "/", "=", "/=" }, - { "&", "=", "&=" }, - { "|", "=", "|=" }, - - { "=", "=", "==" }, - { "!", "=", "!=" }, - { "<", "=", "<=" }, - { ">", "=", ">=" }, - - { ":", ":", "::" }, - { "-", ">", "." }, // Replace "->" with "." - - { "private", ":", "private:" }, - { "protected", ":", "protected:" }, - { "public", ":", "public:" } - }; - - for (unsigned ui = 0; ui < sizeof(combineWithNext) / sizeof(combineWithNext[0]); ui++) - { - if (tok->str() == combineWithNext[ui][0] && tok->next()->str() == combineWithNext[ui][1]) - { - tok->str(combineWithNext[ui][2]); - tok->deleteNext(); - } - } - } - - // typedef.. - for (Token *tok = _tokens; tok;) - { - if (Token::Match(tok, "typedef %type% %type% ;")) - { - const char *type1 = tok->strAt(1); - const char *type2 = tok->strAt(2); - tok = const_cast(tok->tokAt(4)); - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == type2) - tok2->str(type1); - } - continue; - } - - else if (Token::Match(tok, "typedef %type% %type% %type% ;")) - { - const char *type1 = tok->strAt(1); - const char *type2 = tok->strAt(2); - const char *type3 = tok->strAt(3); - tok = const_cast(tok->tokAt(5)); - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->str() == type3) - { - tok2->str(type1); - tok2->insertToken(type2); - tok2 = tok2->next(); - } - } - continue; - } - - tok = tok->next(); - } - - // Remove __asm.. - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::simpleMatch(tok->next(), "__asm {")) - { - while (tok->next()) - { - bool last = Token::simpleMatch(tok->next(), "}"); - - // Unlink and delete tok->next() - tok->deleteNext(); - - // break if this was the last token to delete.. - if (last) - break; - } - } - } - - // Remove "volatile" - while (Token::simpleMatch(_tokens, "volatile")) - { - Token *tok = _tokens; - _tokens = _tokens->next(); - delete tok; - } - for (Token *tok = _tokens; tok; tok = tok->next()) - { - while (Token::simpleMatch(tok->next(), "volatile")) - { - tok->deleteNext(); - } - } -} -//--------------------------------------------------------------------------- - - -void Tokenizer::setVarId() -{ - // Clear all variable ids - for (Token *tok = _tokens; tok; tok = tok->next()) - tok->varId(0); - - // Set variable ids.. - bool firstMatch; - unsigned int _varId = 0; - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (!(firstMatch = Token::Match(tok, "[;{}(] %type% *| %var%")) - && !Token::Match(tok, "[;{}(] %type% %type% *| %var%")) - continue; - - // Determine name of declared variable.. - const char *varname = 0; - Token *tok2 = tok->tokAt(firstMatch ? 2 : 3); - while (tok2 && ! Token::Match(tok2, "[;[=(]")) - { - if (tok2->isName()) - varname = tok2->strAt(0); - else if (tok2->str() != "*") - break; - tok2 = tok2->next(); - } - - // Variable declaration found => Set variable ids - if (Token::Match(tok2, "[;[=]") && varname) - { - ++_varId; - int indentlevel = 0; - int parlevel = 0; - bool dot = false; - for (tok2 = tok->next(); tok2; tok2 = tok2->next()) - { - if (!dot && tok2->str() == varname) - tok2->varId(_varId); - else if (tok2->str() == "{") - ++indentlevel; - else if (tok2->str() == "}") - { - --indentlevel; - if (indentlevel < 0) - break; - } - else if (tok2->str() == "(") - ++parlevel; - else if (tok2->str() == ")") - --parlevel; - else if (parlevel < 0 && tok2->str() == ";") - break; - dot = bool(tok2->str() == "."); - } - } - } - - // Struct/Class members - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (tok->varId() != 0 && - Token::Match(tok->next(), ". %var%") && - tok->tokAt(2)->varId() == 0) - { - ++_varId; - - const std::string pattern(std::string(". ") + tok->strAt(2)); - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - if (tok2->varId() == tok->varId() && Token::simpleMatch(tok2->next(), pattern.c_str())) - tok2->next()->next()->varId(_varId); - } - } - } -} - - -//--------------------------------------------------------------------------- -// Simplify token list -//--------------------------------------------------------------------------- - -void Tokenizer::simplifyTokenList() -{ - // Remove unwanted keywords - static const char* unwantedWords[] = { "unsigned", "unlikely" }; - for (Token *tok = _tokens; tok; tok = tok->next()) - { - for (unsigned ui = 0; ui < sizeof(unwantedWords) / sizeof(unwantedWords[0]) && tok->next(); ui++) - { - if (tok->next()->str() == unwantedWords[ui]) - { - tok->deleteNext(); - break; - } - } - } - - // Replace constants.. - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::Match(tok, "const %type% %var% = %num% ;")) - { - const char *sym = tok->strAt(2); - const char *num = tok->strAt(4); - - for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) - { - if (tok2->str() == sym) - { - tok2->str(num); - } - } - } - } - - - // Fill the map _typeSize.. - _typeSize.clear(); - _typeSize["char"] = sizeof(char); - _typeSize["short"] = sizeof(short); - _typeSize["int"] = sizeof(int); - _typeSize["long"] = sizeof(long); - _typeSize["float"] = sizeof(float); - _typeSize["double"] = sizeof(double); - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::Match(tok, "class %var%")) - { - _typeSize[tok->strAt(1)] = 11; - } - - else if (Token::Match(tok, "struct %var%")) - { - _typeSize[tok->strAt(1)] = 13; - } - } - - - // Replace 'sizeof(type)'.. - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (tok->str() != "sizeof") - continue; - - if (Token::Match(tok, "sizeof ( %type% * )")) - { - std::ostringstream str; - // 'sizeof(type *)' has the same size as 'sizeof(char *)' - str << sizeof(char *); - tok->str(str.str().c_str()); - - for (int i = 0; i < 4; i++) - { - tok->deleteNext(); - } - } - - else if (Token::Match(tok, "sizeof ( %type% )")) - { - const char *type = tok->strAt(2); - int size = SizeOfType(type); - if (size > 0) - { - std::ostringstream str; - str << size; - tok->str(str.str().c_str()); - for (int i = 0; i < 3; i++) - { - tok->deleteNext(); - } - } - } - - else if (Token::Match(tok, "sizeof ( * %var% )")) - { - tok->str("100"); - for (int i = 0; i < 4; ++i) - tok->deleteNext(); - } - } - - // Replace 'sizeof(var)' - for (Token *tok = _tokens; tok; tok = tok->next()) - { - // type array [ num ] ; - if (! Token::Match(tok, "%type% %var% [ %num% ] ;")) - continue; - - int size = SizeOfType(tok->aaaa()); - if (size <= 0) - continue; - - const char *varname = tok->strAt(1); - int total_size = size * atoi(tok->strAt(3)); - - // Replace 'sizeof(var)' with number - int indentlevel = 0; - for (Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) - { - if (tok2->str() == "{") - { - ++indentlevel; - } - - else if (tok2->str() == "}") - { - --indentlevel; - if (indentlevel < 0) - break; - } - - // Todo: Token::Match varname directly - else if (Token::Match(tok2, "sizeof ( %var% )")) - { - if (strcmp(tok2->strAt(2), varname) == 0) - { - std::ostringstream str; - str << total_size; - tok2->str(str.str().c_str()); - // Delete the other tokens.. - for (int i = 0; i < 3; i++) - { - tok2->deleteNext(); - } - } - } - } - } - - - - - // Simple calculations.. - for (bool done = false; !done; done = true) - { - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::simpleMatch(tok->next(), "* 1") || Token::simpleMatch(tok->next(), "1 *")) - { - for (int i = 0; i < 2; i++) - tok->deleteNext(); - done = false; - } - - // (1-2) - if (Token::Match(tok, "[[,(=<>] %num% [+-*/] %num% [],);=<>]")) - { - int i1 = atoi(tok->strAt(1)); - int i2 = atoi(tok->strAt(3)); - if (i2 == 0 && *(tok->strAt(2)) == '/') - { - continue; - } - - switch (*(tok->strAt(2))) - { - case '+': - i1 += i2; - break; - case '-': - i1 -= i2; - break; - case '*': - i1 *= i2; - break; - case '/': - i1 /= i2; - break; - } - tok = tok->next(); - std::ostringstream str; - str << i1; - tok->str(str.str().c_str()); - for (int i = 0; i < 2; i++) - { - tok->deleteNext(); - } - - done = false; - } - } - } - - - // Replace "*(str + num)" => "str[num]" - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (! strchr(";{}(=<>", tok->aaaa0())) - continue; - - Token *next = tok->next(); - if (! next) - break; - - if (Token::Match(next, "* ( %var% + %num% )")) - { - const char *str[4] = {"var", "[", "num", "]"}; - str[0] = tok->strAt(3); - str[2] = tok->strAt(5); - - for (int i = 0; i < 4; i++) - { - tok = tok->next(); - tok->str(str[i]); - } - - tok->deleteNext(); - tok->deleteNext(); - } - } - - - - // Split up variable declarations if possible.. - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (! Token::Match(tok, "[{};]")) - continue; - - Token *type0 = tok->next(); - if (!Token::Match(type0, "%type%")) - continue; - if (Token::Match(type0, "else|return")) - continue; - - Token *tok2 = NULL; - unsigned int typelen = 0; - - if (Token::Match(type0, "%type% %var% ,|=")) - { - if (type0->next()->str() != "operator") - { - tok2 = type0->tokAt(2); // The ',' or '=' token - typelen = 1; - } - } - - else if (Token::Match(type0, "%type% * %var% ,|=")) - { - if (type0->next()->next()->str() != "operator") - { - tok2 = type0->tokAt(3); // The ',' token - typelen = 1; - } - } - - else if (Token::Match(type0, "%type% %var% [ %num% ] ,|=")) - { - tok2 = type0->tokAt(5); // The ',' token - typelen = 1; - } - - else if (Token::Match(type0, "%type% * %var% [ %num% ] ,|=")) - { - tok2 = type0->tokAt(6); // The ',' token - typelen = 1; - } - - else if (Token::Match(type0, "struct %type% %var% ,|=")) - { - tok2 = type0->tokAt(3); - typelen = 2; - } - - else if (Token::Match(type0, "struct %type% * %var% ,|=")) - { - tok2 = type0->tokAt(4); - typelen = 2; - } - - - if (tok2) - { - if (tok2->str() == ",") - { - tok2->str(";"); - InsertTokens(tok2, type0, typelen); - } - - else - { - Token *eq = tok2; - - int parlevel = 0; - while (tok2) - { - if (strchr("{(", tok2->aaaa0())) - { - ++parlevel; - } - - else if (strchr("})", tok2->aaaa0())) - { - if (parlevel < 0) - break; - --parlevel; - } - - else if (parlevel == 0 && strchr(";,", tok2->aaaa0())) - { - // "type var =" => "type var; var =" - Token *VarTok = type0->tokAt(typelen); - if (VarTok->aaaa0() == '*') - VarTok = VarTok->next(); - InsertTokens(eq, VarTok, 2); - eq->str(";"); - - // "= x, " => "= x; type " - if (tok2->str() == ",") - { - tok2->str(";"); - InsertTokens(tok2, type0, typelen); - } - break; - } - - tok2 = tok2->next(); - } - } - } - } - - // Replace NULL with 0.. - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (tok->str() == "NULL") - tok->str("0"); - } - - // Replace pointer casts of 0.. "(char *)0" => "0" - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::Match(tok->next(), "( %type% * ) 0") || Token::Match(tok->next(), "( %type% %type% * ) 0")) - { - while (!Token::simpleMatch(tok->next(), "0")) - tok->deleteNext(); - } - } - - simplifyIfAddBraces(); - - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::Match(tok, "case %any% : %var%")) - tok->next()->next()->insertToken(";"); - if (Token::Match(tok, "default : %var%")) - tok->next()->insertToken(";"); - } - - bool modified = true; - while (modified) - { - modified = false; - modified |= simplifyConditions(); - modified |= simplifyCasts(); - modified |= simplifyFunctionReturn(); - modified |= simplifyKnownVariables(); - modified |= removeReduntantConditions(); - } -} -//--------------------------------------------------------------------------- - -const Token *Tokenizer::findClosing(const Token *tok, const char *start, const char *end) -{ - if (!tok) - return 0; - - // Find the closing "}" - int indentLevel = 0; - for (const Token *closing = tok->next(); closing; closing = closing->next()) - { - if (closing->str() == start) - { - ++indentLevel; - continue; - } - - if (closing->str() == end) - --indentLevel; - - if (indentLevel >= 0) - continue; - - // Closing } is found. - return closing; - } - - return 0; -} - -bool Tokenizer::removeReduntantConditions() -{ - bool ret = false; - - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (!Token::simpleMatch(tok, "if")) - continue; - - if (!Token::Match(tok->tokAt(1), "( %bool% ) {")) - continue; - - // Find matching else - const Token *elseTag = 0; - - // Find the closing "}" - elseTag = Tokenizer::findClosing(tok->tokAt(4), "{", "}"); - if (elseTag) - elseTag = elseTag->next(); - - bool boolValue = false; - if (tok->tokAt(2)->str() == "true") - boolValue = true; - - // Handle if with else - if (elseTag && elseTag->str() == "else") - { - if (Token::simpleMatch(elseTag->next(), "if")) - { - // Handle "else if" - if (boolValue == false) - { - // Convert "if( false ) {aaa;} else if() {bbb;}" => "if() {bbb;}" - Token::eraseTokens(tok, elseTag->tokAt(2)); - ret = true; - } - else - { - // Keep first if, remove every else if and else after it - const Token *lastTagInIf = elseTag->tokAt(2); - while (lastTagInIf) - { - if (lastTagInIf->str() == "(") - { - lastTagInIf = Tokenizer::findClosing(lastTagInIf, "(", ")"); - lastTagInIf = lastTagInIf->next(); - } - - lastTagInIf = Tokenizer::findClosing(lastTagInIf, "{", "}"); - lastTagInIf = lastTagInIf->next(); - if (!Token::simpleMatch(lastTagInIf, "else")) - break; - - lastTagInIf = lastTagInIf->next(); - if (Token::simpleMatch(lastTagInIf, "if")) - lastTagInIf = lastTagInIf->next(); - } - - Token::eraseTokens(elseTag->previous(), lastTagInIf); - ret = true; - } - } - else - { - // Handle else - if (boolValue == false) - { - // Convert "if( false ) {aaa;} else {bbb;}" => "{bbb;}" or ";{bbb;}" - if (tok->previous()) - tok = tok->previous(); - else - tok->str(";"); - - Token::eraseTokens(tok, elseTag->tokAt(1)); - } - else - { - if (Token::simpleMatch(elseTag->tokAt(1), "{")) - { - // Convert "if( true ) {aaa;} else {bbb;}" => "{aaa;}" - const Token *end = Tokenizer::findClosing(elseTag->tokAt(1), "{", "}"); - if (!end) - { - // Possibly syntax error in code - return false; - } - - // Remove the "else { aaa; }" - Token::eraseTokens(elseTag->previous(), end->tokAt(1)); - } - - // Remove "if( true )" - if (tok->previous()) - tok = tok->previous(); - else - tok->str(";"); - - Token::eraseTokens(tok, tok->tokAt(5)); - } - - ret = true; - } - } - - // Handle if without else - else - { - if (boolValue == false) - { - // Remove if and its content - if (tok->previous()) - tok = tok->previous(); - else - tok->str(";"); - - Token::eraseTokens(tok, elseTag); - } - else - { - // convert "if( true ) {aaa;}" => "{aaa;}" - if (tok->previous()) - tok = tok->previous(); - else - tok->str(";"); - - Token::eraseTokens(tok, tok->tokAt(5)); - } - - ret = true; - } - } - - return ret; -} - -bool Tokenizer::simplifyIfAddBraces() -{ - bool ret = false; - - for (Token *tok = _tokens; tok; tok = tok ? tok->next() : NULL) - { - if (Token::Match(tok, "if|for|while (")) - { - // Goto the ending ')' - int parlevel = 1; - tok = tok->next(); - while (parlevel >= 1 && (tok = tok->next())) - { - if (tok->str() == "(") - ++parlevel; - else if (tok->str() == ")") - --parlevel; - } - - // ')' should be followed by '{' - if (!tok || Token::simpleMatch(tok, ") {")) - continue; - } - - else if (tok->str() == "else") - { - // An else followed by an if or brace don't need to be processed further - if (Token::Match(tok, "else if|{")) - continue; - } - - else - { - continue; - } - - // insert open brace.. - tok->insertToken("{"); - tok = tok->next(); - - // insert close brace.. - // In most cases it would work to just search for the next ';' and insert a closing brace after it. - // But here are special cases.. - // * if (cond) for (;;) break; - // * if (cond1) if (cond2) { } - int parlevel = 0; - int indentlevel = 0; - while ((tok = tok->next()) != NULL) - { - if (tok->str() == "{") - ++indentlevel; - - else if (tok->str() == "}") - { - --indentlevel; - if (indentlevel == 0) - break; - } - - else if (tok->str() == "(") - ++parlevel; - - else if (tok->str() == ")") - --parlevel; - - else if (indentlevel == 0 && parlevel == 0 && tok->str() == ";") - break; - } - - if (tok) - { - tok->insertToken("}"); - ret = true; - } - } - - return ret; -} - -bool Tokenizer::simplifyConditions() -{ - bool ret = false; - - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::simpleMatch(tok, "( true &&") || Token::simpleMatch(tok, "&& true &&") || Token::simpleMatch(tok->next(), "&& true )")) - { - tok->deleteNext(); - tok->deleteNext(); - ret = true; - } - - else if (Token::simpleMatch(tok, "( false ||") || Token::simpleMatch(tok, "|| false ||") || Token::simpleMatch(tok->next(), "|| false )")) - { - tok->deleteNext(); - tok->deleteNext(); - ret = true; - } - - // Change numeric constant in condition to "true" or "false" - const Token *tok2 = tok->tokAt(2); - if ((tok->str() == "(" || tok->str() == "&&" || tok->str() == "||") && - Token::Match(tok->next(), "%num%") && - tok2 && - (tok2->str() == ")" || tok2->str() == "&&" || tok2->str() == "||")) - { - tok->next()->str((tok->next()->str() != "0") ? "true" : "false"); - ret = true; - } - - // Reduce "(%num% == %num%)" => "(true)"/"(false)" - const Token *tok4 = tok->tokAt(4); - if (! tok4) - break; - if ((tok->str() == "&&" || tok->str() == "||" || tok->str() == "(") && - Token::Match(tok->tokAt(1), "%num% %any% %num%") && - (tok4->str() == "&&" || tok4->str() == "||" || tok4->str() == ")")) - { - double op1 = (strstr(tok->strAt(1), "0x")) ? strtol(tok->strAt(1), 0, 16) : atof(tok->strAt(1)); - double op2 = (strstr(tok->strAt(3), "0x")) ? strtol(tok->strAt(3), 0, 16) : atof(tok->strAt(3)); - std::string cmp = tok->strAt(2); - - bool result = false; - if (cmp == "==") - result = (op1 == op2); - else if (cmp == "!=") - result = (op1 != op2); - else if (cmp == ">=") - result = (op1 >= op2); - else if (cmp == ">") - result = (op1 > op2); - else if (cmp == "<=") - result = (op1 <= op2); - else if (cmp == "<") - result = (op1 < op2); - else - cmp = ""; - - if (! cmp.empty()) - { - tok = tok->next(); - tok->deleteNext(); - tok->deleteNext(); - - tok->str(result ? "true" : "false"); - ret = true; - } - } - } - - return ret; -} - - -bool Tokenizer::simplifyCasts() -{ - bool ret = false; - for (Token *tok = _tokens; tok; tok = tok->next()) - { - if (Token::Match(tok->next(), "( %type% * )")) - { - tok->deleteNext(); - tok->deleteNext(); - tok->deleteNext(); - tok->deleteNext(); - ret = true; - } - - else if (Token::Match(tok->next(), "dynamic_cast|reinterpret_cast|const_cast|static_cast <")) - { - while (tok->next() && tok->next()->str() != ">") - tok->deleteNext(); - tok->deleteNext(); - tok->deleteNext(); - Token *tok2 = tok; - int parlevel = 0; - while (tok2->next() && parlevel >= 0) - { - tok2 = tok2->next(); - if (Token::simpleMatch(tok2->next(), "(")) - ++parlevel; - else if (Token::simpleMatch(tok2->next(), ")")) - --parlevel; - } - if (tok2->next()) - tok2->deleteNext(); - - ret = true; - } - } - - return ret; -} - - - -bool Tokenizer::simplifyFunctionReturn() -{ - bool ret = false; - int indentlevel = 0; - for (const Token *tok = tokens(); tok; tok = tok->next()) - { - if (tok->str() == "{") - ++indentlevel; - - else if (tok->str() == "}") - --indentlevel; - - else if (indentlevel == 0 && Token::Match(tok, "%var% ( ) { return %num% ; }")) - { - std::ostringstream pattern; - pattern << "[(=+-*/] " << tok->str() << " ( ) [;)+-*/]"; - for (Token *tok2 = _tokens; tok2; tok2 = tok2->next()) - { - if (Token::Match(tok2, pattern.str().c_str())) - { - tok2 = tok2->next(); - tok2->str(tok->strAt(5)); - tok2->deleteNext(); - tok2->deleteNext(); - ret = true; - } - } - } - } - - return ret; -} - -bool Tokenizer::simplifyKnownVariables() -{ - bool ret = false; - for (Token *tok = _tokens; tok; tok = tok->next()) - { - // Search for a block of code - if (! Token::Match(tok, ") const| {")) - continue; - - // parse the block of code.. - int indentlevel = 0; - for (Token *tok2 = tok; tok2; tok2 = tok2->next()) - { - - if (tok2->str() == "{") - ++indentlevel; - - else if (tok2->str() == "}") - { - --indentlevel; - if (indentlevel <= 0) - break; - } - - else if (Token::Match(tok2, "%var% = %num% ;") || - Token::Match(tok2, "%var% = %bool% ;")) - { - unsigned int varid = tok2->varId(); - if (varid == 0) - continue; - - for (Token *tok3 = tok2->next(); tok3; tok3 = tok3->next()) - { - // Perhaps it's a loop => bail out - if (Token::Match(tok3, "[{}]")) - break; - - // Variable is used somehow in a non-defined pattern => bail out - if (tok3->varId() == varid) - break; - - // Replace variable with numeric constant.. - if (Token::Match(tok3, "if ( %varid% )", varid)) - { - tok3 = tok3->next()->next(); - tok3->str(tok2->strAt(2)); - ret = true; - } - } - } - } - } - - return ret; -} - -//--------------------------------------------------------------------------- -// Helper functions for handling the tokens list -//--------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------- - -const Token *Tokenizer::GetFunctionTokenByName(const char funcname[]) const -{ - for (unsigned int i = 0; i < _functionList.size(); ++i) - { - if (_functionList[i]->str() == funcname) - { - return _functionList[i]; - } - } - return NULL; -} - - -void Tokenizer::fillFunctionList() -{ - _functionList.clear(); - - int indentlevel = 0; - for (const Token *tok = _tokens; tok; tok = tok->next()) - { - if (tok->str() == "{") - ++indentlevel; - - else if (tok->str() == "}") - --indentlevel; - - if (indentlevel > 0) - { - continue; - } - - if (Token::Match(tok, "%var% (")) - { - // Check if this is the first token of a function implementation.. - for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) - { - if (tok2->str() == ";") - { - tok = tok2; - break; - } - - else if (tok2->str() == "{") - { - break; - } - - else if (tok2->str() == ")") - { - if (Token::Match(tok2, ") const| {")) - { - _functionList.push_back(tok); - tok = tok2; - } - else - { - tok = tok2; - while (tok->next() && !strchr(";{", tok->next()->aaaa0())) - tok = tok->next(); - } - break; - } - } - } - } - - // If the _functionList functions with duplicate names, remove them - // TODO this will need some better handling - for (unsigned int func1 = 0; func1 < _functionList.size();) - { - bool hasDuplicates = false; - for (unsigned int func2 = func1 + 1; func2 < _functionList.size();) - { - if (_functionList[func1]->str() == _functionList[func2]->str()) - { - hasDuplicates = true; - _functionList.erase(_functionList.begin() + func2); - } - else - { - ++func2; - } - } - - if (! hasDuplicates) - { - ++func1; - } - else - { - _functionList.erase(_functionList.begin() + func1); - } - } -} - -//--------------------------------------------------------------------------- - -// Deallocate lists.. -void Tokenizer::DeallocateTokens() -{ - deleteTokens(_tokens); - _tokens = 0; - _tokensBack = 0; - _files.clear(); -} - -void Tokenizer::deleteTokens(Token *tok) -{ - while (tok) - { - Token *next = tok->next(); - delete tok; - tok = next; - } -} - -//--------------------------------------------------------------------------- - -const char *Tokenizer::getParameterName(const Token *ftok, int par) -{ - int _par = 1; - for (; ftok; ftok = ftok->next()) - { - if (ftok->str() == ",") - ++_par; - if (par == _par && Token::Match(ftok, "%var% [,)]")) - return ftok->aaaa(); - } - return NULL; -} - -//--------------------------------------------------------------------------- - -std::string Tokenizer::fileLine(const Token *tok) const -{ - std::ostringstream ostr; - ostr << "[" << _files.at(tok->fileIndex()) << ":" << tok->linenr() << "]"; - return ostr.str(); -} - - - -//--------------------------------------------------------------------------- +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +Tokenizer::Tokenizer() +{ + _tokens = 0; + _tokensBack = 0; +} + +Tokenizer::~Tokenizer() +{ + DeallocateTokens(); +} + +//--------------------------------------------------------------------------- + +// Helper functions.. + + +//--------------------------------------------------------------------------- + +const Token *Tokenizer::tokens() const +{ + return _tokens; +} + + +const std::vector *Tokenizer::getFiles() const +{ + return &_files; +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// addtoken +// add a token. Used by 'Tokenizer' +//--------------------------------------------------------------------------- + +void Tokenizer::addtoken(const char str[], const unsigned int lineno, const unsigned int fileno) +{ + if (str[0] == 0) + return; + + // Replace hexadecimal value with decimal + std::ostringstream str2; + if (strncmp(str, "0x", 2) == 0) + { + str2 << std::strtoul(str + 2, NULL, 16); + } + else + { + str2 << str; + } + + if (_tokensBack) + { + _tokensBack->insertToken(str2.str().c_str()); + _tokensBack = _tokensBack->next(); + } + else + { + _tokens = new Token; + _tokensBack = _tokens; + _tokensBack->str(str2.str().c_str()); + } + + _tokensBack->linenr(lineno); + _tokensBack->fileIndex(fileno); +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// SizeOfType - gives the size of a type +//--------------------------------------------------------------------------- + + + +int Tokenizer::SizeOfType(const char type[]) const +{ + if (!type) + return 0; + + std::map::const_iterator it = _typeSize.find(type); + if (it == _typeSize.end()) + return 0; + + return it->second; +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// InsertTokens - Copy and insert tokens +//--------------------------------------------------------------------------- + +void Tokenizer::InsertTokens(Token *dest, Token *src, unsigned int n) +{ + while (n > 0) + { + dest->insertToken(src->aaaa()); + dest = dest->next(); + dest->fileIndex(src->fileIndex()); + dest->linenr(src->linenr()); + dest->varId(src->varId()); + src = src->next(); + --n; + } +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Tokenize - tokenizes a given file. +//--------------------------------------------------------------------------- + +void Tokenizer::tokenize(std::istream &code, const char FileName[]) +{ + // The "_files" vector remembers what files have been tokenized.. + _files.push_back(FileLister::simplifyPath(FileName)); + + // line number in parsed code + unsigned int lineno = 1; + + // The current token being parsed + std::string CurrentToken; + + // lineNumbers holds line numbers for files in fileIndexes + // every time an include file is complitely parsed, last item in the vector + // is removed and lineno is set to point to that value. + std::vector lineNumbers; + + // fileIndexes holds index for _files vector about currently parsed files + // every time an include file is complitely parsed, last item in the vector + // is removed and FileIndex is set to point to that value. + std::vector fileIndexes; + + // FileIndex. What file in the _files vector is read now? + unsigned int FileIndex = 0; + + // Read one byte at a time from code and create tokens + for (char ch = (char)code.get(); code.good(); ch = (char)code.get()) + { + // We are not handling UTF and stuff like that. Code is supposed to plain simple text. + if (ch < 0) + continue; + + if (ch == '\n') + { + // Add current token.. + addtoken(CurrentToken.c_str(), lineno++, FileIndex); + CurrentToken.clear(); + continue; + } + + // char.. + if (ch == '\'') + { + // Add previous token + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + + // Read this .. + CurrentToken += ch; + CurrentToken += (char)code.get(); + CurrentToken += (char)code.get(); + if (CurrentToken[1] == '\\') + CurrentToken += (char)code.get(); + + // Add token and start on next.. + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + + continue; + } + + // String.. + if (ch == '\"') + { + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + bool special = false; + char c = ch; + do + { + // Append token.. + CurrentToken += c; + + if (c == '\n') + ++lineno; + + // Special sequence '\.' + if (special) + special = false; + else + special = (c == '\\'); + + // Get next character + c = (char)code.get(); + } + while (code.good() && (special || c != '\"')); + CurrentToken += '\"'; + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + continue; + } + + if (ch == '#' && CurrentToken.empty()) + { + // If previous token was "#" then append this to create a "##" token + if (Token::simpleMatch(_tokensBack, "#")) + { + _tokensBack->str("##"); + continue; + } + + std::string line("#"); + { + char chPrev = '#'; + while (code.good()) + { + ch = (char)code.get(); + if (chPrev != '\\' && ch == '\n') + break; + if (ch != ' ') + chPrev = ch; + if (ch != '\\' && ch != '\n') + { + line += ch; + } + if (ch == '\n') + ++lineno; + } + } + if (strncmp(line.c_str(), "#file", 5) == 0 && + line.find("\"") != std::string::npos) + { + // Extract the filename + line.erase(0, line.find("\"") + 1); + if (line.find("\"") != std::string::npos) + line.erase(line.find("\"")); + + // Relative path.. + if (_files.back().find_first_of("\\/") != std::string::npos) + { + std::string path = _files.back(); + path.erase(1 + path.find_last_of("\\/")); + line = path + line; + } + + // Has this file been tokenized already? + ++lineno; + bool foundOurfile = false; + fileIndexes.push_back(FileIndex); + for (unsigned int i = 0; i < _files.size(); i++) + { + if (FileLister::SameFileName(_files[i].c_str(), line.c_str())) + { + // Use this index + foundOurfile = true; + FileIndex = i; + } + } + + if (!foundOurfile) + { + // The "_files" vector remembers what files have been tokenized.. + _files.push_back(FileLister::simplifyPath(line.c_str())); + FileIndex = _files.size() - 1; + } + + lineNumbers.push_back(lineno); + lineno = 1; + + continue; + } + + else if (strncmp(line.c_str(), "#endfile", 8) == 0) + { + lineno = lineNumbers.back(); + lineNumbers.pop_back(); + FileIndex = fileIndexes.back(); + fileIndexes.pop_back(); + continue; + } + + else + { + addtoken(line.c_str(), lineno, FileIndex); + } + } + + if (strchr("#+-*/%&|^?!=<>[](){};:,.~", ch)) + { + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + CurrentToken += ch; + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + continue; + } + + + if (std::isspace(ch) || std::iscntrl(ch)) + { + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + continue; + } + + CurrentToken += ch; + } + addtoken(CurrentToken.c_str(), lineno, FileIndex); + + // Combine tokens.. + for (Token *tok = _tokens; tok && tok->next(); tok = tok->next()) + { + static const char* combineWithNext[][3] = + { + { "<", "<", "<<" }, + { ">", ">", ">>" }, + + { "&", "&", "&&" }, + { "|", "|", "||" }, + + { "+", "=", "+=" }, + { "-", "=", "-=" }, + { "*", "=", "*=" }, + { "/", "=", "/=" }, + { "&", "=", "&=" }, + { "|", "=", "|=" }, + + { "=", "=", "==" }, + { "!", "=", "!=" }, + { "<", "=", "<=" }, + { ">", "=", ">=" }, + + { ":", ":", "::" }, + { "-", ">", "." }, // Replace "->" with "." + + { "private", ":", "private:" }, + { "protected", ":", "protected:" }, + { "public", ":", "public:" } + }; + + for (unsigned ui = 0; ui < sizeof(combineWithNext) / sizeof(combineWithNext[0]); ui++) + { + if (tok->str() == combineWithNext[ui][0] && tok->next()->str() == combineWithNext[ui][1]) + { + tok->str(combineWithNext[ui][2]); + tok->deleteNext(); + } + } + } + + // typedef.. + for (Token *tok = _tokens; tok;) + { + if (Token::Match(tok, "typedef %type% %type% ;")) + { + const char *type1 = tok->strAt(1); + const char *type2 = tok->strAt(2); + tok = const_cast(tok->tokAt(4)); + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + if (tok2->str() == type2) + tok2->str(type1); + } + continue; + } + + else if (Token::Match(tok, "typedef %type% %type% %type% ;")) + { + const char *type1 = tok->strAt(1); + const char *type2 = tok->strAt(2); + const char *type3 = tok->strAt(3); + tok = const_cast(tok->tokAt(5)); + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + if (tok2->str() == type3) + { + tok2->str(type1); + tok2->insertToken(type2); + tok2 = tok2->next(); + } + } + continue; + } + + tok = tok->next(); + } + + // Remove __asm.. + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::simpleMatch(tok->next(), "__asm {")) + { + while (tok->next()) + { + bool last = Token::simpleMatch(tok->next(), "}"); + + // Unlink and delete tok->next() + tok->deleteNext(); + + // break if this was the last token to delete.. + if (last) + break; + } + } + } + + // Remove "volatile" + while (Token::simpleMatch(_tokens, "volatile")) + { + Token *tok = _tokens; + _tokens = _tokens->next(); + delete tok; + } + for (Token *tok = _tokens; tok; tok = tok->next()) + { + while (Token::simpleMatch(tok->next(), "volatile")) + { + tok->deleteNext(); + } + } +} +//--------------------------------------------------------------------------- + + +void Tokenizer::setVarId() +{ + // Clear all variable ids + for (Token *tok = _tokens; tok; tok = tok->next()) + tok->varId(0); + + // Set variable ids.. + bool firstMatch; + unsigned int _varId = 0; + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (!(firstMatch = Token::Match(tok, "[;{}(] %type% *| %var%")) + && !Token::Match(tok, "[;{}(] %type% %type% *| %var%")) + continue; + + // Determine name of declared variable.. + const char *varname = 0; + Token *tok2 = tok->tokAt(firstMatch ? 2 : 3); + while (tok2 && ! Token::Match(tok2, "[;[=(]")) + { + if (tok2->isName()) + varname = tok2->strAt(0); + else if (tok2->str() != "*") + break; + tok2 = tok2->next(); + } + + // Variable declaration found => Set variable ids + if (Token::Match(tok2, "[;[=]") && varname) + { + ++_varId; + int indentlevel = 0; + int parlevel = 0; + bool dot = false; + for (tok2 = tok->next(); tok2; tok2 = tok2->next()) + { + if (!dot && tok2->str() == varname) + tok2->varId(_varId); + else if (tok2->str() == "{") + ++indentlevel; + else if (tok2->str() == "}") + { + --indentlevel; + if (indentlevel < 0) + break; + } + else if (tok2->str() == "(") + ++parlevel; + else if (tok2->str() == ")") + --parlevel; + else if (parlevel < 0 && tok2->str() == ";") + break; + dot = bool(tok2->str() == "."); + } + } + } + + // Struct/Class members + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (tok->varId() != 0 && + Token::Match(tok->next(), ". %var%") && + tok->tokAt(2)->varId() == 0) + { + ++_varId; + + const std::string pattern(std::string(". ") + tok->strAt(2)); + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + if (tok2->varId() == tok->varId() && Token::simpleMatch(tok2->next(), pattern.c_str())) + tok2->next()->next()->varId(_varId); + } + } + } +} + + +//--------------------------------------------------------------------------- +// Simplify token list +//--------------------------------------------------------------------------- + +void Tokenizer::simplifyTokenList() +{ + // Remove unwanted keywords + static const char* unwantedWords[] = { "unsigned", "unlikely" }; + for (Token *tok = _tokens; tok; tok = tok->next()) + { + for (unsigned ui = 0; ui < sizeof(unwantedWords) / sizeof(unwantedWords[0]) && tok->next(); ui++) + { + if (tok->next()->str() == unwantedWords[ui]) + { + tok->deleteNext(); + break; + } + } + } + + // Replace constants.. + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok, "const %type% %var% = %num% ;")) + { + const char *sym = tok->strAt(2); + const char *num = tok->strAt(4); + + for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) + { + if (tok2->str() == sym) + { + tok2->str(num); + } + } + } + } + + + // Fill the map _typeSize.. + _typeSize.clear(); + _typeSize["char"] = sizeof(char); + _typeSize["short"] = sizeof(short); + _typeSize["int"] = sizeof(int); + _typeSize["long"] = sizeof(long); + _typeSize["float"] = sizeof(float); + _typeSize["double"] = sizeof(double); + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok, "class %var%")) + { + _typeSize[tok->strAt(1)] = 11; + } + + else if (Token::Match(tok, "struct %var%")) + { + _typeSize[tok->strAt(1)] = 13; + } + } + + + // Replace 'sizeof(type)'.. + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (tok->str() != "sizeof") + continue; + + if (Token::Match(tok, "sizeof ( %type% * )")) + { + std::ostringstream str; + // 'sizeof(type *)' has the same size as 'sizeof(char *)' + str << sizeof(char *); + tok->str(str.str().c_str()); + + for (int i = 0; i < 4; i++) + { + tok->deleteNext(); + } + } + + else if (Token::Match(tok, "sizeof ( %type% )")) + { + const char *type = tok->strAt(2); + int size = SizeOfType(type); + if (size > 0) + { + std::ostringstream str; + str << size; + tok->str(str.str().c_str()); + for (int i = 0; i < 3; i++) + { + tok->deleteNext(); + } + } + } + + else if (Token::Match(tok, "sizeof ( * %var% )")) + { + tok->str("100"); + for (int i = 0; i < 4; ++i) + tok->deleteNext(); + } + } + + // Replace 'sizeof(var)' + for (Token *tok = _tokens; tok; tok = tok->next()) + { + // type array [ num ] ; + if (! Token::Match(tok, "%type% %var% [ %num% ] ;")) + continue; + + int size = SizeOfType(tok->aaaa()); + if (size <= 0) + continue; + + const char *varname = tok->strAt(1); + int total_size = size * std::atoi(tok->strAt(3)); + + // Replace 'sizeof(var)' with number + int indentlevel = 0; + for (Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) + { + if (tok2->str() == "{") + { + ++indentlevel; + } + + else if (tok2->str() == "}") + { + --indentlevel; + if (indentlevel < 0) + break; + } + + // Todo: Token::Match varname directly + else if (Token::Match(tok2, "sizeof ( %var% )")) + { + if (strcmp(tok2->strAt(2), varname) == 0) + { + std::ostringstream str; + str << total_size; + tok2->str(str.str().c_str()); + // Delete the other tokens.. + for (int i = 0; i < 3; i++) + { + tok2->deleteNext(); + } + } + } + } + } + + + + + // Simple calculations.. + for (bool done = false; !done; done = true) + { + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::simpleMatch(tok->next(), "* 1") || Token::simpleMatch(tok->next(), "1 *")) + { + for (int i = 0; i < 2; i++) + tok->deleteNext(); + done = false; + } + + // (1-2) + if (Token::Match(tok, "[[,(=<>] %num% [+-*/] %num% [],);=<>]")) + { + int i1 = std::atoi(tok->strAt(1)); + int i2 = std::atoi(tok->strAt(3)); + if (i2 == 0 && *(tok->strAt(2)) == '/') + { + continue; + } + + switch (*(tok->strAt(2))) + { + case '+': + i1 += i2; + break; + case '-': + i1 -= i2; + break; + case '*': + i1 *= i2; + break; + case '/': + i1 /= i2; + break; + } + tok = tok->next(); + std::ostringstream str; + str << i1; + tok->str(str.str().c_str()); + for (int i = 0; i < 2; i++) + { + tok->deleteNext(); + } + + done = false; + } + } + } + + + // Replace "*(str + num)" => "str[num]" + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (! strchr(";{}(=<>", tok->aaaa0())) + continue; + + Token *next = tok->next(); + if (! next) + break; + + if (Token::Match(next, "* ( %var% + %num% )")) + { + const char *str[4] = {"var", "[", "num", "]"}; + str[0] = tok->strAt(3); + str[2] = tok->strAt(5); + + for (int i = 0; i < 4; i++) + { + tok = tok->next(); + tok->str(str[i]); + } + + tok->deleteNext(); + tok->deleteNext(); + } + } + + + + // Split up variable declarations if possible.. + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (! Token::Match(tok, "[{};]")) + continue; + + Token *type0 = tok->next(); + if (!Token::Match(type0, "%type%")) + continue; + if (Token::Match(type0, "else|return")) + continue; + + Token *tok2 = NULL; + unsigned int typelen = 0; + + if (Token::Match(type0, "%type% %var% ,|=")) + { + if (type0->next()->str() != "operator") + { + tok2 = type0->tokAt(2); // The ',' or '=' token + typelen = 1; + } + } + + else if (Token::Match(type0, "%type% * %var% ,|=")) + { + if (type0->next()->next()->str() != "operator") + { + tok2 = type0->tokAt(3); // The ',' token + typelen = 1; + } + } + + else if (Token::Match(type0, "%type% %var% [ %num% ] ,|=")) + { + tok2 = type0->tokAt(5); // The ',' token + typelen = 1; + } + + else if (Token::Match(type0, "%type% * %var% [ %num% ] ,|=")) + { + tok2 = type0->tokAt(6); // The ',' token + typelen = 1; + } + + else if (Token::Match(type0, "struct %type% %var% ,|=")) + { + tok2 = type0->tokAt(3); + typelen = 2; + } + + else if (Token::Match(type0, "struct %type% * %var% ,|=")) + { + tok2 = type0->tokAt(4); + typelen = 2; + } + + + if (tok2) + { + if (tok2->str() == ",") + { + tok2->str(";"); + InsertTokens(tok2, type0, typelen); + } + + else + { + Token *eq = tok2; + + int parlevel = 0; + while (tok2) + { + if (strchr("{(", tok2->aaaa0())) + { + ++parlevel; + } + + else if (strchr("})", tok2->aaaa0())) + { + if (parlevel < 0) + break; + --parlevel; + } + + else if (parlevel == 0 && strchr(";,", tok2->aaaa0())) + { + // "type var =" => "type var; var =" + Token *VarTok = type0->tokAt(typelen); + if (VarTok->aaaa0() == '*') + VarTok = VarTok->next(); + InsertTokens(eq, VarTok, 2); + eq->str(";"); + + // "= x, " => "= x; type " + if (tok2->str() == ",") + { + tok2->str(";"); + InsertTokens(tok2, type0, typelen); + } + break; + } + + tok2 = tok2->next(); + } + } + } + } + + // Replace NULL with 0.. + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (tok->str() == "NULL") + tok->str("0"); + } + + // Replace pointer casts of 0.. "(char *)0" => "0" + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok->next(), "( %type% * ) 0") || Token::Match(tok->next(), "( %type% %type% * ) 0")) + { + while (!Token::simpleMatch(tok->next(), "0")) + tok->deleteNext(); + } + } + + simplifyIfAddBraces(); + + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok, "case %any% : %var%")) + tok->next()->next()->insertToken(";"); + if (Token::Match(tok, "default : %var%")) + tok->next()->insertToken(";"); + } + + bool modified = true; + while (modified) + { + modified = false; + modified |= simplifyConditions(); + modified |= simplifyCasts(); + modified |= simplifyFunctionReturn(); + modified |= simplifyKnownVariables(); + modified |= removeReduntantConditions(); + } +} +//--------------------------------------------------------------------------- + +const Token *Tokenizer::findClosing(const Token *tok, const char *start, const char *end) +{ + if (!tok) + return 0; + + // Find the closing "}" + int indentLevel = 0; + for (const Token *closing = tok->next(); closing; closing = closing->next()) + { + if (closing->str() == start) + { + ++indentLevel; + continue; + } + + if (closing->str() == end) + --indentLevel; + + if (indentLevel >= 0) + continue; + + // Closing } is found. + return closing; + } + + return 0; +} + +bool Tokenizer::removeReduntantConditions() +{ + bool ret = false; + + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (!Token::simpleMatch(tok, "if")) + continue; + + if (!Token::Match(tok->tokAt(1), "( %bool% ) {")) + continue; + + // Find matching else + const Token *elseTag = 0; + + // Find the closing "}" + elseTag = Tokenizer::findClosing(tok->tokAt(4), "{", "}"); + if (elseTag) + elseTag = elseTag->next(); + + bool boolValue = false; + if (tok->tokAt(2)->str() == "true") + boolValue = true; + + // Handle if with else + if (elseTag && elseTag->str() == "else") + { + if (Token::simpleMatch(elseTag->next(), "if")) + { + // Handle "else if" + if (boolValue == false) + { + // Convert "if( false ) {aaa;} else if() {bbb;}" => "if() {bbb;}" + Token::eraseTokens(tok, elseTag->tokAt(2)); + ret = true; + } + else + { + // Keep first if, remove every else if and else after it + const Token *lastTagInIf = elseTag->tokAt(2); + while (lastTagInIf) + { + if (lastTagInIf->str() == "(") + { + lastTagInIf = Tokenizer::findClosing(lastTagInIf, "(", ")"); + lastTagInIf = lastTagInIf->next(); + } + + lastTagInIf = Tokenizer::findClosing(lastTagInIf, "{", "}"); + lastTagInIf = lastTagInIf->next(); + if (!Token::simpleMatch(lastTagInIf, "else")) + break; + + lastTagInIf = lastTagInIf->next(); + if (Token::simpleMatch(lastTagInIf, "if")) + lastTagInIf = lastTagInIf->next(); + } + + Token::eraseTokens(elseTag->previous(), lastTagInIf); + ret = true; + } + } + else + { + // Handle else + if (boolValue == false) + { + // Convert "if( false ) {aaa;} else {bbb;}" => "{bbb;}" or ";{bbb;}" + if (tok->previous()) + tok = tok->previous(); + else + tok->str(";"); + + Token::eraseTokens(tok, elseTag->tokAt(1)); + } + else + { + if (Token::simpleMatch(elseTag->tokAt(1), "{")) + { + // Convert "if( true ) {aaa;} else {bbb;}" => "{aaa;}" + const Token *end = Tokenizer::findClosing(elseTag->tokAt(1), "{", "}"); + if (!end) + { + // Possibly syntax error in code + return false; + } + + // Remove the "else { aaa; }" + Token::eraseTokens(elseTag->previous(), end->tokAt(1)); + } + + // Remove "if( true )" + if (tok->previous()) + tok = tok->previous(); + else + tok->str(";"); + + Token::eraseTokens(tok, tok->tokAt(5)); + } + + ret = true; + } + } + + // Handle if without else + else + { + if (boolValue == false) + { + // Remove if and its content + if (tok->previous()) + tok = tok->previous(); + else + tok->str(";"); + + Token::eraseTokens(tok, elseTag); + } + else + { + // convert "if( true ) {aaa;}" => "{aaa;}" + if (tok->previous()) + tok = tok->previous(); + else + tok->str(";"); + + Token::eraseTokens(tok, tok->tokAt(5)); + } + + ret = true; + } + } + + return ret; +} + +bool Tokenizer::simplifyIfAddBraces() +{ + bool ret = false; + + for (Token *tok = _tokens; tok; tok = tok ? tok->next() : NULL) + { + if (Token::Match(tok, "if|for|while (")) + { + // Goto the ending ')' + int parlevel = 1; + tok = tok->next(); + while (parlevel >= 1 && (tok = tok->next())) + { + if (tok->str() == "(") + ++parlevel; + else if (tok->str() == ")") + --parlevel; + } + + // ')' should be followed by '{' + if (!tok || Token::simpleMatch(tok, ") {")) + continue; + } + + else if (tok->str() == "else") + { + // An else followed by an if or brace don't need to be processed further + if (Token::Match(tok, "else if|{")) + continue; + } + + else + { + continue; + } + + // insert open brace.. + tok->insertToken("{"); + tok = tok->next(); + + // insert close brace.. + // In most cases it would work to just search for the next ';' and insert a closing brace after it. + // But here are special cases.. + // * if (cond) for (;;) break; + // * if (cond1) if (cond2) { } + int parlevel = 0; + int indentlevel = 0; + while ((tok = tok->next()) != NULL) + { + if (tok->str() == "{") + ++indentlevel; + + else if (tok->str() == "}") + { + --indentlevel; + if (indentlevel == 0) + break; + } + + else if (tok->str() == "(") + ++parlevel; + + else if (tok->str() == ")") + --parlevel; + + else if (indentlevel == 0 && parlevel == 0 && tok->str() == ";") + break; + } + + if (tok) + { + tok->insertToken("}"); + ret = true; + } + } + + return ret; +} + +bool Tokenizer::simplifyConditions() +{ + bool ret = false; + + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::simpleMatch(tok, "( true &&") || Token::simpleMatch(tok, "&& true &&") || Token::simpleMatch(tok->next(), "&& true )")) + { + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + else if (Token::simpleMatch(tok, "( false ||") || Token::simpleMatch(tok, "|| false ||") || Token::simpleMatch(tok->next(), "|| false )")) + { + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + // Change numeric constant in condition to "true" or "false" + const Token *tok2 = tok->tokAt(2); + if ((tok->str() == "(" || tok->str() == "&&" || tok->str() == "||") && + Token::Match(tok->next(), "%num%") && + tok2 && + (tok2->str() == ")" || tok2->str() == "&&" || tok2->str() == "||")) + { + tok->next()->str((tok->next()->str() != "0") ? "true" : "false"); + ret = true; + } + + // Reduce "(%num% == %num%)" => "(true)"/"(false)" + const Token *tok4 = tok->tokAt(4); + if (! tok4) + break; + if ((tok->str() == "&&" || tok->str() == "||" || tok->str() == "(") && + Token::Match(tok->tokAt(1), "%num% %any% %num%") && + (tok4->str() == "&&" || tok4->str() == "||" || tok4->str() == ")")) + { + double op1 = (strstr(tok->strAt(1), "0x")) ? std::strtol(tok->strAt(1), 0, 16) : std::atof(tok->strAt(1)); + double op2 = (strstr(tok->strAt(3), "0x")) ? std::strtol(tok->strAt(3), 0, 16) : std::atof(tok->strAt(3)); + std::string cmp = tok->strAt(2); + + bool result = false; + if (cmp == "==") + result = (op1 == op2); + else if (cmp == "!=") + result = (op1 != op2); + else if (cmp == ">=") + result = (op1 >= op2); + else if (cmp == ">") + result = (op1 > op2); + else if (cmp == "<=") + result = (op1 <= op2); + else if (cmp == "<") + result = (op1 < op2); + else + cmp = ""; + + if (! cmp.empty()) + { + tok = tok->next(); + tok->deleteNext(); + tok->deleteNext(); + + tok->str(result ? "true" : "false"); + ret = true; + } + } + } + + return ret; +} + + +bool Tokenizer::simplifyCasts() +{ + bool ret = false; + for (Token *tok = _tokens; tok; tok = tok->next()) + { + if (Token::Match(tok->next(), "( %type% * )")) + { + tok->deleteNext(); + tok->deleteNext(); + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + else if (Token::Match(tok->next(), "dynamic_cast|reinterpret_cast|const_cast|static_cast <")) + { + while (tok->next() && tok->next()->str() != ">") + tok->deleteNext(); + tok->deleteNext(); + tok->deleteNext(); + Token *tok2 = tok; + int parlevel = 0; + while (tok2->next() && parlevel >= 0) + { + tok2 = tok2->next(); + if (Token::simpleMatch(tok2->next(), "(")) + ++parlevel; + else if (Token::simpleMatch(tok2->next(), ")")) + --parlevel; + } + if (tok2->next()) + tok2->deleteNext(); + + ret = true; + } + } + + return ret; +} + + + +bool Tokenizer::simplifyFunctionReturn() +{ + bool ret = false; + int indentlevel = 0; + for (const Token *tok = tokens(); tok; tok = tok->next()) + { + if (tok->str() == "{") + ++indentlevel; + + else if (tok->str() == "}") + --indentlevel; + + else if (indentlevel == 0 && Token::Match(tok, "%var% ( ) { return %num% ; }")) + { + std::ostringstream pattern; + pattern << "[(=+-*/] " << tok->str() << " ( ) [;)+-*/]"; + for (Token *tok2 = _tokens; tok2; tok2 = tok2->next()) + { + if (Token::Match(tok2, pattern.str().c_str())) + { + tok2 = tok2->next(); + tok2->str(tok->strAt(5)); + tok2->deleteNext(); + tok2->deleteNext(); + ret = true; + } + } + } + } + + return ret; +} + +bool Tokenizer::simplifyKnownVariables() +{ + bool ret = false; + for (Token *tok = _tokens; tok; tok = tok->next()) + { + // Search for a block of code + if (! Token::Match(tok, ") const| {")) + continue; + + // parse the block of code.. + int indentlevel = 0; + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + { + + if (tok2->str() == "{") + ++indentlevel; + + else if (tok2->str() == "}") + { + --indentlevel; + if (indentlevel <= 0) + break; + } + + else if (Token::Match(tok2, "%var% = %num% ;") || + Token::Match(tok2, "%var% = %bool% ;")) + { + unsigned int varid = tok2->varId(); + if (varid == 0) + continue; + + for (Token *tok3 = tok2->next(); tok3; tok3 = tok3->next()) + { + // Perhaps it's a loop => bail out + if (Token::Match(tok3, "[{}]")) + break; + + // Variable is used somehow in a non-defined pattern => bail out + if (tok3->varId() == varid) + break; + + // Replace variable with numeric constant.. + if (Token::Match(tok3, "if ( %varid% )", varid)) + { + tok3 = tok3->next()->next(); + tok3->str(tok2->strAt(2)); + ret = true; + } + } + } + } + } + + return ret; +} + +//--------------------------------------------------------------------------- +// Helper functions for handling the tokens list +//--------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- + +const Token *Tokenizer::GetFunctionTokenByName(const char funcname[]) const +{ + for (unsigned int i = 0; i < _functionList.size(); ++i) + { + if (_functionList[i]->str() == funcname) + { + return _functionList[i]; + } + } + return NULL; +} + + +void Tokenizer::fillFunctionList() +{ + _functionList.clear(); + + int indentlevel = 0; + for (const Token *tok = _tokens; tok; tok = tok->next()) + { + if (tok->str() == "{") + ++indentlevel; + + else if (tok->str() == "}") + --indentlevel; + + if (indentlevel > 0) + { + continue; + } + + if (Token::Match(tok, "%var% (")) + { + // Check if this is the first token of a function implementation.. + for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) + { + if (tok2->str() == ";") + { + tok = tok2; + break; + } + + else if (tok2->str() == "{") + { + break; + } + + else if (tok2->str() == ")") + { + if (Token::Match(tok2, ") const| {")) + { + _functionList.push_back(tok); + tok = tok2; + } + else + { + tok = tok2; + while (tok->next() && !strchr(";{", tok->next()->aaaa0())) + tok = tok->next(); + } + break; + } + } + } + } + + // If the _functionList functions with duplicate names, remove them + // TODO this will need some better handling + for (unsigned int func1 = 0; func1 < _functionList.size();) + { + bool hasDuplicates = false; + for (unsigned int func2 = func1 + 1; func2 < _functionList.size();) + { + if (_functionList[func1]->str() == _functionList[func2]->str()) + { + hasDuplicates = true; + _functionList.erase(_functionList.begin() + func2); + } + else + { + ++func2; + } + } + + if (! hasDuplicates) + { + ++func1; + } + else + { + _functionList.erase(_functionList.begin() + func1); + } + } +} + +//--------------------------------------------------------------------------- + +// Deallocate lists.. +void Tokenizer::DeallocateTokens() +{ + deleteTokens(_tokens); + _tokens = 0; + _tokensBack = 0; + _files.clear(); +} + +void Tokenizer::deleteTokens(Token *tok) +{ + while (tok) + { + Token *next = tok->next(); + delete tok; + tok = next; + } +} + +//--------------------------------------------------------------------------- + +const char *Tokenizer::getParameterName(const Token *ftok, int par) +{ + int _par = 1; + for (; ftok; ftok = ftok->next()) + { + if (ftok->str() == ",") + ++_par; + if (par == _par && Token::Match(ftok, "%var% [,)]")) + return ftok->aaaa(); + } + return NULL; +} + +//--------------------------------------------------------------------------- + +std::string Tokenizer::fileLine(const Token *tok) const +{ + std::ostringstream ostr; + ostr << "[" << _files.at(tok->fileIndex()) << ":" << tok->linenr() << "]"; + return ostr.str(); +} + + + +//--------------------------------------------------------------------------- diff --git a/src/tokenize.h b/src/tokenize.h index df8f8a82f..560c18e38 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -1,146 +1,146 @@ -/* - * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, - * Leandro Penz, Kimmo Varis - * - * 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 -#include -#include -#include "settings.h" -#include "errorlogger.h" -#include "token.h" - -class Tokenizer -{ -private: - // Deallocate lists.. - void DeallocateTokens(); - -public: - Tokenizer(); - ~Tokenizer(); - - /** - * Tokenize code - * @param code input stream for code - * @param FileName The filename - */ - void tokenize(std::istream &code, const char FileName[]); - - /** Set variable id */ - void setVarId(); - - /** Simplify tokenlist */ - void simplifyTokenList(); - - - // Helper functions for handling the tokens list.. - - static void deleteTokens(Token *tok); - static const char *getParameterName(const Token *ftok, int par); - - static bool SameFileName(const char fname1[], const char fname2[]); - - - std::string fileLine(const Token *tok) const; - - // Return size. - int SizeOfType(const char type[]) const; - - void initTokens(); - - const std::vector *getFiles() const; - - void fillFunctionList(); - const Token *GetFunctionTokenByName(const char funcname[]) const; - const Token *tokens() const; - - -#ifndef UNIT_TESTING -private: -#endif - - - /** - * Finds matching "end" for "start". - * @param tok The start tag - * @param start e.g. "{" - * @param end e.g. "}" - * @return The end tag that matches given parameter or 0 if not found. - */ - static const Token *findClosing(const Token *tok, const char *start, const char *end); - - void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno); - - /** Add braces to an if-block - * @return true if something is modified - * false if nothing is done. - */ - bool simplifyIfAddBraces(); - - /** Simplify conditions - * @return true if something is modified - * false if nothing is done. - */ - bool simplifyConditions(); - - /** Remove reduntant code, e.g. if( false ) { int a; } should be - * removed, because it is never executed. - * @return true if something is modified - * false if nothing is done. - */ - bool removeReduntantConditions(); - - /** Simplify casts - * @return true if something is modified - * false if nothing is done. - */ - bool simplifyCasts(); - - /** Simplify function calls - constant return value - * @return true if something is modified - * false if nothing is done. - */ - bool simplifyFunctionReturn(); - - /** - * A simplify function that replaces a variable with its value in cases - * when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)" - * - * @param token The token list to check and modify. - * @return true if modifications to token-list are done. - * false if no modifications are done. - */ - bool simplifyKnownVariables(); - - void InsertTokens(Token *dest, Token *src, unsigned int n); - - Token *_tokensBack; - std::map _typeSize; - std::vector _functionList; - std::vector _files; - Token *_tokens; -}; - -//--------------------------------------------------------------------------- -#endif +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, + * Leandro Penz, Kimmo Varis + * + * 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 +#include +#include +#include "settings.h" +#include "errorlogger.h" +#include "token.h" + +class Tokenizer +{ +private: + // Deallocate lists.. + void DeallocateTokens(); + +public: + Tokenizer(); + ~Tokenizer(); + + /** + * Tokenize code + * @param code input stream for code + * @param FileName The filename + */ + void tokenize(std::istream &code, const char FileName[]); + + /** Set variable id */ + void setVarId(); + + /** Simplify tokenlist */ + void simplifyTokenList(); + + + // Helper functions for handling the tokens list.. + + static void deleteTokens(Token *tok); + static const char *getParameterName(const Token *ftok, int par); + + static bool SameFileName(const char fname1[], const char fname2[]); + + + std::string fileLine(const Token *tok) const; + + // Return size. + int SizeOfType(const char type[]) const; + + void initTokens(); + + const std::vector *getFiles() const; + + void fillFunctionList(); + const Token *GetFunctionTokenByName(const char funcname[]) const; + const Token *tokens() const; + + +#ifndef UNIT_TESTING +private: +#endif + + + /** + * Finds matching "end" for "start". + * @param tok The start tag + * @param start e.g. "{" + * @param end e.g. "}" + * @return The end tag that matches given parameter or 0 if not found. + */ + static const Token *findClosing(const Token *tok, const char *start, const char *end); + + void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno); + + /** Add braces to an if-block + * @return true if something is modified + * false if nothing is done. + */ + bool simplifyIfAddBraces(); + + /** Simplify conditions + * @return true if something is modified + * false if nothing is done. + */ + bool simplifyConditions(); + + /** Remove reduntant code, e.g. if( false ) { int a; } should be + * removed, because it is never executed. + * @return true if something is modified + * false if nothing is done. + */ + bool removeReduntantConditions(); + + /** Simplify casts + * @return true if something is modified + * false if nothing is done. + */ + bool simplifyCasts(); + + /** Simplify function calls - constant return value + * @return true if something is modified + * false if nothing is done. + */ + bool simplifyFunctionReturn(); + + /** + * A simplify function that replaces a variable with its value in cases + * when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)" + * + * @param token The token list to check and modify. + * @return true if modifications to token-list are done. + * false if no modifications are done. + */ + bool simplifyKnownVariables(); + + void InsertTokens(Token *dest, Token *src, unsigned int n); + + Token *_tokensBack; + std::map _typeSize; + std::vector _functionList; + std::vector _files; + Token *_tokens; +}; + +//--------------------------------------------------------------------------- +#endif