diff --git a/checkbufferoverrun.cpp b/checkbufferoverrun.cpp index 5213f887c..c7ef9398b 100644 --- a/checkbufferoverrun.cpp +++ b/checkbufferoverrun.cpp @@ -1,564 +1,564 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 TOKEN *tok, const char errmsg[]) -{ - std::ostringstream ostr; - std::list::const_iterator it; - for ( it = _callStack.begin(); it != _callStack.end(); it++ ) - ostr << _tokenizer->fileLine(*it ) << " -> "; - ostr << _tokenizer->fileLine(tok) << ": " << 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 = 1; - while ( varname[varc] ) - varc++; - varc = 2 * ( varc - 1 ); - - - // Array index.. - if ( varid > 0 ) - { - if ( TOKEN::Match(tok, "%varid% [ %num% ]", 0, 0, varid) ) - { - const char *num = tok->strAt(2); - if (strtol(num, NULL, 10) >= size) - { - ReportError(tok->next(), "Array index out of bounds"); - } - } - } - else if ( TOKEN::Match(tok, "%var1% [ %num% ]", varname) ) - { - const char *num = tok->strAt(2 + varc); - if (strtol(num, NULL, 10) >= size) - { - ReportError(tok->next(), "Array index out of bounds"); - } - } - - - 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% ]", 0, 0, varid) ) - { - const char *num = tok->strAt(3); - if (strtol(num, NULL, 10) >= size) - { - ReportError(tok->next(), "Array index out of bounds"); - } - } - } - else if ( !tok->isName() && !TOKEN::Match(tok,"[.&]") && TOKEN::Match(tok->next(), "%var1% [ %num% ]", varname) ) - { - const char *num = tok->next()->strAt(2 + varc); - if (strtol(num, NULL, 10) >= size) - { - ReportError(tok->next(), "Array index out of bounds"); - } - 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% )", 0, 0, varid ) || - TOKEN::Match( tok->next(), "( %var% , %varid% , %num% )", 0, 0, varid ) ) - { - const char *num = tok->strAt(6); - if ( atoi(num) > total_size ) - { - ReportError(tok, "Buffer overrun"); - } - } - continue; - } - } - else if (TOKEN::Match(tok,"memset|memcpy|memmove|memcmp|strncpy|fgets") ) - { - if ( TOKEN::Match( tok->next(), "( %var1% , %num% , %num% )", varname ) || - TOKEN::Match( tok->next(), "( %var% , %var1% , %num% )", varname ) ) - { - const char *num = tok->strAt(varc + 6); - if ( atoi(num) > total_size ) - { - ReportError(tok, "Buffer overrun"); - } - } - continue; - } - - - // Loop.. - if ( TOKEN::Match(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 && !TOKEN::Match(tok2,")")) - tok2 = tok2->next(); - if (!(tok2->tokAt(5))) - break; - - std::ostringstream pattern; - pattern << "%var1% [ " << strindex << " ]"; - - int indentlevel2 = 0; - while (tok2) - { - 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(), varname ) ) - { - ReportError(tok2, "Buffer overrun"); - break; - } - - tok2 = tok2->next(); - } - continue; - } - - - // Writing data into array.. - if ( TOKEN::Match(tok, "strcpy ( %var1% , %str% )", varname) ) - { - 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(tok, "Buffer overrun"); - } - continue; - } - - - // 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, "[(,] %var1% [,)]", varname) ) - { - 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% ,") || 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; - - 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(); - } - else if (indentlevel > 0 && 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(); - } - else - { - continue; - } - - int total_size = size * _tokenizer->SizeOfType(type); - if (total_size == 0) - continue; - - // The callstack is empty - _callStack.clear(); - CheckBufferOverrun_CheckScope( tok->tokAt(5), varname, size, total_size, varid ); - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Checking member variables of structs.. -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::CheckBufferOverrun_StructVariable() -{ - const char *declstruct_pattern[] = {"","","{",0}; - for ( const TOKEN * tok = TOKEN::findtoken( _tokenizer->tokens(), declstruct_pattern ); - tok; - tok = TOKEN::findtoken( tok->next(), declstruct_pattern ) ) - { - if ( ! TOKEN::Match(tok,"struct|class") ) - continue; - - const char *structname = tok->next()->aaaa(); - - if ( !(tok->next()->isName()) ) - continue; - - // Found a struct declaration. Search for arrays.. - for ( const TOKEN * tok2 = tok->next()->next(); tok2; tok2 = tok2->next() ) - { - if ( TOKEN::Match(tok2, "}") ) - 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 ( TOKEN::Match(tok, "class") ) - { - std::string func_pattern(structname + std::string(" :: %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::Match(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 ( strcmp(tok3->aaaa(), 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 ( TOKEN::Match(tok3, ";") ) - { - CheckTok = tok3; - break; - } - - // End of function declaration.. - if ( TOKEN::Match(tok3, ") ;") ) - break; - - // Function implementation.. - if ( TOKEN::Match(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::CheckBufferOverrun() -{ - CheckBufferOverrun_LocalVariable(); - CheckBufferOverrun_StructVariable(); -} -//--------------------------------------------------------------------------- - - - - - - - - -//--------------------------------------------------------------------------- -// Dangerous functions -//--------------------------------------------------------------------------- - -void CheckBufferOverrunClass::WarningDangerousFunctions() -{ - for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (TOKEN::Match(tok, "gets (")) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Found 'gets'. You should use 'fgets' instead"; - _errorLogger->reportErr(ostr.str()); - } - - else if (TOKEN::Match(tok, "scanf (") && strcmp(tok->strAt(2),"\"%s\"") == 0) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Found 'scanf'. You should use 'fgets' instead"; - _errorLogger->reportErr(ostr.str()); - } - } -} -//--------------------------------------------------------------------------- - - - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 TOKEN *tok, const char errmsg[]) +{ + std::ostringstream ostr; + std::list::const_iterator it; + for ( it = _callStack.begin(); it != _callStack.end(); it++ ) + ostr << _tokenizer->fileLine(*it ) << " -> "; + ostr << _tokenizer->fileLine(tok) << ": " << 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 = 1; + while ( varname[varc] ) + varc++; + varc = 2 * ( varc - 1 ); + + + // Array index.. + if ( varid > 0 ) + { + if ( TOKEN::Match(tok, "%varid% [ %num% ]", 0, 0, varid) ) + { + const char *num = tok->strAt(2); + if (strtol(num, NULL, 10) >= size) + { + ReportError(tok->next(), "Array index out of bounds"); + } + } + } + else if ( TOKEN::Match(tok, "%var1% [ %num% ]", varname) ) + { + const char *num = tok->strAt(2 + varc); + if (strtol(num, NULL, 10) >= size) + { + ReportError(tok->next(), "Array index out of bounds"); + } + } + + + 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% ]", 0, 0, varid) ) + { + const char *num = tok->strAt(3); + if (strtol(num, NULL, 10) >= size) + { + ReportError(tok->next(), "Array index out of bounds"); + } + } + } + else if ( !tok->isName() && !TOKEN::Match(tok,"[.&]") && TOKEN::Match(tok->next(), "%var1% [ %num% ]", varname) ) + { + const char *num = tok->next()->strAt(2 + varc); + if (strtol(num, NULL, 10) >= size) + { + ReportError(tok->next(), "Array index out of bounds"); + } + 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% )", 0, 0, varid ) || + TOKEN::Match( tok->next(), "( %var% , %varid% , %num% )", 0, 0, varid ) ) + { + const char *num = tok->strAt(6); + if ( atoi(num) > total_size ) + { + ReportError(tok, "Buffer overrun"); + } + } + continue; + } + } + else if (TOKEN::Match(tok,"memset|memcpy|memmove|memcmp|strncpy|fgets") ) + { + if ( TOKEN::Match( tok->next(), "( %var1% , %num% , %num% )", varname ) || + TOKEN::Match( tok->next(), "( %var% , %var1% , %num% )", varname ) ) + { + const char *num = tok->strAt(varc + 6); + if ( atoi(num) > total_size ) + { + ReportError(tok, "Buffer overrun"); + } + } + continue; + } + + + // Loop.. + if ( TOKEN::Match(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 && !TOKEN::Match(tok2,")")) + tok2 = tok2->next(); + if (!(tok2->tokAt(5))) + break; + + std::ostringstream pattern; + pattern << "%var1% [ " << strindex << " ]"; + + int indentlevel2 = 0; + while (tok2) + { + 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(), varname ) ) + { + ReportError(tok2, "Buffer overrun"); + break; + } + + tok2 = tok2->next(); + } + continue; + } + + + // Writing data into array.. + if ( TOKEN::Match(tok, "strcpy ( %var1% , %str% )", varname) ) + { + 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(tok, "Buffer overrun"); + } + continue; + } + + + // 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, "[(,] %var1% [,)]", varname) ) + { + 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% ,") || 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; + + 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(); + } + else if (indentlevel > 0 && 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(); + } + else + { + continue; + } + + int total_size = size * _tokenizer->SizeOfType(type); + if (total_size == 0) + continue; + + // The callstack is empty + _callStack.clear(); + CheckBufferOverrun_CheckScope( tok->tokAt(5), varname, size, total_size, varid ); + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Checking member variables of structs.. +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::CheckBufferOverrun_StructVariable() +{ + const char *declstruct_pattern[] = {"","","{",0}; + for ( const TOKEN * tok = TOKEN::findtoken( _tokenizer->tokens(), declstruct_pattern ); + tok; + tok = TOKEN::findtoken( tok->next(), declstruct_pattern ) ) + { + if ( ! TOKEN::Match(tok,"struct|class") ) + continue; + + const char *structname = tok->next()->aaaa(); + + if ( !(tok->next()->isName()) ) + continue; + + // Found a struct declaration. Search for arrays.. + for ( const TOKEN * tok2 = tok->next()->next(); tok2; tok2 = tok2->next() ) + { + if ( TOKEN::Match(tok2, "}") ) + 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 ( TOKEN::Match(tok, "class") ) + { + std::string func_pattern(structname + std::string(" :: %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::Match(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 ( strcmp(tok3->aaaa(), 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 ( TOKEN::Match(tok3, ";") ) + { + CheckTok = tok3; + break; + } + + // End of function declaration.. + if ( TOKEN::Match(tok3, ") ;") ) + break; + + // Function implementation.. + if ( TOKEN::Match(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::CheckBufferOverrun() +{ + CheckBufferOverrun_LocalVariable(); + CheckBufferOverrun_StructVariable(); +} +//--------------------------------------------------------------------------- + + + + + + + + +//--------------------------------------------------------------------------- +// Dangerous functions +//--------------------------------------------------------------------------- + +void CheckBufferOverrunClass::WarningDangerousFunctions() +{ + for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (TOKEN::Match(tok, "gets (")) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Found 'gets'. You should use 'fgets' instead"; + _errorLogger->reportErr(ostr.str()); + } + + else if (TOKEN::Match(tok, "scanf (") && strcmp(tok->strAt(2),"\"%s\"") == 0) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Found 'scanf'. You should use 'fgets' instead"; + _errorLogger->reportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + + + + diff --git a/checkbufferoverrun.h b/checkbufferoverrun.h index eb43aa749..eefbfefc1 100644 --- a/checkbufferoverrun.h +++ b/checkbufferoverrun.h @@ -1,54 +1,54 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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/checkclass.cpp b/checkclass.cpp index 223a756ce..7aea39733 100644 --- a/checkclass.cpp +++ b/checkclass.cpp @@ -1,693 +1,693 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - - -#ifdef __BORLANDC__ -#include -#include -#endif -//--------------------------------------------------------------------------- - -CheckClass::CheckClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ) -{ - _tokenizer = tokenizer; - _settings = settings; - _errorLogger = errorLogger; -} - -CheckClass::~CheckClass() -{ - -} - -//--------------------------------------------------------------------------- - -struct CheckClass::VAR *CheckClass::ClassChecking_GetVarList(const TOKEN *tok1) -{ - // Get variable list.. - struct VAR *varlist = NULL; - unsigned int indentlevel = 0; - for (const TOKEN *tok = tok1; tok; tok = tok->next()) - { - if (!tok->next()) - break; - - if (tok->aaaa0() == '{') - indentlevel++; - if (tok->aaaa0() == '}') - { - if (indentlevel <= 1) - break; - indentlevel--; - } - - - if (indentlevel==1 && (strchr(";{}", tok->aaaa0()) || (tok->aaaa0()!=':' && strchr(tok->aaaa(), ':')))) - { - const TOKEN *next = tok->next(); - - const char *varname = 0; - - // Is it a variable declaration? - if ( TOKEN::Match(next,"%type% %var% ;") ) - { - const char *types[] = {"bool", "char", "int", "short", "long", "float", "double", 0}; - for ( int type = 0; types[type]; type++ ) - { - if ( strcmp(next->aaaa(), types[type]) == 0) - { - varname = next->next()->aaaa(); - break; - } - } - } - - // Pointer? - else if ( TOKEN::Match(next, "%type% * %var% ;") ) - { - varname = next->strAt(2); - } - - if (varname) - { - struct VAR *var = new VAR; - memset(var, 0, sizeof(struct VAR)); - var->name = varname; - var->init = false; - var->next = varlist; - varlist = var; - } - } - } - - return varlist; -} -//--------------------------------------------------------------------------- - -const TOKEN * CheckClass::FindClassFunction( const TOKEN *tok, const char classname[], const char funcname[], int &indentlevel ) -{ - if ( indentlevel < 0 || tok == NULL ) - return NULL; - - std::ostringstream classPattern; - classPattern << "class " << classname << " :|{"; - - std::ostringstream internalPattern; - internalPattern << funcname << " ("; - - std::ostringstream externalPattern; - externalPattern << classname << " :: " << funcname << " ("; - - for ( ;tok; tok = tok->next() ) - { - if ( indentlevel == 0 && TOKEN::Match(tok, classPattern.str().c_str()) ) - { - while ( tok && tok->str() != "{" ) - tok = tok->next(); - if ( tok ) - tok = tok->next(); - if ( ! tok ) - break; - indentlevel = 1; - } - - if ( tok->str() == "{" ) - { - // If indentlevel==0 don't go to indentlevel 1. Skip the block. - if ( indentlevel > 0 ) - ++indentlevel; - - else - { - for ( ; tok; tok = tok->next() ) - { - if ( tok->str() == "{" ) - ++indentlevel; - else if ( tok->str() == "}" ) - { - --indentlevel; - if ( indentlevel <= 0 ) - break; - } - } - if ( tok == NULL ) - return NULL; - - continue; - } - } - - if ( tok->str() == "}" ) - { - indentlevel--; - if ( indentlevel < 0 ) - return NULL; - } - - if ( indentlevel == 1 ) - { - // Member function implemented in the class declaration? - if (!TOKEN::Match(tok,"~") && TOKEN::Match(tok->next(), internalPattern.str().c_str())) - { - const TOKEN *tok2 = tok; - while ( tok2 && tok2->str() != "{" && tok2->str() != ";" ) - tok2 = tok2->next(); - if ( tok2 && tok2->str() == "{" ) - return tok->next(); - } - } - - else if ( indentlevel == 0 && TOKEN::Match(tok, externalPattern.str().c_str()) ) - { - return tok; - } - } - - // Not found - return NULL; -} -//--------------------------------------------------------------------------- - -void CheckClass::InitVar(struct VAR *varlist, const char varname[]) -{ - for (struct VAR *var = varlist; var; var = var->next) - { - if ( strcmp(var->name, varname) == 0 ) - { - var->init = true; - break; - } - } -} -//--------------------------------------------------------------------------- - -void CheckClass::ClassChecking_VarList_Initialize(const TOKEN *tok1, const TOKEN *ftok, struct VAR *varlist, const char classname[], std::list &callstack) -{ - bool Assign = false; - unsigned int indentlevel = 0; - - for (; ftok; ftok = ftok->next()) - { - if (!ftok->next()) - break; - - // Class constructor.. initializing variables like this - // clKalle::clKalle() : var(value) { } - if (indentlevel==0) - { - if (Assign && TOKEN::Match(ftok, "%var% (")) - { - InitVar( varlist, ftok->aaaa() ); - } - - Assign |= (ftok->aaaa0() == ':'); - } - - - if (ftok->aaaa0() == '{') - { - indentlevel++; - Assign = false; - } - - if (ftok->aaaa0() == '}') - { - if (indentlevel <= 1) - break; - indentlevel--; - } - - if ( indentlevel < 1 ) - continue; - - // Before a new statement there is "[{};)=]" or "else" - if ( ! TOKEN::Match(ftok, "[{};)=]") && ! TOKEN::Match(ftok, "else") ) - continue; - - // Using the operator= function to initialize all variables.. - if ( TOKEN::Match(ftok->next(), "* this = ") ) - { - for (struct VAR *var = varlist; var; var = var->next) - var->init = true; - break; - } - - if (!TOKEN::Match(ftok->next(), "%var%") && !TOKEN::Match(ftok->next(), "this . %var%")) - continue; - - // Goto the first token in this statement.. - ftok = ftok->next(); - - // Skip "this->" - if ( TOKEN::Match(ftok, "this .") ) - ftok = ftok->tokAt(2); - - // Clearing all variables.. - if (TOKEN::Match(ftok,"memset ( this ,")) - { - for (struct VAR *var = varlist; var; var = var->next) - var->init = true; - break; - } - - // Calling member function? - else if (TOKEN::Match(ftok, "%var% (")) - { - // No recursive calls! - if ( std::find(callstack.begin(),callstack.end(),ftok->aaaa()) == callstack.end() ) - { - callstack.push_back( ftok->aaaa() ); - int i = 0; - const TOKEN *ftok2 = FindClassFunction( tok1, classname, ftok->aaaa(), i ); - ClassChecking_VarList_Initialize(tok1, ftok2, varlist, classname, callstack); - } - } - - // Assignment of member variable? - else if (TOKEN::Match(ftok, "%var% =")) - { - InitVar( varlist, ftok->aaaa() ); - } - - // The functions 'clear' and 'Clear' are supposed to initialize variable. - if (TOKEN::Match(ftok,"%var% . clear (") || TOKEN::Match(ftok,"%var% . Clear (")) - { - InitVar( varlist, ftok->aaaa() ); - } - } -} - - - - - - -//--------------------------------------------------------------------------- -// ClassCheck: Check that all class constructors are ok. -//--------------------------------------------------------------------------- - -void CheckClass::CheckConstructors() -{ - const char pattern_class[] = "class %var% {"; - - // Locate class - const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), pattern_class ); - while (tok1) - { - const char *className[2]; - className[0] = tok1->strAt( 1 ); - className[1] = 0; - - // TODO: handling of private constructors should be improved. - bool hasPrivateConstructor = false; - { - int indentlevel = 0; - bool isPrivate = true; - for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) - { - // Indentation - if ( tok->str() == "{" ) - ++indentlevel; - - else if ( tok->str() == "}" ) - { - --indentlevel; - if (indentlevel <= 0) - break; - } - - // Parse class contents (indentlevel == 1).. - if ( indentlevel == 1 ) - { - // What section are we in.. private/non-private - if ( tok->str() == "private:" ) - isPrivate = true; - else if ( tok->str() == "protected:" || tok->str() == "public:" ) - isPrivate = false; - - // Is there a private constructor? - else if ( isPrivate && TOKEN::Match(tok, "%var1% (", className) ) - { - hasPrivateConstructor = true; - break; - } - } - } - } - - if ( hasPrivateConstructor ) - { - // TODO: Handle private constructors. - // Right now to avoid false positives I just bail out - tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); - continue; - } - - // Are there a class constructor? - const TOKEN *constructor_token = TOKEN::findmatch( tok1, "%any% %var1% (", className ); - while ( TOKEN::Match( constructor_token, "~" ) ) - constructor_token = TOKEN::findmatch( constructor_token->next(), "%any% %var1% (", className ); - - // There are no constructor. - if ( ! constructor_token ) - { - // If "--style" has been given, give a warning - if ( _settings._checkCodingStyle ) - { - // If the class has member variables there should be an constructor - struct VAR *varlist = ClassChecking_GetVarList(tok1); - if ( varlist ) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok1); - ostr << " The class '" << className[0] << "' has no constructor"; - _errorLogger->reportErr(ostr.str()); - } - // Delete the varlist.. - while (varlist) - { - struct VAR *nextvar = varlist->next; - delete varlist; - varlist = nextvar; - } - } - - tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); - continue; - } - - // Check that all member variables are initialized.. - struct VAR *varlist = ClassChecking_GetVarList(tok1); - - // Check constructors - CheckConstructors( tok1, varlist, className[0] ); - - // Check assignment operators - CheckConstructors( tok1, varlist, "operator =" ); - - // Delete the varlist.. - while (varlist) - { - struct VAR *nextvar = varlist->next; - delete varlist; - varlist = nextvar; - } - - tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); - } -} - -void CheckClass::CheckConstructors(const TOKEN *tok1, struct VAR *varlist, const char funcname[]) -{ - const char * const className = tok1->strAt(1); - - int indentlevel = 0; - const TOKEN *constructor_token = FindClassFunction( tok1, className, funcname, indentlevel ); - std::list callstack; - ClassChecking_VarList_Initialize(tok1, constructor_token, varlist, className, callstack); - while ( constructor_token ) - { - // Check if any variables are uninitialized - for (struct VAR *var = varlist; var; var = var->next) - { - if ( var->init ) - continue; - - // Is it a static member variable? - std::ostringstream pattern; - pattern << className << "::" << var->name << "="; - if (TOKEN::findmatch(_tokenizer->tokens(), pattern.str().c_str())) - continue; - - // It's non-static and it's not initialized => error - std::ostringstream ostr; - ostr << _tokenizer->fileLine(constructor_token); - ostr << " Uninitialized member variable '" << className << "::" << var->name << "'"; - _errorLogger->reportErr(ostr.str()); - } - - for ( struct VAR *var = varlist; var; var = var->next ) - var->init = false; - - constructor_token = FindClassFunction( constructor_token->next(), className, funcname, indentlevel ); - callstack.clear(); - ClassChecking_VarList_Initialize(tok1, constructor_token, varlist, className, callstack); - } -} - - -//--------------------------------------------------------------------------- -// ClassCheck: Unused private functions -//--------------------------------------------------------------------------- - -void CheckClass::CheckUnusedPrivateFunctions() -{ - // Locate some class - const char *pattern_class[] = {"class","","{",NULL}; - for (const TOKEN *tok1 = TOKEN::findtoken(_tokenizer->tokens(), pattern_class); tok1; tok1 = TOKEN::findtoken(tok1->next(), pattern_class)) - { - const char *classname = tok1->next()->aaaa(); - - // The class implementation must be available.. - const char *pattern_classconstructor[] = {"","::","",NULL}; - pattern_classconstructor[0] = classname; - pattern_classconstructor[2] = classname; - if (!TOKEN::findtoken(_tokenizer->tokens(),pattern_classconstructor)) - continue; - - // Get private functions.. - std::list FuncList; - FuncList.clear(); - bool priv = false; - unsigned int indent_level = 0; - for (const TOKEN *tok = tok1; tok; tok = tok->next()) - { - if (TOKEN::Match(tok,"friend %var%")) - { - // Todo: Handle friend classes - FuncList.clear(); - break; - } - - if (tok->aaaa0() == '{') - indent_level++; - if (tok->aaaa0() == '}') - { - if (indent_level <= 1) - break; - indent_level--; - } - if (strcmp(tok->aaaa(),"};") == 0) - break; - if (strcmp(tok->aaaa(),"private:") == 0) - priv = true; - else if (strcmp(tok->aaaa(),"public:") == 0) - priv = false; - else if (strcmp(tok->aaaa(),"protected:") == 0) - priv = false; - else if (priv && indent_level == 1) - { - if ( TOKEN::Match(tok, "typedef %type% (") ) - tok = tok->tokAt(2); - - if (TOKEN::Match(tok, "%var% (") && - !TOKEN::Match(tok,classname)) - { - FuncList.push_back(tok->aaaa()); - } - } - } - - // Check that all private functions are used.. - const char *pattern_function[] = {"","::",NULL}; - pattern_function[0] = classname; - bool HasFuncImpl = false; - const TOKEN *ftok = _tokenizer->tokens(); - while (ftok) - { - ftok = TOKEN::findtoken(ftok,pattern_function); - int numpar = 0; - while (ftok && ftok->aaaa0()!=';' && ftok->aaaa0()!='{') - { - if (ftok->aaaa0() == '(') - numpar++; - else if (ftok->aaaa0() == ')') - numpar--; - ftok = ftok->next(); - } - - if (!ftok) - break; - - if (ftok->aaaa0() != ';' && numpar == 0) - { - HasFuncImpl = true; - - indent_level = 0; - while (ftok) - { - if (ftok->aaaa0() == '{') - indent_level++; - if (ftok->aaaa0() == '}') - { - if (indent_level<=1) - break; - indent_level--; - } - if (ftok->next() && ftok->next()->aaaa0() == '(') - FuncList.remove(ftok->aaaa()); - ftok = ftok->next(); - } - } - - if (ftok) - ftok = ftok->next(); - } - - while (HasFuncImpl && !FuncList.empty()) - { - bool fp = false; - - // Final check; check if the function pointer is used somewhere.. - const char *_pattern[] = {"=","",NULL}; - _pattern[1] = FuncList.front().c_str(); - fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); - _pattern[0] = "return"; - fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); - _pattern[0] = "("; - fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); - _pattern[0] = ")"; - fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); - _pattern[0] = ","; - fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); - - if (!fp) - { - std::ostringstream ostr; - ostr << "Class '" << classname << "', unused private function: '" << FuncList.front() << "'"; - _errorLogger->reportErr(ostr.str()); - } - FuncList.pop_front(); - } - } -} - -//--------------------------------------------------------------------------- -// ClassCheck: Check that memset is not used on classes -//--------------------------------------------------------------------------- - -void CheckClass::CheckMemset() -{ - // Locate all 'memset' tokens.. - for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (!TOKEN::Match(tok,"memset") && !TOKEN::Match(tok,"memcpy") && !TOKEN::Match(tok,"memmove")) - continue; - - // Todo: Handle memcpy and memmove - const char *type = NULL; - if (TOKEN::Match(tok, "memset ( %var% , %num% , sizeof ( %type% ) )")) - type = tok->strAt(8); - else if (TOKEN::Match(tok, "memset ( & %var% , %num% , sizeof ( %type% ) )")) - type = tok->strAt(9); - else if (TOKEN::Match(tok, "memset ( %var% , %num% , sizeof ( struct %type% ) )")) - type = tok->strAt(9); - else if (TOKEN::Match(tok, "memset ( & %var% , %num% , sizeof ( struct %type% ) )")) - type = tok->strAt(10); - else if (TOKEN::Match(tok, "%type% ( %var% , %var% , sizeof ( %type% ) )")) - type = tok->strAt(8); - - // No type defined => The tokens didn't match - if (!(type && type[0])) - continue; - - // Warn if type is a class.. - const char *pattern1[] = {"class","",NULL}; - pattern1[1] = type; - if (TOKEN::findtoken(_tokenizer->tokens(),pattern1)) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Using '" << tok->aaaa() << "' on class."; - _errorLogger->reportErr(ostr.str()); - continue; - } - - // Warn if type is a struct that contains any std::* - const char *pattern2[] = {"struct","","{",NULL}; - pattern2[1] = type; - for (const TOKEN *tstruct = TOKEN::findtoken(_tokenizer->tokens(), pattern2); tstruct; tstruct = tstruct->next()) - { - if (tstruct->aaaa0() == '}') - break; - - if (TOKEN::Match(tstruct, "std :: %type% %var% ;")) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Using '" << tok->aaaa() << "' on struct that contains a 'std::" << tstruct->strAt(2) << "'"; - _errorLogger->reportErr(ostr.str()); - break; - } - } - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// ClassCheck: "void operator=(" -//--------------------------------------------------------------------------- - -void CheckClass::CheckOperatorEq1() -{ - if (const TOKEN *tok = TOKEN::findmatch(_tokenizer->tokens(), "void operator = (")) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": 'operator=' should return something"; - _errorLogger->reportErr(ostr.str()); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// A destructor in a base class should be virtual -//--------------------------------------------------------------------------- - -void CheckClass::virtualDestructor() -{ - const char pattern_classdecl[] = "class %var% : %var%"; - - const TOKEN *derived = _tokenizer->tokens(); - while ((derived = TOKEN::findmatch(derived, pattern_classdecl)) != NULL) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + + +#ifdef __BORLANDC__ +#include +#include +#endif +//--------------------------------------------------------------------------- + +CheckClass::CheckClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ) +{ + _tokenizer = tokenizer; + _settings = settings; + _errorLogger = errorLogger; +} + +CheckClass::~CheckClass() +{ + +} + +//--------------------------------------------------------------------------- + +struct CheckClass::VAR *CheckClass::ClassChecking_GetVarList(const TOKEN *tok1) +{ + // Get variable list.. + struct VAR *varlist = NULL; + unsigned int indentlevel = 0; + for (const TOKEN *tok = tok1; tok; tok = tok->next()) + { + if (!tok->next()) + break; + + if (tok->aaaa0() == '{') + indentlevel++; + if (tok->aaaa0() == '}') + { + if (indentlevel <= 1) + break; + indentlevel--; + } + + + if (indentlevel==1 && (strchr(";{}", tok->aaaa0()) || (tok->aaaa0()!=':' && strchr(tok->aaaa(), ':')))) + { + const TOKEN *next = tok->next(); + + const char *varname = 0; + + // Is it a variable declaration? + if ( TOKEN::Match(next,"%type% %var% ;") ) + { + const char *types[] = {"bool", "char", "int", "short", "long", "float", "double", 0}; + for ( int type = 0; types[type]; type++ ) + { + if ( strcmp(next->aaaa(), types[type]) == 0) + { + varname = next->next()->aaaa(); + break; + } + } + } + + // Pointer? + else if ( TOKEN::Match(next, "%type% * %var% ;") ) + { + varname = next->strAt(2); + } + + if (varname) + { + struct VAR *var = new VAR; + memset(var, 0, sizeof(struct VAR)); + var->name = varname; + var->init = false; + var->next = varlist; + varlist = var; + } + } + } + + return varlist; +} +//--------------------------------------------------------------------------- + +const TOKEN * CheckClass::FindClassFunction( const TOKEN *tok, const char classname[], const char funcname[], int &indentlevel ) +{ + if ( indentlevel < 0 || tok == NULL ) + return NULL; + + std::ostringstream classPattern; + classPattern << "class " << classname << " :|{"; + + std::ostringstream internalPattern; + internalPattern << funcname << " ("; + + std::ostringstream externalPattern; + externalPattern << classname << " :: " << funcname << " ("; + + for ( ;tok; tok = tok->next() ) + { + if ( indentlevel == 0 && TOKEN::Match(tok, classPattern.str().c_str()) ) + { + while ( tok && tok->str() != "{" ) + tok = tok->next(); + if ( tok ) + tok = tok->next(); + if ( ! tok ) + break; + indentlevel = 1; + } + + if ( tok->str() == "{" ) + { + // If indentlevel==0 don't go to indentlevel 1. Skip the block. + if ( indentlevel > 0 ) + ++indentlevel; + + else + { + for ( ; tok; tok = tok->next() ) + { + if ( tok->str() == "{" ) + ++indentlevel; + else if ( tok->str() == "}" ) + { + --indentlevel; + if ( indentlevel <= 0 ) + break; + } + } + if ( tok == NULL ) + return NULL; + + continue; + } + } + + if ( tok->str() == "}" ) + { + indentlevel--; + if ( indentlevel < 0 ) + return NULL; + } + + if ( indentlevel == 1 ) + { + // Member function implemented in the class declaration? + if (!TOKEN::Match(tok,"~") && TOKEN::Match(tok->next(), internalPattern.str().c_str())) + { + const TOKEN *tok2 = tok; + while ( tok2 && tok2->str() != "{" && tok2->str() != ";" ) + tok2 = tok2->next(); + if ( tok2 && tok2->str() == "{" ) + return tok->next(); + } + } + + else if ( indentlevel == 0 && TOKEN::Match(tok, externalPattern.str().c_str()) ) + { + return tok; + } + } + + // Not found + return NULL; +} +//--------------------------------------------------------------------------- + +void CheckClass::InitVar(struct VAR *varlist, const char varname[]) +{ + for (struct VAR *var = varlist; var; var = var->next) + { + if ( strcmp(var->name, varname) == 0 ) + { + var->init = true; + break; + } + } +} +//--------------------------------------------------------------------------- + +void CheckClass::ClassChecking_VarList_Initialize(const TOKEN *tok1, const TOKEN *ftok, struct VAR *varlist, const char classname[], std::list &callstack) +{ + bool Assign = false; + unsigned int indentlevel = 0; + + for (; ftok; ftok = ftok->next()) + { + if (!ftok->next()) + break; + + // Class constructor.. initializing variables like this + // clKalle::clKalle() : var(value) { } + if (indentlevel==0) + { + if (Assign && TOKEN::Match(ftok, "%var% (")) + { + InitVar( varlist, ftok->aaaa() ); + } + + Assign |= (ftok->aaaa0() == ':'); + } + + + if (ftok->aaaa0() == '{') + { + indentlevel++; + Assign = false; + } + + if (ftok->aaaa0() == '}') + { + if (indentlevel <= 1) + break; + indentlevel--; + } + + if ( indentlevel < 1 ) + continue; + + // Before a new statement there is "[{};)=]" or "else" + if ( ! TOKEN::Match(ftok, "[{};)=]") && ! TOKEN::Match(ftok, "else") ) + continue; + + // Using the operator= function to initialize all variables.. + if ( TOKEN::Match(ftok->next(), "* this = ") ) + { + for (struct VAR *var = varlist; var; var = var->next) + var->init = true; + break; + } + + if (!TOKEN::Match(ftok->next(), "%var%") && !TOKEN::Match(ftok->next(), "this . %var%")) + continue; + + // Goto the first token in this statement.. + ftok = ftok->next(); + + // Skip "this->" + if ( TOKEN::Match(ftok, "this .") ) + ftok = ftok->tokAt(2); + + // Clearing all variables.. + if (TOKEN::Match(ftok,"memset ( this ,")) + { + for (struct VAR *var = varlist; var; var = var->next) + var->init = true; + break; + } + + // Calling member function? + else if (TOKEN::Match(ftok, "%var% (")) + { + // No recursive calls! + if ( std::find(callstack.begin(),callstack.end(),ftok->aaaa()) == callstack.end() ) + { + callstack.push_back( ftok->aaaa() ); + int i = 0; + const TOKEN *ftok2 = FindClassFunction( tok1, classname, ftok->aaaa(), i ); + ClassChecking_VarList_Initialize(tok1, ftok2, varlist, classname, callstack); + } + } + + // Assignment of member variable? + else if (TOKEN::Match(ftok, "%var% =")) + { + InitVar( varlist, ftok->aaaa() ); + } + + // The functions 'clear' and 'Clear' are supposed to initialize variable. + if (TOKEN::Match(ftok,"%var% . clear (") || TOKEN::Match(ftok,"%var% . Clear (")) + { + InitVar( varlist, ftok->aaaa() ); + } + } +} + + + + + + +//--------------------------------------------------------------------------- +// ClassCheck: Check that all class constructors are ok. +//--------------------------------------------------------------------------- + +void CheckClass::CheckConstructors() +{ + const char pattern_class[] = "class %var% {"; + + // Locate class + const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), pattern_class ); + while (tok1) + { + const char *className[2]; + className[0] = tok1->strAt( 1 ); + className[1] = 0; + + // TODO: handling of private constructors should be improved. + bool hasPrivateConstructor = false; + { + int indentlevel = 0; + bool isPrivate = true; + for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) + { + // Indentation + if ( tok->str() == "{" ) + ++indentlevel; + + else if ( tok->str() == "}" ) + { + --indentlevel; + if (indentlevel <= 0) + break; + } + + // Parse class contents (indentlevel == 1).. + if ( indentlevel == 1 ) + { + // What section are we in.. private/non-private + if ( tok->str() == "private:" ) + isPrivate = true; + else if ( tok->str() == "protected:" || tok->str() == "public:" ) + isPrivate = false; + + // Is there a private constructor? + else if ( isPrivate && TOKEN::Match(tok, "%var1% (", className) ) + { + hasPrivateConstructor = true; + break; + } + } + } + } + + if ( hasPrivateConstructor ) + { + // TODO: Handle private constructors. + // Right now to avoid false positives I just bail out + tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); + continue; + } + + // Are there a class constructor? + const TOKEN *constructor_token = TOKEN::findmatch( tok1, "%any% %var1% (", className ); + while ( TOKEN::Match( constructor_token, "~" ) ) + constructor_token = TOKEN::findmatch( constructor_token->next(), "%any% %var1% (", className ); + + // There are no constructor. + if ( ! constructor_token ) + { + // If "--style" has been given, give a warning + if ( _settings._checkCodingStyle ) + { + // If the class has member variables there should be an constructor + struct VAR *varlist = ClassChecking_GetVarList(tok1); + if ( varlist ) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok1); + ostr << " The class '" << className[0] << "' has no constructor"; + _errorLogger->reportErr(ostr.str()); + } + // Delete the varlist.. + while (varlist) + { + struct VAR *nextvar = varlist->next; + delete varlist; + varlist = nextvar; + } + } + + tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); + continue; + } + + // Check that all member variables are initialized.. + struct VAR *varlist = ClassChecking_GetVarList(tok1); + + // Check constructors + CheckConstructors( tok1, varlist, className[0] ); + + // Check assignment operators + CheckConstructors( tok1, varlist, "operator =" ); + + // Delete the varlist.. + while (varlist) + { + struct VAR *nextvar = varlist->next; + delete varlist; + varlist = nextvar; + } + + tok1 = TOKEN::findmatch( tok1->next(), pattern_class ); + } +} + +void CheckClass::CheckConstructors(const TOKEN *tok1, struct VAR *varlist, const char funcname[]) +{ + const char * const className = tok1->strAt(1); + + int indentlevel = 0; + const TOKEN *constructor_token = FindClassFunction( tok1, className, funcname, indentlevel ); + std::list callstack; + ClassChecking_VarList_Initialize(tok1, constructor_token, varlist, className, callstack); + while ( constructor_token ) + { + // Check if any variables are uninitialized + for (struct VAR *var = varlist; var; var = var->next) + { + if ( var->init ) + continue; + + // Is it a static member variable? + std::ostringstream pattern; + pattern << className << "::" << var->name << "="; + if (TOKEN::findmatch(_tokenizer->tokens(), pattern.str().c_str())) + continue; + + // It's non-static and it's not initialized => error + std::ostringstream ostr; + ostr << _tokenizer->fileLine(constructor_token); + ostr << " Uninitialized member variable '" << className << "::" << var->name << "'"; + _errorLogger->reportErr(ostr.str()); + } + + for ( struct VAR *var = varlist; var; var = var->next ) + var->init = false; + + constructor_token = FindClassFunction( constructor_token->next(), className, funcname, indentlevel ); + callstack.clear(); + ClassChecking_VarList_Initialize(tok1, constructor_token, varlist, className, callstack); + } +} + + +//--------------------------------------------------------------------------- +// ClassCheck: Unused private functions +//--------------------------------------------------------------------------- + +void CheckClass::CheckUnusedPrivateFunctions() +{ + // Locate some class + const char *pattern_class[] = {"class","","{",NULL}; + for (const TOKEN *tok1 = TOKEN::findtoken(_tokenizer->tokens(), pattern_class); tok1; tok1 = TOKEN::findtoken(tok1->next(), pattern_class)) + { + const char *classname = tok1->next()->aaaa(); + + // The class implementation must be available.. + const char *pattern_classconstructor[] = {"","::","",NULL}; + pattern_classconstructor[0] = classname; + pattern_classconstructor[2] = classname; + if (!TOKEN::findtoken(_tokenizer->tokens(),pattern_classconstructor)) + continue; + + // Get private functions.. + std::list FuncList; + FuncList.clear(); + bool priv = false; + unsigned int indent_level = 0; + for (const TOKEN *tok = tok1; tok; tok = tok->next()) + { + if (TOKEN::Match(tok,"friend %var%")) + { + // Todo: Handle friend classes + FuncList.clear(); + break; + } + + if (tok->aaaa0() == '{') + indent_level++; + if (tok->aaaa0() == '}') + { + if (indent_level <= 1) + break; + indent_level--; + } + if (strcmp(tok->aaaa(),"};") == 0) + break; + if (strcmp(tok->aaaa(),"private:") == 0) + priv = true; + else if (strcmp(tok->aaaa(),"public:") == 0) + priv = false; + else if (strcmp(tok->aaaa(),"protected:") == 0) + priv = false; + else if (priv && indent_level == 1) + { + if ( TOKEN::Match(tok, "typedef %type% (") ) + tok = tok->tokAt(2); + + if (TOKEN::Match(tok, "%var% (") && + !TOKEN::Match(tok,classname)) + { + FuncList.push_back(tok->aaaa()); + } + } + } + + // Check that all private functions are used.. + const char *pattern_function[] = {"","::",NULL}; + pattern_function[0] = classname; + bool HasFuncImpl = false; + const TOKEN *ftok = _tokenizer->tokens(); + while (ftok) + { + ftok = TOKEN::findtoken(ftok,pattern_function); + int numpar = 0; + while (ftok && ftok->aaaa0()!=';' && ftok->aaaa0()!='{') + { + if (ftok->aaaa0() == '(') + numpar++; + else if (ftok->aaaa0() == ')') + numpar--; + ftok = ftok->next(); + } + + if (!ftok) + break; + + if (ftok->aaaa0() != ';' && numpar == 0) + { + HasFuncImpl = true; + + indent_level = 0; + while (ftok) + { + if (ftok->aaaa0() == '{') + indent_level++; + if (ftok->aaaa0() == '}') + { + if (indent_level<=1) + break; + indent_level--; + } + if (ftok->next() && ftok->next()->aaaa0() == '(') + FuncList.remove(ftok->aaaa()); + ftok = ftok->next(); + } + } + + if (ftok) + ftok = ftok->next(); + } + + while (HasFuncImpl && !FuncList.empty()) + { + bool fp = false; + + // Final check; check if the function pointer is used somewhere.. + const char *_pattern[] = {"=","",NULL}; + _pattern[1] = FuncList.front().c_str(); + fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); + _pattern[0] = "return"; + fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); + _pattern[0] = "("; + fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); + _pattern[0] = ")"; + fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); + _pattern[0] = ","; + fp |= (TOKEN::findtoken(_tokenizer->tokens(), _pattern) != NULL); + + if (!fp) + { + std::ostringstream ostr; + ostr << "Class '" << classname << "', unused private function: '" << FuncList.front() << "'"; + _errorLogger->reportErr(ostr.str()); + } + FuncList.pop_front(); + } + } +} + +//--------------------------------------------------------------------------- +// ClassCheck: Check that memset is not used on classes +//--------------------------------------------------------------------------- + +void CheckClass::CheckMemset() +{ + // Locate all 'memset' tokens.. + for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (!TOKEN::Match(tok,"memset") && !TOKEN::Match(tok,"memcpy") && !TOKEN::Match(tok,"memmove")) + continue; + + // Todo: Handle memcpy and memmove + const char *type = NULL; + if (TOKEN::Match(tok, "memset ( %var% , %num% , sizeof ( %type% ) )")) + type = tok->strAt(8); + else if (TOKEN::Match(tok, "memset ( & %var% , %num% , sizeof ( %type% ) )")) + type = tok->strAt(9); + else if (TOKEN::Match(tok, "memset ( %var% , %num% , sizeof ( struct %type% ) )")) + type = tok->strAt(9); + else if (TOKEN::Match(tok, "memset ( & %var% , %num% , sizeof ( struct %type% ) )")) + type = tok->strAt(10); + else if (TOKEN::Match(tok, "%type% ( %var% , %var% , sizeof ( %type% ) )")) + type = tok->strAt(8); + + // No type defined => The tokens didn't match + if (!(type && type[0])) + continue; + + // Warn if type is a class.. + const char *pattern1[] = {"class","",NULL}; + pattern1[1] = type; + if (TOKEN::findtoken(_tokenizer->tokens(),pattern1)) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Using '" << tok->aaaa() << "' on class."; + _errorLogger->reportErr(ostr.str()); + continue; + } + + // Warn if type is a struct that contains any std::* + const char *pattern2[] = {"struct","","{",NULL}; + pattern2[1] = type; + for (const TOKEN *tstruct = TOKEN::findtoken(_tokenizer->tokens(), pattern2); tstruct; tstruct = tstruct->next()) + { + if (tstruct->aaaa0() == '}') + break; + + if (TOKEN::Match(tstruct, "std :: %type% %var% ;")) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Using '" << tok->aaaa() << "' on struct that contains a 'std::" << tstruct->strAt(2) << "'"; + _errorLogger->reportErr(ostr.str()); + break; + } + } + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// ClassCheck: "void operator=(" +//--------------------------------------------------------------------------- + +void CheckClass::CheckOperatorEq1() +{ + if (const TOKEN *tok = TOKEN::findmatch(_tokenizer->tokens(), "void operator = (")) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": 'operator=' should return something"; + _errorLogger->reportErr(ostr.str()); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// A destructor in a base class should be virtual +//--------------------------------------------------------------------------- + +void CheckClass::virtualDestructor() +{ + const char pattern_classdecl[] = "class %var% : %var%"; + + const TOKEN *derived = _tokenizer->tokens(); + while ((derived = TOKEN::findmatch(derived, pattern_classdecl)) != NULL) { // Check that the derived class has a non empty destructor.. { @@ -709,61 +709,61 @@ void CheckClass::virtualDestructor() continue; } } - - // Iterate through each base class... - derived = derived->tokAt(3); - while ( TOKEN::Match(derived, "%var%") ) - { - bool isPublic = TOKEN::Match(derived, "public"); - - // What kind of inheritance is it.. public|protected|private - if ( TOKEN::Match( derived, "public|protected|private" ) ) - derived = derived->next(); - - // Name of base class.. - const char *baseName[2]; - baseName[0] = derived->strAt(0); - baseName[1] = 0; - - // Update derived so it's ready for the next loop. - derived = derived->next(); - if ( TOKEN::Match(derived, ",") ) - derived = derived->next(); - - // If not public inheritance, skip checking of this base class.. - if ( ! isPublic ) - continue; - - // Find the destructor declaration for the base class. - const TOKEN *base = TOKEN::findmatch(_tokenizer->tokens(), "%any% ~ %var1% (", baseName); - while (TOKEN::Match(base, "::")) - base = TOKEN::findmatch(base->next(), "%any% ~ %var1% (", baseName); - - while (TOKEN::Match(base, "%var%") && !TOKEN::Match(base, "virtual")) - base = base->previous(); - - // Check that there is a destructor.. - if ( ! base ) - { - // Is the class declaration available? - base = TOKEN::findmatch(_tokenizer->tokens(), "class %var1% :|{", baseName); - if ( base ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(base) << ": Base class " << baseName[0] << " doesn't have a virtual destructor"; - _errorLogger->reportErr(errmsg.str()); - } - } - - // There is a destructor. Check that it's virtual.. - else if ( ! TOKEN::Match(base, "virtual") ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(base) << ": The destructor for the base class " << baseName[0] << " is not virtual"; - _errorLogger->reportErr(errmsg.str()); - } - } - } -} -//--------------------------------------------------------------------------- - + + // Iterate through each base class... + derived = derived->tokAt(3); + while ( TOKEN::Match(derived, "%var%") ) + { + bool isPublic = TOKEN::Match(derived, "public"); + + // What kind of inheritance is it.. public|protected|private + if ( TOKEN::Match( derived, "public|protected|private" ) ) + derived = derived->next(); + + // Name of base class.. + const char *baseName[2]; + baseName[0] = derived->strAt(0); + baseName[1] = 0; + + // Update derived so it's ready for the next loop. + derived = derived->next(); + if ( TOKEN::Match(derived, ",") ) + derived = derived->next(); + + // If not public inheritance, skip checking of this base class.. + if ( ! isPublic ) + continue; + + // Find the destructor declaration for the base class. + const TOKEN *base = TOKEN::findmatch(_tokenizer->tokens(), "%any% ~ %var1% (", baseName); + while (TOKEN::Match(base, "::")) + base = TOKEN::findmatch(base->next(), "%any% ~ %var1% (", baseName); + + while (TOKEN::Match(base, "%var%") && !TOKEN::Match(base, "virtual")) + base = base->previous(); + + // Check that there is a destructor.. + if ( ! base ) + { + // Is the class declaration available? + base = TOKEN::findmatch(_tokenizer->tokens(), "class %var1% :|{", baseName); + if ( base ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(base) << ": Base class " << baseName[0] << " doesn't have a virtual destructor"; + _errorLogger->reportErr(errmsg.str()); + } + } + + // There is a destructor. Check that it's virtual.. + else if ( ! TOKEN::Match(base, "virtual") ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(base) << ": The destructor for the base class " << baseName[0] << " is not virtual"; + _errorLogger->reportErr(errmsg.str()); + } + } + } +} +//--------------------------------------------------------------------------- + diff --git a/checkclass.h b/checkclass.h index 7d3c36c42..b2ae215f4 100644 --- a/checkclass.h +++ b/checkclass.h @@ -1,67 +1,67 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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); - void InitVar(struct VAR *varlist, const char varname[]); - const TOKEN *FindClassFunction( const TOKEN *tok, const char classname[], const char funcname[], int &indentlevel ); - struct VAR *ClassChecking_GetVarList(const TOKEN *tok1); - - // Check constructors for a specified class - void CheckConstructors(const TOKEN *tok1, struct VAR *varlist, const char funcname[]); - - const Tokenizer *_tokenizer; - Settings _settings; - ErrorLogger *_errorLogger; -}; -//--------------------------------------------------------------------------- -#endif - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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); + void InitVar(struct VAR *varlist, const char varname[]); + const TOKEN *FindClassFunction( const TOKEN *tok, const char classname[], const char funcname[], int &indentlevel ); + struct VAR *ClassChecking_GetVarList(const TOKEN *tok1); + + // Check constructors for a specified class + void CheckConstructors(const TOKEN *tok1, struct VAR *varlist, const char funcname[]); + + const Tokenizer *_tokenizer; + Settings _settings; + ErrorLogger *_errorLogger; +}; +//--------------------------------------------------------------------------- +#endif + diff --git a/checkfunctionusage.cpp b/checkfunctionusage.cpp index 4fa1fed70..ea866fda6 100644 --- a/checkfunctionusage.cpp +++ b/checkfunctionusage.cpp @@ -1,167 +1,167 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// FUNCTION USAGE - Check for unused functions etc -//--------------------------------------------------------------------------- - -CheckFunctionUsage::CheckFunctionUsage( ErrorLogger *errorLogger ) -{ - _errorLogger = errorLogger; -} - -CheckFunctionUsage::~CheckFunctionUsage() -{ - -} - - -void CheckFunctionUsage::parseTokens( const Tokenizer &tokenizer ) -{ - // Function declarations.. - for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) - { - if ( tok->fileIndex() != 0 ) - continue; - - const TOKEN *funcname = 0; - - if ( TOKEN::Match( tok, "%type% %var% (" ) ) - funcname = tok->tokAt(1); - else if ( TOKEN::Match(tok, "%type% * %var% (") ) - funcname = tok->tokAt(2); - else if ( TOKEN::Match(tok, "%type% :: %var% (") && !TOKEN::Match(tok, tok->strAt(2)) ) - funcname = tok->tokAt(2); - - // Check that ") {" is found.. - for (const TOKEN *tok2 = funcname; tok2; tok2 = tok2->next()) - { - if ( TOKEN::Match(tok2, ")") ) - { - if ( ! TOKEN::Match(tok2, ") {") && ! TOKEN::Match(tok2, ") const {") ) - funcname = NULL; - break; - } - } - - if ( funcname ) - { - FunctionUsage &func = _functions[ funcname->str() ]; - - // No filename set yet.. - if (func.filename.empty()) - func.filename = tokenizer.getFiles()->at(0); - - // Multiple files => filename = "+" - else if (func.filename != tokenizer.getFiles()->at(0)) - { - func.filename = "+"; - func.usedOtherFile |= func.usedSameFile; - } - } - } - - // Function usage.. - for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) - { - const TOKEN *funcname = 0; - - if ( TOKEN::Match( tok, "[;{}.,()[=+-/&|!?:] %var% [(),;:}]" ) || - TOKEN::Match(tok, ":: %var% (") || - TOKEN::Match(tok, "|= %var% (") || - TOKEN::Match(tok, "&= %var% (") || - TOKEN::Match(tok, "&& %var% (") || - TOKEN::Match(tok, "|| %var% (") || - TOKEN::Match(tok, "else %var% (") || - TOKEN::Match(tok, "return %var% (") ) - funcname = tok->next(); - - // funcname ( => Assert that the end paranthesis isn't followed by { - if ( TOKEN::Match(funcname, "%var% (") ) - { - int parlevel = 0; - for ( const TOKEN *tok2 = funcname; tok2; tok2 = tok2->next() ) - { - if (tok2->str() == "(") - ++parlevel; - - else if (tok2->str() == ")") - { - --parlevel; - if (parlevel == 0 && (TOKEN::Match(tok2, ") {") || TOKEN::Match(tok2, ") const"))) - funcname = NULL; - if ( parlevel <= 0 ) - break; - } - } - } - - if ( funcname ) - { - FunctionUsage &func = _functions[ funcname->str() ]; - - if ( func.filename.empty() || func.filename == "+" ) - func.usedOtherFile = true; - - else - func.usedSameFile = true; - } - } -} - - - - -void CheckFunctionUsage::check() -{ - for ( std::map::const_iterator it = _functions.begin(); it != _functions.end(); ++it ) - { - const FunctionUsage &func = it->second; - if ( func.usedOtherFile || func.filename.empty() ) - continue; - if ( ! func.usedSameFile ) - { - std::ostringstream errmsg; - if ( func.filename != "+" ) - errmsg << "[" << func.filename << "] "; - errmsg << "The function '" << it->first << "' is never used."; - _errorLogger->reportErr( errmsg.str() ); - } - else if ( ! func.usedOtherFile ) - { - /* TODO - add error message "function is only used in it can be static" - std::ostringstream errmsg; - errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage."; - _errorLogger->reportErr( errmsg.str() ); - */ - } - } -} - - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// FUNCTION USAGE - Check for unused functions etc +//--------------------------------------------------------------------------- + +CheckFunctionUsage::CheckFunctionUsage( ErrorLogger *errorLogger ) +{ + _errorLogger = errorLogger; +} + +CheckFunctionUsage::~CheckFunctionUsage() +{ + +} + + +void CheckFunctionUsage::parseTokens( const Tokenizer &tokenizer ) +{ + // Function declarations.. + for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) + { + if ( tok->fileIndex() != 0 ) + continue; + + const TOKEN *funcname = 0; + + if ( TOKEN::Match( tok, "%type% %var% (" ) ) + funcname = tok->tokAt(1); + else if ( TOKEN::Match(tok, "%type% * %var% (") ) + funcname = tok->tokAt(2); + else if ( TOKEN::Match(tok, "%type% :: %var% (") && !TOKEN::Match(tok, tok->strAt(2)) ) + funcname = tok->tokAt(2); + + // Check that ") {" is found.. + for (const TOKEN *tok2 = funcname; tok2; tok2 = tok2->next()) + { + if ( TOKEN::Match(tok2, ")") ) + { + if ( ! TOKEN::Match(tok2, ") {") && ! TOKEN::Match(tok2, ") const {") ) + funcname = NULL; + break; + } + } + + if ( funcname ) + { + FunctionUsage &func = _functions[ funcname->str() ]; + + // No filename set yet.. + if (func.filename.empty()) + func.filename = tokenizer.getFiles()->at(0); + + // Multiple files => filename = "+" + else if (func.filename != tokenizer.getFiles()->at(0)) + { + func.filename = "+"; + func.usedOtherFile |= func.usedSameFile; + } + } + } + + // Function usage.. + for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) + { + const TOKEN *funcname = 0; + + if ( TOKEN::Match( tok, "[;{}.,()[=+-/&|!?:] %var% [(),;:}]" ) || + TOKEN::Match(tok, ":: %var% (") || + TOKEN::Match(tok, "|= %var% (") || + TOKEN::Match(tok, "&= %var% (") || + TOKEN::Match(tok, "&& %var% (") || + TOKEN::Match(tok, "|| %var% (") || + TOKEN::Match(tok, "else %var% (") || + TOKEN::Match(tok, "return %var% (") ) + funcname = tok->next(); + + // funcname ( => Assert that the end paranthesis isn't followed by { + if ( TOKEN::Match(funcname, "%var% (") ) + { + int parlevel = 0; + for ( const TOKEN *tok2 = funcname; tok2; tok2 = tok2->next() ) + { + if (tok2->str() == "(") + ++parlevel; + + else if (tok2->str() == ")") + { + --parlevel; + if (parlevel == 0 && (TOKEN::Match(tok2, ") {") || TOKEN::Match(tok2, ") const"))) + funcname = NULL; + if ( parlevel <= 0 ) + break; + } + } + } + + if ( funcname ) + { + FunctionUsage &func = _functions[ funcname->str() ]; + + if ( func.filename.empty() || func.filename == "+" ) + func.usedOtherFile = true; + + else + func.usedSameFile = true; + } + } +} + + + + +void CheckFunctionUsage::check() +{ + for ( std::map::const_iterator it = _functions.begin(); it != _functions.end(); ++it ) + { + const FunctionUsage &func = it->second; + if ( func.usedOtherFile || func.filename.empty() ) + continue; + if ( ! func.usedSameFile ) + { + std::ostringstream errmsg; + if ( func.filename != "+" ) + errmsg << "[" << func.filename << "] "; + errmsg << "The function '" << it->first << "' is never used."; + _errorLogger->reportErr( errmsg.str() ); + } + else if ( ! func.usedOtherFile ) + { + /* TODO - add error message "function is only used in it can be static" + std::ostringstream errmsg; + errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage."; + _errorLogger->reportErr( errmsg.str() ); + */ + } + } +} + + + diff --git a/checkfunctionusage.h b/checkfunctionusage.h index 34976f70d..49b49cbc7 100644 --- a/checkfunctionusage.h +++ b/checkfunctionusage.h @@ -1,66 +1,66 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 _functions; -}; - -//--------------------------------------------------------------------------- -#endif - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 _functions; +}; + +//--------------------------------------------------------------------------- +#endif + diff --git a/checkheaders.cpp b/checkheaders.cpp index ebb15255a..129df2255 100644 --- a/checkheaders.cpp +++ b/checkheaders.cpp @@ -1,256 +1,256 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// HEADERS - No implementation in a header -//--------------------------------------------------------------------------- - -CheckHeaders::CheckHeaders( const Tokenizer *tokenizer, ErrorLogger *errorLogger ) -{ - _tokenizer = tokenizer; - _errorLogger = errorLogger; -} - -CheckHeaders::~CheckHeaders() -{ - -} - -void CheckHeaders::WarningHeaderWithImplementation() -{ - for ( const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - // Only interested in included file - if (tok->fileIndex() == 0) - continue; - - if (TOKEN::Match(tok, ") {")) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Found implementation in header"; - _errorLogger->reportErr(ostr.str()); - - // Goto next file.. - unsigned int fileindex = tok->fileIndex(); - while ( tok->next() && tok->fileIndex() == fileindex ) - tok = tok->next(); - } - } -} -//--------------------------------------------------------------------------- - - - - - - - - -//--------------------------------------------------------------------------- -// HEADERS - Unneeded include -//--------------------------------------------------------------------------- - -void CheckHeaders::WarningIncludeHeader() -{ - // Including.. - for ( const TOKEN *includetok = _tokenizer->tokens(); includetok; includetok = includetok->next()) - { - if (includetok->str() != "#include") - continue; - - // Get fileindex of included file.. - unsigned int hfile = 0; - const char *includefile = includetok->next()->aaaa(); - while (hfile < _tokenizer->getFiles()->size()) - { - if ( Tokenizer::SameFileName( _tokenizer->getFiles()->at(hfile).c_str(), includefile ) ) - break; - hfile++; - } - if (hfile == _tokenizer->getFiles()->size()) - continue; - - // This header is needed if: - // * It contains some needed class declaration - // * It contains some needed function declaration - // * It contains some needed constant value - // * It contains some needed variable - // * It contains some needed enum - - std::list classlist; - std::list namelist; - - // Extract classes and names in the header.. - int indentlevel = 0; - for ( const TOKEN *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next() ) - { - if ( tok1->fileIndex() != hfile ) - continue; - - // I'm only interested in stuff that is declared at indentlevel 0 - if (tok1->str() == "{") - ++indentlevel; - - else if (tok1->str() == "}") - --indentlevel; - - if (indentlevel != 0) - continue; - - // Class or namespace declaration.. - // -------------------------------------- - if (TOKEN::Match(tok1,"class %var% {") || TOKEN::Match(tok1,"class %var% :") || TOKEN::Match(tok1,"namespace %var% {")) - classlist.push_back(tok1->strAt(1)); - - // Variable declaration.. - // -------------------------------------- - else if (TOKEN::Match(tok1, "%type% %var% ;") || TOKEN::Match(tok1, "%type% %var% [")) - namelist.push_back(tok1->strAt(1)); - - else if (TOKEN::Match(tok1, "%type% * %var% ;") || TOKEN::Match(tok1, "%type% * %var% [")) - namelist.push_back(tok1->strAt(2)); - - else if (TOKEN::Match(tok1, "const %type% %var% =") || TOKEN::Match(tok1, "const %type% %var% [")) - namelist.push_back(tok1->strAt(2)); - - else if (TOKEN::Match(tok1, "const %type% * %var% =") || TOKEN::Match(tok1, "const %type% * %var% [")) - namelist.push_back(tok1->strAt(3)); - - // enum.. - // -------------------------------------- - else if (tok1->str() == "enum") - { - tok1 = tok1->next(); - while ( ! TOKEN::Match( tok1, "; %any%" ) ) - { - if ( tok1->isName() ) - namelist.push_back(tok1->str()); - tok1 = tok1->next(); - } - } - - // function.. - // -------------------------------------- - else if (TOKEN::Match(tok1,"%type% %var% (")) - namelist.push_back(tok1->strAt(1)); - - else if (TOKEN::Match(tok1,"%type% * %var% (")) - namelist.push_back(tok1->strAt(2)); - - else if (TOKEN::Match(tok1,"const %type% %var% (")) - namelist.push_back(tok1->strAt(2)); - - else if (TOKEN::Match(tok1,"const %type% * %var% (")) - namelist.push_back(tok1->strAt(3)); - - // typedef.. - // -------------------------------------- - else if (tok1->str() == "typedef") - { - if (strcmp(tok1->strAt(1),"enum")==0) - continue; - int parlevel = 0; - while (tok1->next()) - { - if ( TOKEN::Match(tok1, "[({]") ) - parlevel++; - - else if ( TOKEN::Match(tok1, "[)}]") ) - parlevel--; - - else if (parlevel == 0) - { - if ( tok1->str() == ";" ) - break; - - if ( TOKEN::Match(tok1, "%var% ;") ) - namelist.push_back(tok1->str()); - } - - tok1 = tok1->next(); - } - } - } - - - // Check if the extracted names are used... - bool Needed = false; - bool NeedDeclaration = false; - for ( const TOKEN *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next()) - { - if (tok1->fileIndex() != includetok->fileIndex()) - continue; - - if ( TOKEN::Match(tok1, ": %var% {") || TOKEN::Match(tok1, ": %type% %var% {") ) - { - std::string classname = tok1->strAt((strcmp(tok1->strAt(2),"{")) ? 2 : 1); - if (std::find(classlist.begin(),classlist.end(),classname)!=classlist.end()) - { - Needed = true; - break; - } - } - - if ( ! tok1->isName() ) - continue; - - if (std::find(namelist.begin(),namelist.end(),tok1->aaaa() ) != namelist.end()) - { - Needed = true; - break; - } - - if ( ! NeedDeclaration ) - NeedDeclaration = (std::find(classlist.begin(),classlist.end(),tok1->aaaa() ) != classlist.end()); - } - - - // Not a header file? - if (includetok->fileIndex() == 0) - Needed |= NeedDeclaration; - - // Not needed! - if (!Needed) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(includetok) << ": The included header '" << includefile << "' is not needed"; - if (NeedDeclaration) - ostr << " (but a forward declaration is needed)"; - _errorLogger->reportErr(ostr.str()); - } - } -} -//--------------------------------------------------------------------------- - - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// HEADERS - No implementation in a header +//--------------------------------------------------------------------------- + +CheckHeaders::CheckHeaders( const Tokenizer *tokenizer, ErrorLogger *errorLogger ) +{ + _tokenizer = tokenizer; + _errorLogger = errorLogger; +} + +CheckHeaders::~CheckHeaders() +{ + +} + +void CheckHeaders::WarningHeaderWithImplementation() +{ + for ( const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + // Only interested in included file + if (tok->fileIndex() == 0) + continue; + + if (TOKEN::Match(tok, ") {")) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Found implementation in header"; + _errorLogger->reportErr(ostr.str()); + + // Goto next file.. + unsigned int fileindex = tok->fileIndex(); + while ( tok->next() && tok->fileIndex() == fileindex ) + tok = tok->next(); + } + } +} +//--------------------------------------------------------------------------- + + + + + + + + +//--------------------------------------------------------------------------- +// HEADERS - Unneeded include +//--------------------------------------------------------------------------- + +void CheckHeaders::WarningIncludeHeader() +{ + // Including.. + for ( const TOKEN *includetok = _tokenizer->tokens(); includetok; includetok = includetok->next()) + { + if (includetok->str() != "#include") + continue; + + // Get fileindex of included file.. + unsigned int hfile = 0; + const char *includefile = includetok->next()->aaaa(); + while (hfile < _tokenizer->getFiles()->size()) + { + if ( Tokenizer::SameFileName( _tokenizer->getFiles()->at(hfile).c_str(), includefile ) ) + break; + hfile++; + } + if (hfile == _tokenizer->getFiles()->size()) + continue; + + // This header is needed if: + // * It contains some needed class declaration + // * It contains some needed function declaration + // * It contains some needed constant value + // * It contains some needed variable + // * It contains some needed enum + + std::list classlist; + std::list namelist; + + // Extract classes and names in the header.. + int indentlevel = 0; + for ( const TOKEN *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next() ) + { + if ( tok1->fileIndex() != hfile ) + continue; + + // I'm only interested in stuff that is declared at indentlevel 0 + if (tok1->str() == "{") + ++indentlevel; + + else if (tok1->str() == "}") + --indentlevel; + + if (indentlevel != 0) + continue; + + // Class or namespace declaration.. + // -------------------------------------- + if (TOKEN::Match(tok1,"class %var% {") || TOKEN::Match(tok1,"class %var% :") || TOKEN::Match(tok1,"namespace %var% {")) + classlist.push_back(tok1->strAt(1)); + + // Variable declaration.. + // -------------------------------------- + else if (TOKEN::Match(tok1, "%type% %var% ;") || TOKEN::Match(tok1, "%type% %var% [")) + namelist.push_back(tok1->strAt(1)); + + else if (TOKEN::Match(tok1, "%type% * %var% ;") || TOKEN::Match(tok1, "%type% * %var% [")) + namelist.push_back(tok1->strAt(2)); + + else if (TOKEN::Match(tok1, "const %type% %var% =") || TOKEN::Match(tok1, "const %type% %var% [")) + namelist.push_back(tok1->strAt(2)); + + else if (TOKEN::Match(tok1, "const %type% * %var% =") || TOKEN::Match(tok1, "const %type% * %var% [")) + namelist.push_back(tok1->strAt(3)); + + // enum.. + // -------------------------------------- + else if (tok1->str() == "enum") + { + tok1 = tok1->next(); + while ( ! TOKEN::Match( tok1, "; %any%" ) ) + { + if ( tok1->isName() ) + namelist.push_back(tok1->str()); + tok1 = tok1->next(); + } + } + + // function.. + // -------------------------------------- + else if (TOKEN::Match(tok1,"%type% %var% (")) + namelist.push_back(tok1->strAt(1)); + + else if (TOKEN::Match(tok1,"%type% * %var% (")) + namelist.push_back(tok1->strAt(2)); + + else if (TOKEN::Match(tok1,"const %type% %var% (")) + namelist.push_back(tok1->strAt(2)); + + else if (TOKEN::Match(tok1,"const %type% * %var% (")) + namelist.push_back(tok1->strAt(3)); + + // typedef.. + // -------------------------------------- + else if (tok1->str() == "typedef") + { + if (strcmp(tok1->strAt(1),"enum")==0) + continue; + int parlevel = 0; + while (tok1->next()) + { + if ( TOKEN::Match(tok1, "[({]") ) + parlevel++; + + else if ( TOKEN::Match(tok1, "[)}]") ) + parlevel--; + + else if (parlevel == 0) + { + if ( tok1->str() == ";" ) + break; + + if ( TOKEN::Match(tok1, "%var% ;") ) + namelist.push_back(tok1->str()); + } + + tok1 = tok1->next(); + } + } + } + + + // Check if the extracted names are used... + bool Needed = false; + bool NeedDeclaration = false; + for ( const TOKEN *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next()) + { + if (tok1->fileIndex() != includetok->fileIndex()) + continue; + + if ( TOKEN::Match(tok1, ": %var% {") || TOKEN::Match(tok1, ": %type% %var% {") ) + { + std::string classname = tok1->strAt((strcmp(tok1->strAt(2),"{")) ? 2 : 1); + if (std::find(classlist.begin(),classlist.end(),classname)!=classlist.end()) + { + Needed = true; + break; + } + } + + if ( ! tok1->isName() ) + continue; + + if (std::find(namelist.begin(),namelist.end(),tok1->aaaa() ) != namelist.end()) + { + Needed = true; + break; + } + + if ( ! NeedDeclaration ) + NeedDeclaration = (std::find(classlist.begin(),classlist.end(),tok1->aaaa() ) != classlist.end()); + } + + + // Not a header file? + if (includetok->fileIndex() == 0) + Needed |= NeedDeclaration; + + // Not needed! + if (!Needed) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(includetok) << ": The included header '" << includefile << "' is not needed"; + if (NeedDeclaration) + ostr << " (but a forward declaration is needed)"; + _errorLogger->reportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + + + diff --git a/checkheaders.h b/checkheaders.h index efd68e78c..2d951bef5 100644 --- a/checkheaders.h +++ b/checkheaders.h @@ -1,43 +1,43 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 // free - -#include - -#include -#include - -#ifdef __BORLANDC__ -#include // <- memset -#else -#include -#endif - -#ifndef _MSC_VER -#define _strdup(str) strdup(str) -#endif - -//--------------------------------------------------------------------------- - -CheckMemoryLeakClass::CheckMemoryLeakClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ) - : _settings(settings) -{ - _tokenizer = tokenizer; - _errorLogger = errorLogger; -} - -CheckMemoryLeakClass::~CheckMemoryLeakClass() -{ - -} - -bool CheckMemoryLeakClass::isclass( const TOKEN *tok ) -{ - if ( tok->isStandardType() ) - return false; - - std::ostringstream pattern; - pattern << "struct " << tok->str(); - if ( TOKEN::findmatch( _tokenizer->tokens(), pattern.str().c_str() ) ) - return false; - - return true; -} -//--------------------------------------------------------------------------- - -CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetAllocationType( const TOKEN *tok2 ) -{ - // What we may have... - // * var = (char *)malloc(10); - // * var = new char[10]; - // * var = strdup("hello"); - if ( tok2 && tok2->str() == "(" ) - { - while ( tok2 && tok2->str() != ")" ) - tok2 = tok2->next(); - tok2 = tok2 ? tok2->next() : NULL; - } - if ( ! tok2 ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 // free + +#include + +#include +#include + +#ifdef __BORLANDC__ +#include // <- memset +#else +#include +#endif + +#ifndef _MSC_VER +#define _strdup(str) strdup(str) +#endif + +//--------------------------------------------------------------------------- + +CheckMemoryLeakClass::CheckMemoryLeakClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ) + : _settings(settings) +{ + _tokenizer = tokenizer; + _errorLogger = errorLogger; +} + +CheckMemoryLeakClass::~CheckMemoryLeakClass() +{ + +} + +bool CheckMemoryLeakClass::isclass( const TOKEN *tok ) +{ + if ( tok->isStandardType() ) + return false; + + std::ostringstream pattern; + pattern << "struct " << tok->str(); + if ( TOKEN::findmatch( _tokenizer->tokens(), pattern.str().c_str() ) ) + return false; + + return true; +} +//--------------------------------------------------------------------------- + +CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetAllocationType( const TOKEN *tok2 ) +{ + // What we may have... + // * var = (char *)malloc(10); + // * var = new char[10]; + // * var = strdup("hello"); + if ( tok2 && tok2->str() == "(" ) + { + while ( tok2 && tok2->str() != ")" ) + tok2 = tok2->next(); + tok2 = tok2 ? tok2->next() : NULL; + } + if ( ! tok2 ) return No; if ( ! tok2->isName() ) - return No; - - // Does tok2 point on "malloc", "strdup" or "kmalloc".. - const char *mallocfunc[] = {"malloc", - "calloc", - "strdup", - "kmalloc", - "kzalloc", - "kcalloc", - 0}; - for ( unsigned int i = 0; mallocfunc[i]; i++ ) - { - if ( tok2->str() == mallocfunc[i] ) - return Malloc; - } - - // Does tok2 point on "g_malloc", "g_strdup", .. - const char *gmallocfunc[] = {"g_new", - "g_new0", - "g_try_new", - "g_try_new0", - "g_malloc", - "g_malloc0", - "g_try_malloc", - "g_try_malloc0", - "g_strdup", - "g_strndup", - 0}; - for ( unsigned int i = 0; gmallocfunc[i]; i++ ) - { - if ( tok2->str() == gmallocfunc[i] ) - return gMalloc; - } - - if ( TOKEN::Match( tok2, "new %type% [;(]" ) ) - return New; - - if ( TOKEN::Match( tok2, "new %type% [" ) ) - return NewA; - - if ( TOKEN::Match( tok2, "fopen (" ) ) - return FOPEN; - - if ( TOKEN::Match( tok2, "popen (" ) ) - return POPEN; - - // Userdefined allocation function.. - std::list::const_iterator it = _listAllocFunc.begin(); - while ( it != _listAllocFunc.end() ) - { - if ( tok2->str() == it->funcname ) - return it->alloctype; - ++it; - } - - return No; -} - - - -CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetReallocationType( const TOKEN *tok2 ) -{ - // What we may have... - // * var = (char *)realloc(..; - if ( tok2 && tok2->str() == "(" ) - { - while ( tok2 && tok2->str() != ")" ) - tok2 = tok2->next(); - tok2 = tok2 ? tok2->next() : NULL; - } - if ( ! tok2 ) - return No; - - if ( TOKEN::Match(tok2, "realloc") ) - return Malloc; - - // GTK memory reallocation.. - if ( TOKEN::Match(tok2, "g_realloc|g_try_realloc|g_renew|g_try_renew") ) - return gMalloc; - - return No; -} - - -CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetDeallocationType( const TOKEN *tok, const char *varnames[] ) -{ - if ( TOKEN::Match(tok, "delete %var1% ;", varnames) ) - return New; - - if ( TOKEN::Match(tok, "delete [ ] %var1% ;", varnames) ) - return NewA; - - if ( TOKEN::Match(tok, "free ( %var1% ) ;", varnames) || - TOKEN::Match(tok, "kfree ( %var1% ) ;", varnames) ) - { - return Malloc; - } - - if ( TOKEN::Match(tok, "g_free ( %var1% ) ;", varnames) ) - return gMalloc; - - if ( TOKEN::Match(tok, "fclose ( %var1% )", varnames) ) - return FOPEN; - - if ( TOKEN::Match(tok, "pclose ( %var1% )", varnames) ) - return POPEN; - - return No; -} -//-------------------------------------------------------------------------- - -const char * CheckMemoryLeakClass::call_func( const TOKEN *tok, std::list callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype ) -{ - // Keywords that are not function calls.. - if (TOKEN::Match(tok,"if|for|while")) - return 0; - - // String functions that are not allocating nor deallocating memory.. - if (TOKEN::Match(tok, "strcpy|strncpy|strcat|strncat|strcmp|strncmp|strcasecmp|stricmp|sprintf|strchr|strrchr|strstr")) - return 0; - - // Memory functions that are not allocating nor deallocating memory.. - if (TOKEN::Match(tok, "memset|memcpy|memmove|memchr")) - return 0; - - // I/O functions that are not allocating nor deallocating memory.. - if (TOKEN::Match(tok, "fgets|fgetc|fputs|fputc|printf")) - return 0; - - // Convert functions that are not allocating nor deallocating memory.. - if (TOKEN::Match(tok, "atoi|atof|atol|strtol|strtoul|strtod")) - return 0; - - if (GetAllocationType(tok)!=No || GetReallocationType(tok)!=No || GetDeallocationType(tok,varnames)!=No) - return 0; - - if ( callstack.size() > 2 ) - return "dealloc"; - - const char *funcname = tok->aaaa(); - for ( std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it ) - { - if ( (*it)->str() == funcname ) - return "dealloc"; - } - callstack.push_back(tok); - - int par = 1; - int parlevel = 0; - for ( ; tok; tok = tok->next() ) - { - if ( tok->str() == "(" ) - ++parlevel; - else if ( tok->str() == ")" ) - { - --parlevel; - if ( parlevel < 1 ) - return NULL; - } - - if ( parlevel == 1 ) - { - if ( tok->str() == "," ) - ++par; - if ( TOKEN::Match(tok, "[,()] %var1% [,()]", varnames) ) - { - const TOKEN *ftok = _tokenizer->GetFunctionTokenByName(funcname); - const char *parname = Tokenizer::getParameterName( ftok, par ); - if ( ! parname ) - return "use"; - // Check if the function deallocates the variable.. - while ( ftok && (ftok->str() != "{") ) - ftok = ftok->next(); - TOKEN *func = getcode( ftok->tokAt(1), callstack, parname, alloctype, dealloctype ); - simplifycode( func ); - const TOKEN *func_ = func; - while ( func_ && func_->str() == ";" ) - func_ = func_->next(); - /* - for (const TOKEN *t = func; t; t = t->next()) - { - std::cout << t->str() << "\n"; - }*/ - - const char *ret = 0; - if (TOKEN::findmatch(func_, "goto")) - { - // TODO : "goto" isn't handled well - if ( TOKEN::findmatch(func_, "dealloc") ) - ret = "dealloc"; - else if ( TOKEN::findmatch(func_, "use") ) - ret = "use"; - } - else if ( TOKEN::findmatch(func_, "dealloc") ) - ret = "dealloc"; - else if ( TOKEN::findmatch(func_, "use") ) - ret = "use"; - - Tokenizer::deleteTokens(func); - return ret; - } - } - } - return NULL; -} - -//-------------------------------------------------------------------------- - -void CheckMemoryLeakClass::MismatchError( const TOKEN *Tok1, const std::list &callstack, const char varname[] ) -{ - std::ostringstream errmsg; - for ( std::list::const_iterator tok = callstack.begin(); tok != callstack.end(); ++tok ) - errmsg << _tokenizer->fileLine(*tok) << " -> "; - errmsg << _tokenizer->fileLine(Tok1) << ": Mismatching allocation and deallocation: " << varname; - _errorLogger->reportErr( errmsg.str() ); -} -//--------------------------------------------------------------------------- - -void CheckMemoryLeakClass::MemoryLeak( const TOKEN *tok, const char varname[], AllocType alloctype ) -{ - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok); - - if( alloctype == CheckMemoryLeakClass::FOPEN || - alloctype == CheckMemoryLeakClass::POPEN ) - errmsg << ": Resource leak: "; - else - errmsg << ": Memory leak: "; - - errmsg << varname; - _errorLogger->reportErr( errmsg.str() ); -} -//--------------------------------------------------------------------------- - -void CheckMemoryLeakClass::instoken(TOKEN *tok, const char str[]) -{ - tok->insertToken( str ); -} -//--------------------------------------------------------------------------- - -bool CheckMemoryLeakClass::notvar(const TOKEN *tok, const char *varnames[]) -{ - return bool( TOKEN::Match(tok, "! %var1% [;)&|]", varnames) || - TOKEN::Match(tok, "! ( %var1% )", varnames) || - TOKEN::Match(tok, "unlikely ( ! %var1% )", varnames) || - TOKEN::Match(tok, "unlikely ( %var1% == 0 )", varnames) || - TOKEN::Match(tok, "0 == %var1% [;)&|]", varnames) || - TOKEN::Match(tok, "%var1% == 0", varnames) ); -} - -TOKEN *CheckMemoryLeakClass::getcode(const TOKEN *tok, std::list callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype) -{ - const char *varnames[2]; - varnames[0] = varname; - varnames[1] = 0; - - TOKEN *rethead = 0, *rettail = 0; - #define addtoken(_str) \ - { \ - if (rettail) \ - { \ - rettail->insertToken(_str); \ - rettail = rettail->next(); \ - } \ - else \ - { \ - rethead = new TOKEN; \ - rettail = rethead; \ - rettail->setstr(_str); \ - } \ - \ - rettail->linenr( tok->linenr() ); \ - rettail->fileIndex( tok->fileIndex() ); \ - } - - // The first token should be ";" - addtoken(";"); - - bool isloop = false; - - int indentlevel = 0; - int parlevel = 0; - for ( ; tok; tok = tok->next() ) - { - if ( tok->str() == "{" ) - { - addtoken( "{" ); - indentlevel++; - } - else if ( tok->str() == "}" ) - { - addtoken( "}" ); - if ( indentlevel <= 0 ) - break; - indentlevel--; - } - - if ( tok->str() == "(" ) - parlevel++; - else if ( tok->str() == ")" ) - parlevel--; - isloop &= ( parlevel > 0 ); - - if ( parlevel == 0 && tok->str()==";") - addtoken(";"); - - if (TOKEN::Match(tok, "[(;{}] %var1% =", varnames)) - { - AllocType alloc = GetAllocationType(tok->tokAt(3)); - - if ( alloc == No ) - { - alloc = GetReallocationType( tok->tokAt(3) ); - if ( alloc != No ) - { - addtoken( "dealloc" ); - addtoken( ";" ); - } - } - - // If "--all" hasn't been given, don't check classes.. - if ( alloc == New && ! _settings._showAll ) - { - if ( TOKEN::Match(tok->tokAt(3), "new %type% [(;]") ) - { - if ( isclass( tok->tokAt(4) ) ) - alloc = No; - } - } - - if ( alloc != No ) - { - addtoken("alloc"); - if (alloctype!=No && alloctype!=alloc) - MismatchError(tok, callstack, varname); - if (dealloctype!=No && dealloctype!=alloc) - MismatchError(tok, callstack, varname); - alloctype = alloc; - } - - // assignment.. - else - { - // is the pointer in rhs? - bool rhs = false; - for ( const TOKEN *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next() ) - { - if ( tok2->str() == ";" ) - break; - - if ( tok2->str() == varname ) - { - rhs = true; - break; - } - } - - addtoken( (rhs ? "use" : "assign") ); - } - } + return No; + + // Does tok2 point on "malloc", "strdup" or "kmalloc".. + const char *mallocfunc[] = {"malloc", + "calloc", + "strdup", + "kmalloc", + "kzalloc", + "kcalloc", + 0}; + for ( unsigned int i = 0; mallocfunc[i]; i++ ) + { + if ( tok2->str() == mallocfunc[i] ) + return Malloc; + } + + // Does tok2 point on "g_malloc", "g_strdup", .. + const char *gmallocfunc[] = {"g_new", + "g_new0", + "g_try_new", + "g_try_new0", + "g_malloc", + "g_malloc0", + "g_try_malloc", + "g_try_malloc0", + "g_strdup", + "g_strndup", + 0}; + for ( unsigned int i = 0; gmallocfunc[i]; i++ ) + { + if ( tok2->str() == gmallocfunc[i] ) + return gMalloc; + } + + if ( TOKEN::Match( tok2, "new %type% [;(]" ) ) + return New; + + if ( TOKEN::Match( tok2, "new %type% [" ) ) + return NewA; + + if ( TOKEN::Match( tok2, "fopen (" ) ) + return FOPEN; + + if ( TOKEN::Match( tok2, "popen (" ) ) + return POPEN; + + // Userdefined allocation function.. + std::list::const_iterator it = _listAllocFunc.begin(); + while ( it != _listAllocFunc.end() ) + { + if ( tok2->str() == it->funcname ) + return it->alloctype; + ++it; + } + + return No; +} + + + +CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetReallocationType( const TOKEN *tok2 ) +{ + // What we may have... + // * var = (char *)realloc(..; + if ( tok2 && tok2->str() == "(" ) + { + while ( tok2 && tok2->str() != ")" ) + tok2 = tok2->next(); + tok2 = tok2 ? tok2->next() : NULL; + } + if ( ! tok2 ) + return No; + + if ( TOKEN::Match(tok2, "realloc") ) + return Malloc; + + // GTK memory reallocation.. + if ( TOKEN::Match(tok2, "g_realloc|g_try_realloc|g_renew|g_try_renew") ) + return gMalloc; + + return No; +} + + +CheckMemoryLeakClass::AllocType CheckMemoryLeakClass::GetDeallocationType( const TOKEN *tok, const char *varnames[] ) +{ + if ( TOKEN::Match(tok, "delete %var1% ;", varnames) ) + return New; + + if ( TOKEN::Match(tok, "delete [ ] %var1% ;", varnames) ) + return NewA; + + if ( TOKEN::Match(tok, "free ( %var1% ) ;", varnames) || + TOKEN::Match(tok, "kfree ( %var1% ) ;", varnames) ) + { + return Malloc; + } + + if ( TOKEN::Match(tok, "g_free ( %var1% ) ;", varnames) ) + return gMalloc; + + if ( TOKEN::Match(tok, "fclose ( %var1% )", varnames) ) + return FOPEN; + + if ( TOKEN::Match(tok, "pclose ( %var1% )", varnames) ) + return POPEN; + + return No; +} +//-------------------------------------------------------------------------- + +const char * CheckMemoryLeakClass::call_func( const TOKEN *tok, std::list callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype ) +{ + // Keywords that are not function calls.. + if (TOKEN::Match(tok,"if|for|while")) + return 0; + + // String functions that are not allocating nor deallocating memory.. + if (TOKEN::Match(tok, "strcpy|strncpy|strcat|strncat|strcmp|strncmp|strcasecmp|stricmp|sprintf|strchr|strrchr|strstr")) + return 0; + + // Memory functions that are not allocating nor deallocating memory.. + if (TOKEN::Match(tok, "memset|memcpy|memmove|memchr")) + return 0; + + // I/O functions that are not allocating nor deallocating memory.. + if (TOKEN::Match(tok, "fgets|fgetc|fputs|fputc|printf")) + return 0; + + // Convert functions that are not allocating nor deallocating memory.. + if (TOKEN::Match(tok, "atoi|atof|atol|strtol|strtoul|strtod")) + return 0; + + if (GetAllocationType(tok)!=No || GetReallocationType(tok)!=No || GetDeallocationType(tok,varnames)!=No) + return 0; + + if ( callstack.size() > 2 ) + return "dealloc"; + + const char *funcname = tok->aaaa(); + for ( std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it ) + { + if ( (*it)->str() == funcname ) + return "dealloc"; + } + callstack.push_back(tok); + + int par = 1; + int parlevel = 0; + for ( ; tok; tok = tok->next() ) + { + if ( tok->str() == "(" ) + ++parlevel; + else if ( tok->str() == ")" ) + { + --parlevel; + if ( parlevel < 1 ) + return NULL; + } + + if ( parlevel == 1 ) + { + if ( tok->str() == "," ) + ++par; + if ( TOKEN::Match(tok, "[,()] %var1% [,()]", varnames) ) + { + const TOKEN *ftok = _tokenizer->GetFunctionTokenByName(funcname); + const char *parname = Tokenizer::getParameterName( ftok, par ); + if ( ! parname ) + return "use"; + // Check if the function deallocates the variable.. + while ( ftok && (ftok->str() != "{") ) + ftok = ftok->next(); + TOKEN *func = getcode( ftok->tokAt(1), callstack, parname, alloctype, dealloctype ); + simplifycode( func ); + const TOKEN *func_ = func; + while ( func_ && func_->str() == ";" ) + func_ = func_->next(); + /* + for (const TOKEN *t = func; t; t = t->next()) + { + std::cout << t->str() << "\n"; + }*/ + + const char *ret = 0; + if (TOKEN::findmatch(func_, "goto")) + { + // TODO : "goto" isn't handled well + if ( TOKEN::findmatch(func_, "dealloc") ) + ret = "dealloc"; + else if ( TOKEN::findmatch(func_, "use") ) + ret = "use"; + } + else if ( TOKEN::findmatch(func_, "dealloc") ) + ret = "dealloc"; + else if ( TOKEN::findmatch(func_, "use") ) + ret = "use"; + + Tokenizer::deleteTokens(func); + return ret; + } + } + } + return NULL; +} + +//-------------------------------------------------------------------------- + +void CheckMemoryLeakClass::MismatchError( const TOKEN *Tok1, const std::list &callstack, const char varname[] ) +{ + std::ostringstream errmsg; + for ( std::list::const_iterator tok = callstack.begin(); tok != callstack.end(); ++tok ) + errmsg << _tokenizer->fileLine(*tok) << " -> "; + errmsg << _tokenizer->fileLine(Tok1) << ": Mismatching allocation and deallocation: " << varname; + _errorLogger->reportErr( errmsg.str() ); +} +//--------------------------------------------------------------------------- + +void CheckMemoryLeakClass::MemoryLeak( const TOKEN *tok, const char varname[], AllocType alloctype ) +{ + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok); + + if( alloctype == CheckMemoryLeakClass::FOPEN || + alloctype == CheckMemoryLeakClass::POPEN ) + errmsg << ": Resource leak: "; + else + errmsg << ": Memory leak: "; + + errmsg << varname; + _errorLogger->reportErr( errmsg.str() ); +} +//--------------------------------------------------------------------------- + +void CheckMemoryLeakClass::instoken(TOKEN *tok, const char str[]) +{ + tok->insertToken( str ); +} +//--------------------------------------------------------------------------- + +bool CheckMemoryLeakClass::notvar(const TOKEN *tok, const char *varnames[]) +{ + return bool( TOKEN::Match(tok, "! %var1% [;)&|]", varnames) || + TOKEN::Match(tok, "! ( %var1% )", varnames) || + TOKEN::Match(tok, "unlikely ( ! %var1% )", varnames) || + TOKEN::Match(tok, "unlikely ( %var1% == 0 )", varnames) || + TOKEN::Match(tok, "0 == %var1% [;)&|]", varnames) || + TOKEN::Match(tok, "%var1% == 0", varnames) ); +} + +TOKEN *CheckMemoryLeakClass::getcode(const TOKEN *tok, std::list callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype) +{ + const char *varnames[2]; + varnames[0] = varname; + varnames[1] = 0; + + TOKEN *rethead = 0, *rettail = 0; + #define addtoken(_str) \ + { \ + if (rettail) \ + { \ + rettail->insertToken(_str); \ + rettail = rettail->next(); \ + } \ + else \ + { \ + rethead = new TOKEN; \ + rettail = rethead; \ + rettail->setstr(_str); \ + } \ + \ + rettail->linenr( tok->linenr() ); \ + rettail->fileIndex( tok->fileIndex() ); \ + } + + // The first token should be ";" + addtoken(";"); + + bool isloop = false; + + int indentlevel = 0; + int parlevel = 0; + for ( ; tok; tok = tok->next() ) + { + if ( tok->str() == "{" ) + { + addtoken( "{" ); + indentlevel++; + } + else if ( tok->str() == "}" ) + { + addtoken( "}" ); + if ( indentlevel <= 0 ) + break; + indentlevel--; + } + + if ( tok->str() == "(" ) + parlevel++; + else if ( tok->str() == ")" ) + parlevel--; + isloop &= ( parlevel > 0 ); + + if ( parlevel == 0 && tok->str()==";") + addtoken(";"); + + if (TOKEN::Match(tok, "[(;{}] %var1% =", varnames)) + { + AllocType alloc = GetAllocationType(tok->tokAt(3)); + + if ( alloc == No ) + { + alloc = GetReallocationType( tok->tokAt(3) ); + if ( alloc != No ) + { + addtoken( "dealloc" ); + addtoken( ";" ); + } + } + + // If "--all" hasn't been given, don't check classes.. + if ( alloc == New && ! _settings._showAll ) + { + if ( TOKEN::Match(tok->tokAt(3), "new %type% [(;]") ) + { + if ( isclass( tok->tokAt(4) ) ) + alloc = No; + } + } + + if ( alloc != No ) + { + addtoken("alloc"); + if (alloctype!=No && alloctype!=alloc) + MismatchError(tok, callstack, varname); + if (dealloctype!=No && dealloctype!=alloc) + MismatchError(tok, callstack, varname); + alloctype = alloc; + } + + // assignment.. + else + { + // is the pointer in rhs? + bool rhs = false; + for ( const TOKEN *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next() ) + { + if ( tok2->str() == ";" ) + break; + + if ( tok2->str() == varname ) + { + rhs = true; + break; + } + } + + addtoken( (rhs ? "use" : "assign") ); + } + } if ( TOKEN::Match(tok->previous(), "[;{})] %var%") ) - { - AllocType dealloc = GetDeallocationType(tok, varnames); - if ( dealloc != No ) - { - addtoken("dealloc"); - if (alloctype!=No && alloctype!=dealloc) - MismatchError(tok, callstack, varname); - if (dealloctype!=No && dealloctype!=dealloc) - MismatchError(tok, callstack, varname); + { + AllocType dealloc = GetDeallocationType(tok, varnames); + if ( dealloc != No ) + { + addtoken("dealloc"); + if (alloctype!=No && alloctype!=dealloc) + MismatchError(tok, callstack, varname); + if (dealloctype!=No && dealloctype!=dealloc) + MismatchError(tok, callstack, varname); dealloctype = dealloc; continue; - } - } - - // if else switch - if ( TOKEN::Match(tok, "if ( %var1% )", varnames) || - TOKEN::Match(tok, "if ( %var1% != 0 )", varnames) || - TOKEN::Match(tok, "if ( 0 != %var1% )", varnames) ) - { - addtoken("if(var)"); - - // Make sure the "use" will not be added - while ( tok->str() != ")" ) - tok = tok->next(); - } - else if ( TOKEN::Match(tok, "if (") && notvar(tok->tokAt(2), varnames) ) - { - addtoken("if(!var)"); - } - else if ( TOKEN::Match(tok, "if ( true )") ) - { - addtoken("if(true)"); - } - else if ( TOKEN::Match(tok, "if ( false )") ) - { - addtoken("if(false)"); - } - else if ( TOKEN::Match(tok, "if") ) - { - // Check if the condition depends on var somehow.. - bool dep = false; - int parlevel = 0; - for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) - { - if ( tok2->str() == "(" ) - ++parlevel; - if ( tok2->str() == ")" ) - { - --parlevel; - if ( parlevel <= 0 ) - break; - } - if ( (tok2->str() != ".") && - TOKEN::Match(tok2->next(), "%var1%", varnames) && - !TOKEN::Match(tok2->next(), "%var1% .", varnames) ) - { - dep = true; - break; - } - } - addtoken( (dep ? "ifv" : "if") ); - } - else if ( (tok->str() == "else") || (tok->str() == "switch") ) - { - addtoken(tok->aaaa()); - } - - if ( (tok->str() == "case") ) - { - addtoken("case"); - addtoken(";"); - } - - if ( (tok->str() == "default") ) - { - addtoken("case"); - addtoken(";"); - } - - // Loops.. - if ((tok->str() == "for") || (tok->str() == "while") ) - { - addtoken("loop"); - isloop = true; - } - if ( (tok->str() == "do") ) - { - addtoken("do"); - } - if ( isloop && notvar(tok,varnames) ) - addtoken("!var"); - - // continue / break.. - if ( tok->str() == "continue" ) - addtoken("continue"); - if ( tok->str() == "break" ) - addtoken("break"); - - // goto.. - if ( tok->str() == "goto" ) - { - addtoken("goto"); - } - - // Return.. - if ( tok->str() == "return" ) - { - addtoken("return"); - if ( TOKEN::Match(tok, "return %var1%", varnames) || - TOKEN::Match(tok, "return & %var1%", varnames) ) - addtoken("use"); - } - - // throw.. - if ( tok->str() == "throw" ) - addtoken("throw"); - - // Assignment.. - if ( TOKEN::Match(tok,"[)=] %var1% [;)]", varnames) || - TOKEN::Match(tok, "%var1% +=|-=", varnames) ) - addtoken("use"); - - // Investigate function calls.. - if ( TOKEN::Match(tok, "%var% (") ) - { - const char *str = call_func(tok, callstack, varnames, alloctype, dealloctype); - if ( str ) - addtoken( str ); - } - - // Callback.. - if ( TOKEN::Match(tok, "( * %var% ) (") ) - { - for ( const TOKEN *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next() ) - { - if ( TOKEN::Match(tok2, ";{") ) - break; - else if ( tok2->str() == varname ) - { - addtoken("use"); - break; - } - } - } - - // Linux lists.. - if ( TOKEN::Match( tok, "[=(,] & %var1% [.[]", varnames ) ) - { - // todo: better checking - addtoken("use"); - } - } - - return rethead; -} - -void CheckMemoryLeakClass::erase(TOKEN *begin, const TOKEN *end) -{ - TOKEN::eraseTokens( begin, end ); -} - -void CheckMemoryLeakClass::simplifycode(TOKEN *tok) -{ - // reduce the code.. - bool done = false; - while ( ! done ) - { - done = true; - - for ( TOKEN *tok2 = tok; tok2; tok2 = tok2 ? tok2->next() : NULL ) - { - // Delete extra ";" - while (TOKEN::Match(tok2,"[;{}] ;")) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Replace "{ }" with ";" - if ( TOKEN::Match(tok2->next(), "{ }") ) - { - tok2->next()->setstr(";"); - erase(tok2->next(), tok2->tokAt(3)); - done = false; - } - - // Delete braces around a single instruction.. - if ( TOKEN::Match(tok2->next(), "{ %var% ; }") ) - { - erase( tok2, tok2->tokAt(2) ); - erase( tok2->next()->next(), tok2->tokAt(4) ); - done = false; - } - if ( TOKEN::Match(tok2->next(), "{ %var% %var% ; }") ) - { - erase( tok2, tok2->tokAt(2) ); - erase( tok2->next()->next()->next(), tok2->tokAt(5) ); - done = false; - } - - // Delete empty if that is not followed by an else - if (tok2->tokAt(2) && - TOKEN::Match(tok2->next(), "if ; !!else") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Delete "if ; else ;" - if ( TOKEN::Match(tok2->next(), "if ; else ;") ) - { - erase( tok2, tok2->tokAt(4) ); - done = false; + } + } + + // if else switch + if ( TOKEN::Match(tok, "if ( %var1% )", varnames) || + TOKEN::Match(tok, "if ( %var1% != 0 )", varnames) || + TOKEN::Match(tok, "if ( 0 != %var1% )", varnames) ) + { + addtoken("if(var)"); + + // Make sure the "use" will not be added + while ( tok->str() != ")" ) + tok = tok->next(); + } + else if ( TOKEN::Match(tok, "if (") && notvar(tok->tokAt(2), varnames) ) + { + addtoken("if(!var)"); + } + else if ( TOKEN::Match(tok, "if ( true )") ) + { + addtoken("if(true)"); + } + else if ( TOKEN::Match(tok, "if ( false )") ) + { + addtoken("if(false)"); + } + else if ( TOKEN::Match(tok, "if") ) + { + // Check if the condition depends on var somehow.. + bool dep = false; + int parlevel = 0; + for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) + { + if ( tok2->str() == "(" ) + ++parlevel; + if ( tok2->str() == ")" ) + { + --parlevel; + if ( parlevel <= 0 ) + break; + } + if ( (tok2->str() != ".") && + TOKEN::Match(tok2->next(), "%var1%", varnames) && + !TOKEN::Match(tok2->next(), "%var1% .", varnames) ) + { + dep = true; + break; + } + } + addtoken( (dep ? "ifv" : "if") ); + } + else if ( (tok->str() == "else") || (tok->str() == "switch") ) + { + addtoken(tok->aaaa()); + } + + if ( (tok->str() == "case") ) + { + addtoken("case"); + addtoken(";"); + } + + if ( (tok->str() == "default") ) + { + addtoken("case"); + addtoken(";"); + } + + // Loops.. + if ((tok->str() == "for") || (tok->str() == "while") ) + { + addtoken("loop"); + isloop = true; + } + if ( (tok->str() == "do") ) + { + addtoken("do"); + } + if ( isloop && notvar(tok,varnames) ) + addtoken("!var"); + + // continue / break.. + if ( tok->str() == "continue" ) + addtoken("continue"); + if ( tok->str() == "break" ) + addtoken("break"); + + // goto.. + if ( tok->str() == "goto" ) + { + addtoken("goto"); + } + + // Return.. + if ( tok->str() == "return" ) + { + addtoken("return"); + if ( TOKEN::Match(tok, "return %var1%", varnames) || + TOKEN::Match(tok, "return & %var1%", varnames) ) + addtoken("use"); + } + + // throw.. + if ( tok->str() == "throw" ) + addtoken("throw"); + + // Assignment.. + if ( TOKEN::Match(tok,"[)=] %var1% [;)]", varnames) || + TOKEN::Match(tok, "%var1% +=|-=", varnames) ) + addtoken("use"); + + // Investigate function calls.. + if ( TOKEN::Match(tok, "%var% (") ) + { + const char *str = call_func(tok, callstack, varnames, alloctype, dealloctype); + if ( str ) + addtoken( str ); + } + + // Callback.. + if ( TOKEN::Match(tok, "( * %var% ) (") ) + { + for ( const TOKEN *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next() ) + { + if ( TOKEN::Match(tok2, ";{") ) + break; + else if ( tok2->str() == varname ) + { + addtoken("use"); + break; + } + } + } + + // Linux lists.. + if ( TOKEN::Match( tok, "[=(,] & %var1% [.[]", varnames ) ) + { + // todo: better checking + addtoken("use"); + } + } + + return rethead; +} + +void CheckMemoryLeakClass::erase(TOKEN *begin, const TOKEN *end) +{ + TOKEN::eraseTokens( begin, end ); +} + +void CheckMemoryLeakClass::simplifycode(TOKEN *tok) +{ + // reduce the code.. + bool done = false; + while ( ! done ) + { + done = true; + + for ( TOKEN *tok2 = tok; tok2; tok2 = tok2 ? tok2->next() : NULL ) + { + // Delete extra ";" + while (TOKEN::Match(tok2,"[;{}] ;")) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Replace "{ }" with ";" + if ( TOKEN::Match(tok2->next(), "{ }") ) + { + tok2->next()->setstr(";"); + erase(tok2->next(), tok2->tokAt(3)); + done = false; + } + + // Delete braces around a single instruction.. + if ( TOKEN::Match(tok2->next(), "{ %var% ; }") ) + { + erase( tok2, tok2->tokAt(2) ); + erase( tok2->next()->next(), tok2->tokAt(4) ); + done = false; + } + if ( TOKEN::Match(tok2->next(), "{ %var% %var% ; }") ) + { + erase( tok2, tok2->tokAt(2) ); + erase( tok2->next()->next()->next(), tok2->tokAt(5) ); + done = false; + } + + // Delete empty if that is not followed by an else + if (tok2->tokAt(2) && + TOKEN::Match(tok2->next(), "if ; !!else") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Delete "if ; else ;" + if ( TOKEN::Match(tok2->next(), "if ; else ;") ) + { + erase( tok2, tok2->tokAt(4) ); + done = false; } // Reduce "; if(!var) alloc ; !!else" => "; dealloc ; alloc ;" @@ -667,99 +667,99 @@ void CheckMemoryLeakClass::simplifycode(TOKEN *tok) tok2->insertToken( "dealloc" ); done = false; - } + } + + // TODO Make this more generic. Delete "if ; else use ; use" + if ( TOKEN::Match(tok2, "; if ; else assign|use ; assign|use") || + TOKEN::Match(tok2, "; if assign|use ; else ; assign|use") ) + { + erase( tok2, tok2->tokAt(4) ); + done = false; + } + + + // Reduce "if assign|dealloc|use ;" that is not followed by an else.. + // If "--all" has been given these are deleted + // Otherwise, only the "if" will be deleted + if (TOKEN::Match(tok2, "[;{}] if assign|dealloc|use ; !!else") ) + { + if ( _settings._showAll ) + erase(tok2, tok2->tokAt(3)); + else + erase( tok2, tok2->tokAt(2) ); + done = false; + } + + // Reduce "if(var) dealloc ;" and "if(var) use ;" that is not followed by an else.. + if (TOKEN::Match(tok2, "[;{}] if(var) assign|dealloc|use ; !!else") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Reduce "if* ;" that is not followed by an else.. + if (TOKEN::Match(tok2->next(), "if(var)|if(!var)|if(true)|if(false)|ifv ; !!else") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Reduce "if if" => "if" + if ( TOKEN::Match(tok2, "if if") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Reduce "else ;" => ";" + if ( TOKEN::Match(tok2->next(), "else ;") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Delete if block: "alloc; if return use ;" + if (TOKEN::Match(tok2,"alloc ; if return use ; !!else") ) + { + erase(tok2, tok2->tokAt(5)); + done = false; + } + + + // Reduce "if return ; alloc ;" => "alloc ;" + if (TOKEN::Match(tok2, "[;{}] if return ; alloc ;")) + { + erase(tok2, tok2->tokAt(4)); + done = false; + } + + // "[;{}] if alloc ; else return ;" => "[;{}] alloc ;" + if (TOKEN::Match(tok2,"[;{}] if alloc ; else return ;")) + { + erase(tok2, tok2->tokAt(2)); // Remove "if" + erase(tok2->next(), tok2->tokAt(5)); // Remove "; else return" + done = false; + } + + // Replace "dealloc use ;" with "dealloc ;" + if ( TOKEN::Match(tok2, "dealloc use ;") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } - // TODO Make this more generic. Delete "if ; else use ; use" - if ( TOKEN::Match(tok2, "; if ; else assign|use ; assign|use") || - TOKEN::Match(tok2, "; if assign|use ; else ; assign|use") ) - { - erase( tok2, tok2->tokAt(4) ); - done = false; - } - - - // Reduce "if assign|dealloc|use ;" that is not followed by an else.. - // If "--all" has been given these are deleted - // Otherwise, only the "if" will be deleted - if (TOKEN::Match(tok2, "[;{}] if assign|dealloc|use ; !!else") ) - { - if ( _settings._showAll ) - erase(tok2, tok2->tokAt(3)); - else - erase( tok2, tok2->tokAt(2) ); - done = false; - } - - // Reduce "if(var) dealloc ;" and "if(var) use ;" that is not followed by an else.. - if (TOKEN::Match(tok2, "[;{}] if(var) assign|dealloc|use ; !!else") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Reduce "if* ;" that is not followed by an else.. - if (TOKEN::Match(tok2->next(), "if(var)|if(!var)|if(true)|if(false)|ifv ; !!else") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Reduce "if if" => "if" - if ( TOKEN::Match(tok2, "if if") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Reduce "else ;" => ";" - if ( TOKEN::Match(tok2->next(), "else ;") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Delete if block: "alloc; if return use ;" - if (TOKEN::Match(tok2,"alloc ; if return use ; !!else") ) - { - erase(tok2, tok2->tokAt(5)); - done = false; - } - - - // Reduce "if return ; alloc ;" => "alloc ;" - if (TOKEN::Match(tok2, "[;{}] if return ; alloc ;")) - { - erase(tok2, tok2->tokAt(4)); - done = false; - } - - // "[;{}] if alloc ; else return ;" => "[;{}] alloc ;" - if (TOKEN::Match(tok2,"[;{}] if alloc ; else return ;")) - { - erase(tok2, tok2->tokAt(2)); // Remove "if" - erase(tok2->next(), tok2->tokAt(5)); // Remove "; else return" - done = false; - } - - // Replace "dealloc use ;" with "dealloc ;" - if ( TOKEN::Match(tok2, "dealloc use ;") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - // Reducing if.. if ( _settings._showAll ) - { - if (TOKEN::Match(tok2,"if assign|dealloc|use ; else")) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - if (TOKEN::Match(tok2,"[;{}] if { assign|dealloc|use ; return ; } !!else") ) - { - erase(tok2,tok2->tokAt(8)); - done = false; + { + if (TOKEN::Match(tok2,"if assign|dealloc|use ; else")) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + if (TOKEN::Match(tok2,"[;{}] if { assign|dealloc|use ; return ; } !!else") ) + { + erase(tok2,tok2->tokAt(8)); + done = false; } } @@ -770,35 +770,35 @@ void CheckMemoryLeakClass::simplifycode(TOKEN *tok) erase(tok2, tok2->tokAt(3)); done = false; } - - // Reduce "if ; else %var% ;" => "if %var% ;" - if ( TOKEN::Match(tok2, "if ; else %var% ;") ) - { - erase( tok2, tok2->tokAt(3) ); - done = false; - } - - // Reduce "if ; else return use ;" => "if return use ;" - if ( TOKEN::Match(tok2, "if ; else return use ;") ) - { - erase( tok2, tok2->tokAt(3) ); - done = false; - } - - // Reduce "do { alloc ; } " => "alloc ;" - // TODO: If the loop can be executed twice reduce to "loop alloc ;" instead - if ( TOKEN::Match(tok2->next(), "do { alloc ; }") ) - { - erase(tok2, tok2->tokAt(3)); - erase(tok2->next()->next(), tok2->tokAt(4)); - done = false; - } - - // Reduce "loop if break ; => ";" - if ( TOKEN::Match( tok2->next(), "loop if break|continue ; !!else") ) - { - erase( tok2, tok2->tokAt(4) ); - done = false; + + // Reduce "if ; else %var% ;" => "if %var% ;" + if ( TOKEN::Match(tok2, "if ; else %var% ;") ) + { + erase( tok2, tok2->tokAt(3) ); + done = false; + } + + // Reduce "if ; else return use ;" => "if return use ;" + if ( TOKEN::Match(tok2, "if ; else return use ;") ) + { + erase( tok2, tok2->tokAt(3) ); + done = false; + } + + // Reduce "do { alloc ; } " => "alloc ;" + // TODO: If the loop can be executed twice reduce to "loop alloc ;" instead + if ( TOKEN::Match(tok2->next(), "do { alloc ; }") ) + { + erase(tok2, tok2->tokAt(3)); + erase(tok2->next()->next(), tok2->tokAt(4)); + done = false; + } + + // Reduce "loop if break ; => ";" + if ( TOKEN::Match( tok2->next(), "loop if break|continue ; !!else") ) + { + erase( tok2, tok2->tokAt(4) ); + done = false; } // Reduce "loop { assign|dealloc|use ; alloc ; if break ; }" to "assign|dealloc|use ; alloc ;" @@ -810,539 +810,539 @@ void CheckMemoryLeakClass::simplifycode(TOKEN *tok) tok2 = tok2->next()->next()->next()->next(); erase( tok2, tok2->tokAt(5) ); done = false; - } - - // Reduce "if(true) X ;" => "X ;" - if (TOKEN::Match(tok2->next(), "if(true) %var% ; !!else") ) - { - erase( tok2, tok2->tokAt(2) ); - done = false; - } - - // Replace "loop { X ; break ; }" with "X ;" - if ( TOKEN::Match(tok2->next(), "loop { %var% ; break ; }") ) - { - erase(tok2, tok2->tokAt(3)); - erase(tok2->next()->next(), tok2->tokAt(6)); - done = false; - } - - // Replace "loop ;" with ";" - if ( TOKEN::Match(tok2->next(), "loop ;") ) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Replace "loop !var ;" with ";" - if ( TOKEN::Match(tok2->next(), "loop !var ;") ) - { - erase(tok2, tok2->tokAt(4)); - done = false; - } - - // Replace "loop !var alloc ;" with " alloc ;" - if ( TOKEN::Match(tok2->next(), "loop !var alloc ;") ) - { - erase(tok2, tok2->tokAt(3)); - done = false; - } - - // Delete if block in "alloc ; if(!var) return ;" - if ( TOKEN::Match(tok2, "alloc ; if(!var) return ;") ) - { - erase(tok2, tok2->tokAt(4)); - done = false; - } - - // Delete if block: "alloc; if return use ;" - if (TOKEN::Match(tok2,"alloc ; if return use ; !!else") ) - { - erase(tok2, tok2->tokAt(5)); - done = false; - } - - // Reduce "if return ; if return ;" => "if return ;" - if ( TOKEN::Match(tok2->next(), "if return ; if return ;") ) - { - erase( tok2, tok2->tokAt(4) ); - done = false; - } - - // Reduce "[;{}] return ; %var%" => "[;{}] return ;" - if ( TOKEN::Match(tok2, "[;{}] return ; %var%") ) - { - erase( tok2->next()->next(), tok2->tokAt(4) ); - done = false; - } - - // Reduce "[;{}] return use ; %var%" => "[;{}] return use ;" - if ( TOKEN::Match(tok2, "[;{}] return use ; %var%") ) - { - erase( tok2->next()->next()->next(), tok2->tokAt(5) ); - done = false; - } - - // Reduce "if(var) return use ;" => "return use ;" - if ( TOKEN::Match(tok2->next(), "if(var) return use ; !!else") ) - { - erase( tok2, tok2->tokAt(2) ); - done = false; - } - - // Reduce "if(var) assign|dealloc|use ;" => "assign|dealloc|use ;" - if ( TOKEN::Match(tok2->next(), "if(var) assign|dealloc|use ; !!else") ) - { - erase( tok2, tok2->tokAt(2) ); - done = false; - } - - // Reduce "[;{}] alloc ; dealloc ;" => "[;{}]" - if ( TOKEN::Match(tok2, "[;{}] alloc ; dealloc ;") ) - { - erase( tok2, tok2->tokAt(5) ); - done = false; - } - - // Reduce "if* alloc ; dealloc ;" => ";" - if ( TOKEN::Match(tok2->tokAt(2), "alloc ; dealloc ;") && - tok2->next()->str().find("if") == 0 ) - { - erase( tok2, tok2->tokAt(5) ); - done = false; - } - - // Delete second use in "use ; use ;" - while (TOKEN::Match(tok2, "[;{}] use ; use ;")) - { - erase(tok2, tok2->tokAt(3)); - done = false; - } - - // Delete first part in "use ; dealloc ;" - if (TOKEN::Match(tok2, "[;{}] use ; dealloc ;")) - { - erase(tok2, tok2->tokAt(3)); - done = false; - } - - // Delete first part in "use ; return use ;" - if (TOKEN::Match(tok2, "[;{}] use ; return use ;")) - { - erase(tok2, tok2->tokAt(2)); - done = false; - } - - // Delete second case in "case ; case ;" - while (TOKEN::Match(tok2, "case ; case ;")) - { - erase(tok2, tok2->tokAt(3)); - done = false; - } - - // Replace switch with if (if not complicated) - if (TOKEN::Match(tok2, "switch {")) - { - // Right now, I just handle if there are a few case and perhaps a default. - bool valid = false; - bool incase = false; - for ( const TOKEN * _tok = tok2->tokAt(2); _tok; _tok = _tok->next() ) - { - if ( _tok->str() == "{" ) - break; - - else if ( _tok->str() == "}" ) - { - valid = true; - break; - } - - else if (strncmp(_tok->aaaa(),"if",2)==0) - break; - - else if (_tok->str() == "switch") - break; - - else if (_tok->str() == "loop") - break; - - else if (incase && TOKEN::Match(_tok,"case")) - break; - - incase |= TOKEN::Match(_tok,"case"); - incase &= !TOKEN::Match(_tok,"break"); - } - - if ( !incase && valid ) - { - done = false; - tok2->setstr(";"); - erase( tok2, tok2->tokAt(2) ); - tok2 = tok2->next(); - bool first = true; - while (TOKEN::Match(tok2,"case") || TOKEN::Match(tok2,"default")) - { - bool def = TOKEN::Match(tok2, "default"); - tok2->setstr(first ? "if" : "}"); - if ( first ) - { - first = false; - instoken( tok2, "{" ); - } - else - { - // Insert "else [if] { - instoken( tok2, "{" ); - if ( ! def ) - instoken( tok2, "if" ); - instoken( tok2, "else" ); - } - while ( tok2 && tok2->str() != "}" && ! TOKEN::Match(tok2,"break ;") ) - tok2 = tok2->next(); - if (TOKEN::Match(tok2,"break ;")) - { - tok2->setstr(";"); - tok2 = tok2->next()->next(); - } - } - } - } - - if ( TOKEN::Match(tok2, "throw") ) - { - tok2->setstr( "return" ); - done = false; - } - } - } -} - - - - - -// Check for memory leaks for a function variable. -void CheckMemoryLeakClass::CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] ) -{ - std::list callstack; - - AllocType alloctype = No; - AllocType dealloctype = No; - - TOKEN *tok = getcode( Tok1, callstack, varname, alloctype, dealloctype ); + } + + // Reduce "if(true) X ;" => "X ;" + if (TOKEN::Match(tok2->next(), "if(true) %var% ; !!else") ) + { + erase( tok2, tok2->tokAt(2) ); + done = false; + } + + // Replace "loop { X ; break ; }" with "X ;" + if ( TOKEN::Match(tok2->next(), "loop { %var% ; break ; }") ) + { + erase(tok2, tok2->tokAt(3)); + erase(tok2->next()->next(), tok2->tokAt(6)); + done = false; + } + + // Replace "loop ;" with ";" + if ( TOKEN::Match(tok2->next(), "loop ;") ) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Replace "loop !var ;" with ";" + if ( TOKEN::Match(tok2->next(), "loop !var ;") ) + { + erase(tok2, tok2->tokAt(4)); + done = false; + } + + // Replace "loop !var alloc ;" with " alloc ;" + if ( TOKEN::Match(tok2->next(), "loop !var alloc ;") ) + { + erase(tok2, tok2->tokAt(3)); + done = false; + } + + // Delete if block in "alloc ; if(!var) return ;" + if ( TOKEN::Match(tok2, "alloc ; if(!var) return ;") ) + { + erase(tok2, tok2->tokAt(4)); + done = false; + } + + // Delete if block: "alloc; if return use ;" + if (TOKEN::Match(tok2,"alloc ; if return use ; !!else") ) + { + erase(tok2, tok2->tokAt(5)); + done = false; + } + + // Reduce "if return ; if return ;" => "if return ;" + if ( TOKEN::Match(tok2->next(), "if return ; if return ;") ) + { + erase( tok2, tok2->tokAt(4) ); + done = false; + } + + // Reduce "[;{}] return ; %var%" => "[;{}] return ;" + if ( TOKEN::Match(tok2, "[;{}] return ; %var%") ) + { + erase( tok2->next()->next(), tok2->tokAt(4) ); + done = false; + } + + // Reduce "[;{}] return use ; %var%" => "[;{}] return use ;" + if ( TOKEN::Match(tok2, "[;{}] return use ; %var%") ) + { + erase( tok2->next()->next()->next(), tok2->tokAt(5) ); + done = false; + } + + // Reduce "if(var) return use ;" => "return use ;" + if ( TOKEN::Match(tok2->next(), "if(var) return use ; !!else") ) + { + erase( tok2, tok2->tokAt(2) ); + done = false; + } + + // Reduce "if(var) assign|dealloc|use ;" => "assign|dealloc|use ;" + if ( TOKEN::Match(tok2->next(), "if(var) assign|dealloc|use ; !!else") ) + { + erase( tok2, tok2->tokAt(2) ); + done = false; + } + + // Reduce "[;{}] alloc ; dealloc ;" => "[;{}]" + if ( TOKEN::Match(tok2, "[;{}] alloc ; dealloc ;") ) + { + erase( tok2, tok2->tokAt(5) ); + done = false; + } + + // Reduce "if* alloc ; dealloc ;" => ";" + if ( TOKEN::Match(tok2->tokAt(2), "alloc ; dealloc ;") && + tok2->next()->str().find("if") == 0 ) + { + erase( tok2, tok2->tokAt(5) ); + done = false; + } + + // Delete second use in "use ; use ;" + while (TOKEN::Match(tok2, "[;{}] use ; use ;")) + { + erase(tok2, tok2->tokAt(3)); + done = false; + } + + // Delete first part in "use ; dealloc ;" + if (TOKEN::Match(tok2, "[;{}] use ; dealloc ;")) + { + erase(tok2, tok2->tokAt(3)); + done = false; + } + + // Delete first part in "use ; return use ;" + if (TOKEN::Match(tok2, "[;{}] use ; return use ;")) + { + erase(tok2, tok2->tokAt(2)); + done = false; + } + + // Delete second case in "case ; case ;" + while (TOKEN::Match(tok2, "case ; case ;")) + { + erase(tok2, tok2->tokAt(3)); + done = false; + } + + // Replace switch with if (if not complicated) + if (TOKEN::Match(tok2, "switch {")) + { + // Right now, I just handle if there are a few case and perhaps a default. + bool valid = false; + bool incase = false; + for ( const TOKEN * _tok = tok2->tokAt(2); _tok; _tok = _tok->next() ) + { + if ( _tok->str() == "{" ) + break; + + else if ( _tok->str() == "}" ) + { + valid = true; + break; + } + + else if (strncmp(_tok->aaaa(),"if",2)==0) + break; + + else if (_tok->str() == "switch") + break; + + else if (_tok->str() == "loop") + break; + + else if (incase && TOKEN::Match(_tok,"case")) + break; + + incase |= TOKEN::Match(_tok,"case"); + incase &= !TOKEN::Match(_tok,"break"); + } + + if ( !incase && valid ) + { + done = false; + tok2->setstr(";"); + erase( tok2, tok2->tokAt(2) ); + tok2 = tok2->next(); + bool first = true; + while (TOKEN::Match(tok2,"case") || TOKEN::Match(tok2,"default")) + { + bool def = TOKEN::Match(tok2, "default"); + tok2->setstr(first ? "if" : "}"); + if ( first ) + { + first = false; + instoken( tok2, "{" ); + } + else + { + // Insert "else [if] { + instoken( tok2, "{" ); + if ( ! def ) + instoken( tok2, "if" ); + instoken( tok2, "else" ); + } + while ( tok2 && tok2->str() != "}" && ! TOKEN::Match(tok2,"break ;") ) + tok2 = tok2->next(); + if (TOKEN::Match(tok2,"break ;")) + { + tok2->setstr(";"); + tok2 = tok2->next()->next(); + } + } + } + } + + if ( TOKEN::Match(tok2, "throw") ) + { + tok2->setstr( "return" ); + done = false; + } + } + } +} + + + + + +// Check for memory leaks for a function variable. +void CheckMemoryLeakClass::CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] ) +{ + std::list callstack; + + AllocType alloctype = No; + AllocType dealloctype = No; + + TOKEN *tok = getcode( Tok1, callstack, varname, alloctype, dealloctype ); //tok->printOut( "getcode result" ); - simplifycode( tok ); + simplifycode( tok ); //tok->printOut( "simplifycode result" ); - - // If the variable is not allocated at all => no memory leak - if (TOKEN::findmatch(tok, "alloc") == 0) - { - Tokenizer::deleteTokens(tok); - return; - } - - // TODO : handle "goto" - if (TOKEN::findmatch(tok, "goto")) - { - Tokenizer::deleteTokens(tok); - return; - } - - const TOKEN *result; - if ( (result = TOKEN::findmatch(tok, "loop alloc ;")) != NULL ) - { - MemoryLeak(result, varname, alloctype); - } - - else if ( (result = TOKEN::findmatch(tok, "alloc ; if break|continue|return ;")) != NULL ) + + // If the variable is not allocated at all => no memory leak + if (TOKEN::findmatch(tok, "alloc") == 0) { - MemoryLeak(result->tokAt(3), varname, alloctype); - } - - else if ( _settings._showAll && (result = TOKEN::findmatch(tok, "alloc ; ifv break|continue|return ;")) != NULL ) - { - MemoryLeak(result->tokAt(3), varname, alloctype); - } - - else if ( (result = TOKEN::findmatch(tok, "alloc ; alloc|assign|return ;")) != NULL ) - { - MemoryLeak(result->tokAt(2), varname, alloctype); - } - - else if ( ! TOKEN::findmatch(tok,"dealloc") && - ! TOKEN::findmatch(tok,"use") && - ! TOKEN::findmatch(tok,"return use ;") ) - { - const TOKEN *last = tok; - while (last->next()) - last = last->next(); - MemoryLeak(last, varname, alloctype); - } - - // detect cases that "simplifycode" don't handle well.. - else if ( _settings._debug ) - { - TOKEN *first = tok; - while ( first && first->str() == ";" ) - first = first->next(); - - bool noerr = false; - noerr |= TOKEN::Match( first, "alloc ; }" ); - noerr |= TOKEN::Match( first, "alloc ; dealloc ; }" ); - noerr |= TOKEN::Match( first, "alloc ; return use ; }" ); - noerr |= TOKEN::Match( first, "alloc ; use ; }" ); - noerr |= TOKEN::Match( first, "alloc ; use ; return ; }" ); - noerr |= TOKEN::Match( first, "if alloc ; dealloc ; }" ); - noerr |= TOKEN::Match( first, "if alloc ; return use ; }" ); - noerr |= TOKEN::Match( first, "if alloc ; use ; }" ); - noerr |= TOKEN::Match( first, "alloc ; ifv return ; dealloc ; }" ); - noerr |= TOKEN::Match( first, "alloc ; if return ; dealloc; }" ); - - // Unhandled case.. - if ( ! noerr ) - { - std::cout << "Token listing..\n "; - for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) - std::cout << " " << tok2->str(); - std::cout << "\n"; - } - } - - Tokenizer::deleteTokens(tok); -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// Checks for memory leaks inside function.. -//--------------------------------------------------------------------------- - -void CheckMemoryLeakClass::CheckMemoryLeak_InFunction() -{ - bool infunc = false; - int indentlevel = 0; - for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->str() == "{") - indentlevel++; - - else if (tok->str() == "}") - indentlevel--; - - - // In function.. - if ( indentlevel == 0 ) - { - if ( TOKEN::Match(tok, ") {") ) - infunc = true; - - if ( TOKEN::Match(tok, "[;}]") ) - infunc = false; - } - - // Declare a local variable => Check - if (indentlevel>0 && infunc) - { - if ( TOKEN::Match(tok, "[{};] %type% * %var% [;=]") ) - CheckMemoryLeak_CheckScope( tok->next(), tok->strAt(3) ); - - else if ( TOKEN::Match(tok, "[{};] %type% %type% * %var% [;=]") ) - CheckMemoryLeak_CheckScope( tok->next(), tok->strAt(4) ); - } - } -} -//--------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------- -// Checks for memory leaks in classes.. -//--------------------------------------------------------------------------- - - - -void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers() -{ - 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 && TOKEN::Match(tok, "class %var% [{:]") ) - { - std::vector classname; - classname.push_back( tok->strAt(1) ); - CheckMemoryLeak_ClassMembers_ParseClass( tok, classname ); - } - } -} - - -void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers_ParseClass( const TOKEN *tok1, std::vector &classname ) -{ - // Go into class. - while ( tok1 && tok1->str() != "{" ) - tok1 = tok1->next(); - if ( tok1 ) - tok1 = tok1->next(); - - int indentlevel = 0; - for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) - { - if ( tok->str() == "{" ) - indentlevel++; - - else if ( tok->str() == "}" ) - { - indentlevel--; - if ( indentlevel < 0 ) - return; - } - - // Only parse this particular class.. not subclasses - if ( indentlevel > 0 ) - continue; - - // Declaring subclass.. recursive checking - if ( TOKEN::Match(tok, "class %var% [{:]") ) - { - classname.push_back( tok->strAt(1) ); - CheckMemoryLeak_ClassMembers_ParseClass( tok, classname ); - classname.pop_back(); - } - - // Declaring member variable.. check allocations and deallocations - if ( TOKEN::Match(tok->next(), "%type% * %var% ;") ) - { - if ( tok->isName() || TOKEN::Match(tok, "[;}]")) - { - if (_settings._showAll || !isclass(tok->tokAt(1))) - CheckMemoryLeak_ClassMembers_Variable( classname, tok->strAt(3) ); - } - } - } -} - -void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers_Variable( const std::vector &classname, const char varname[] ) -{ - // Function pattern.. Check if member function - std::ostringstream fpattern; - for ( unsigned int i = 0; i < classname.size(); i++ ) - { - fpattern << classname[i] << " :: "; - } - fpattern << "%var% ("; - - // Destructor pattern.. Check if class destructor.. - std::ostringstream destructor; - for ( unsigned int i = 0; i < classname.size(); i++ ) - { - destructor << classname[i] << " :: "; - } - destructor << " ~ " << classname.back() << " ("; - - // Pattern used in member function. "Var = ..." - std::ostringstream varname_eq; - varname_eq << varname << " ="; - - // Full variable name.. - std::ostringstream FullVariableName; - for ( unsigned int i = 0; i < classname.size(); i++ ) - FullVariableName << classname[i] << "::"; - FullVariableName << varname; - - // Check if member variable has been allocated and deallocated.. - AllocType Alloc = No; - AllocType Dealloc = No; - - // Loop through all tokens. Inspect member functions - bool memberfunction = false; - int indentlevel = 0; - for ( const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next() ) - { - if ( tok->str() == "{" ) - indentlevel++; - - else if ( tok->str() == "}" ) - indentlevel--; - - // Set the 'memberfunction' variable.. - if ( indentlevel == 0 ) - { - if ( TOKEN::Match(tok, "[;}]") ) - memberfunction = false; - else if ( TOKEN::Match( tok, fpattern.str().c_str() ) || TOKEN::Match( tok, destructor.str().c_str() ) ) - memberfunction = true; - } - - // Parse member function.. - if ( indentlevel > 0 && memberfunction ) - { - // Allocate.. - if ( TOKEN::Match( tok, varname_eq.str().c_str() ) ) - { - AllocType alloc = GetAllocationType( tok->tokAt(2) ); - if ( alloc != No ) - { - std::list callstack; - if ( Dealloc != No && Dealloc != alloc ) - MismatchError( tok, callstack, FullVariableName.str().c_str() ); - if ( Alloc != No && Alloc != alloc ) - MismatchError( tok, callstack, FullVariableName.str().c_str() ); - Alloc = alloc; - } - } - - // Deallocate.. - const char *varnames[2] = { "var", 0 }; - varnames[0] = varname; - AllocType dealloc = GetDeallocationType( tok, varnames ); - if ( dealloc != No ) - { - std::list callstack; - if ( Dealloc != No && Dealloc != dealloc ) - MismatchError( tok, callstack, FullVariableName.str().c_str() ); - if ( Alloc != No && Alloc != dealloc ) - MismatchError( tok, callstack, FullVariableName.str().c_str() ); - Dealloc = dealloc; - } - } - } - - if ( Alloc != No && Dealloc == No ) - { - MemoryLeak( _tokenizer->tokens(), FullVariableName.str().c_str(), Alloc ); - } -} - - - - -//--------------------------------------------------------------------------- -// Checks for memory leaks.. -//--------------------------------------------------------------------------- - -void CheckMemoryLeakClass::CheckMemoryLeak() -{ - _listAllocFunc.clear(); - - // Check for memory leaks inside functions.. - CheckMemoryLeak_InFunction(); - - // Check that all class members are deallocated.. - if ( _settings._showAll ) - CheckMemoryLeak_ClassMembers(); -} -//--------------------------------------------------------------------------- - - - - - + Tokenizer::deleteTokens(tok); + return; + } + + // TODO : handle "goto" + if (TOKEN::findmatch(tok, "goto")) + { + Tokenizer::deleteTokens(tok); + return; + } + + const TOKEN *result; + if ( (result = TOKEN::findmatch(tok, "loop alloc ;")) != NULL ) + { + MemoryLeak(result, varname, alloctype); + } + + else if ( (result = TOKEN::findmatch(tok, "alloc ; if break|continue|return ;")) != NULL ) + { + MemoryLeak(result->tokAt(3), varname, alloctype); + } + + else if ( _settings._showAll && (result = TOKEN::findmatch(tok, "alloc ; ifv break|continue|return ;")) != NULL ) + { + MemoryLeak(result->tokAt(3), varname, alloctype); + } + + else if ( (result = TOKEN::findmatch(tok, "alloc ; alloc|assign|return ;")) != NULL ) + { + MemoryLeak(result->tokAt(2), varname, alloctype); + } + + else if ( ! TOKEN::findmatch(tok,"dealloc") && + ! TOKEN::findmatch(tok,"use") && + ! TOKEN::findmatch(tok,"return use ;") ) + { + const TOKEN *last = tok; + while (last->next()) + last = last->next(); + MemoryLeak(last, varname, alloctype); + } + + // detect cases that "simplifycode" don't handle well.. + else if ( _settings._debug ) + { + TOKEN *first = tok; + while ( first && first->str() == ";" ) + first = first->next(); + + bool noerr = false; + noerr |= TOKEN::Match( first, "alloc ; }" ); + noerr |= TOKEN::Match( first, "alloc ; dealloc ; }" ); + noerr |= TOKEN::Match( first, "alloc ; return use ; }" ); + noerr |= TOKEN::Match( first, "alloc ; use ; }" ); + noerr |= TOKEN::Match( first, "alloc ; use ; return ; }" ); + noerr |= TOKEN::Match( first, "if alloc ; dealloc ; }" ); + noerr |= TOKEN::Match( first, "if alloc ; return use ; }" ); + noerr |= TOKEN::Match( first, "if alloc ; use ; }" ); + noerr |= TOKEN::Match( first, "alloc ; ifv return ; dealloc ; }" ); + noerr |= TOKEN::Match( first, "alloc ; if return ; dealloc; }" ); + + // Unhandled case.. + if ( ! noerr ) + { + std::cout << "Token listing..\n "; + for ( const TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) + std::cout << " " << tok2->str(); + std::cout << "\n"; + } + } + + Tokenizer::deleteTokens(tok); +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// Checks for memory leaks inside function.. +//--------------------------------------------------------------------------- + +void CheckMemoryLeakClass::CheckMemoryLeak_InFunction() +{ + bool infunc = false; + int indentlevel = 0; + for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() == "{") + indentlevel++; + + else if (tok->str() == "}") + indentlevel--; + + + // In function.. + if ( indentlevel == 0 ) + { + if ( TOKEN::Match(tok, ") {") ) + infunc = true; + + if ( TOKEN::Match(tok, "[;}]") ) + infunc = false; + } + + // Declare a local variable => Check + if (indentlevel>0 && infunc) + { + if ( TOKEN::Match(tok, "[{};] %type% * %var% [;=]") ) + CheckMemoryLeak_CheckScope( tok->next(), tok->strAt(3) ); + + else if ( TOKEN::Match(tok, "[{};] %type% %type% * %var% [;=]") ) + CheckMemoryLeak_CheckScope( tok->next(), tok->strAt(4) ); + } + } +} +//--------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// Checks for memory leaks in classes.. +//--------------------------------------------------------------------------- + + + +void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers() +{ + 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 && TOKEN::Match(tok, "class %var% [{:]") ) + { + std::vector classname; + classname.push_back( tok->strAt(1) ); + CheckMemoryLeak_ClassMembers_ParseClass( tok, classname ); + } + } +} + + +void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers_ParseClass( const TOKEN *tok1, std::vector &classname ) +{ + // Go into class. + while ( tok1 && tok1->str() != "{" ) + tok1 = tok1->next(); + if ( tok1 ) + tok1 = tok1->next(); + + int indentlevel = 0; + for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) + { + if ( tok->str() == "{" ) + indentlevel++; + + else if ( tok->str() == "}" ) + { + indentlevel--; + if ( indentlevel < 0 ) + return; + } + + // Only parse this particular class.. not subclasses + if ( indentlevel > 0 ) + continue; + + // Declaring subclass.. recursive checking + if ( TOKEN::Match(tok, "class %var% [{:]") ) + { + classname.push_back( tok->strAt(1) ); + CheckMemoryLeak_ClassMembers_ParseClass( tok, classname ); + classname.pop_back(); + } + + // Declaring member variable.. check allocations and deallocations + if ( TOKEN::Match(tok->next(), "%type% * %var% ;") ) + { + if ( tok->isName() || TOKEN::Match(tok, "[;}]")) + { + if (_settings._showAll || !isclass(tok->tokAt(1))) + CheckMemoryLeak_ClassMembers_Variable( classname, tok->strAt(3) ); + } + } + } +} + +void CheckMemoryLeakClass::CheckMemoryLeak_ClassMembers_Variable( const std::vector &classname, const char varname[] ) +{ + // Function pattern.. Check if member function + std::ostringstream fpattern; + for ( unsigned int i = 0; i < classname.size(); i++ ) + { + fpattern << classname[i] << " :: "; + } + fpattern << "%var% ("; + + // Destructor pattern.. Check if class destructor.. + std::ostringstream destructor; + for ( unsigned int i = 0; i < classname.size(); i++ ) + { + destructor << classname[i] << " :: "; + } + destructor << " ~ " << classname.back() << " ("; + + // Pattern used in member function. "Var = ..." + std::ostringstream varname_eq; + varname_eq << varname << " ="; + + // Full variable name.. + std::ostringstream FullVariableName; + for ( unsigned int i = 0; i < classname.size(); i++ ) + FullVariableName << classname[i] << "::"; + FullVariableName << varname; + + // Check if member variable has been allocated and deallocated.. + AllocType Alloc = No; + AllocType Dealloc = No; + + // Loop through all tokens. Inspect member functions + bool memberfunction = false; + int indentlevel = 0; + for ( const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next() ) + { + if ( tok->str() == "{" ) + indentlevel++; + + else if ( tok->str() == "}" ) + indentlevel--; + + // Set the 'memberfunction' variable.. + if ( indentlevel == 0 ) + { + if ( TOKEN::Match(tok, "[;}]") ) + memberfunction = false; + else if ( TOKEN::Match( tok, fpattern.str().c_str() ) || TOKEN::Match( tok, destructor.str().c_str() ) ) + memberfunction = true; + } + + // Parse member function.. + if ( indentlevel > 0 && memberfunction ) + { + // Allocate.. + if ( TOKEN::Match( tok, varname_eq.str().c_str() ) ) + { + AllocType alloc = GetAllocationType( tok->tokAt(2) ); + if ( alloc != No ) + { + std::list callstack; + if ( Dealloc != No && Dealloc != alloc ) + MismatchError( tok, callstack, FullVariableName.str().c_str() ); + if ( Alloc != No && Alloc != alloc ) + MismatchError( tok, callstack, FullVariableName.str().c_str() ); + Alloc = alloc; + } + } + + // Deallocate.. + const char *varnames[2] = { "var", 0 }; + varnames[0] = varname; + AllocType dealloc = GetDeallocationType( tok, varnames ); + if ( dealloc != No ) + { + std::list callstack; + if ( Dealloc != No && Dealloc != dealloc ) + MismatchError( tok, callstack, FullVariableName.str().c_str() ); + if ( Alloc != No && Alloc != dealloc ) + MismatchError( tok, callstack, FullVariableName.str().c_str() ); + Dealloc = dealloc; + } + } + } + + if ( Alloc != No && Dealloc == No ) + { + MemoryLeak( _tokenizer->tokens(), FullVariableName.str().c_str(), Alloc ); + } +} + + + + +//--------------------------------------------------------------------------- +// Checks for memory leaks.. +//--------------------------------------------------------------------------- + +void CheckMemoryLeakClass::CheckMemoryLeak() +{ + _listAllocFunc.clear(); + + // Check for memory leaks inside functions.. + CheckMemoryLeak_InFunction(); + + // Check that all class members are deallocated.. + if ( _settings._showAll ) + CheckMemoryLeak_ClassMembers(); +} +//--------------------------------------------------------------------------- + + + + + diff --git a/checkmemoryleak.h b/checkmemoryleak.h index 6b520193d..680259e22 100644 --- a/checkmemoryleak.h +++ b/checkmemoryleak.h @@ -1,108 +1,108 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -class CheckMemoryLeakClass -{ -public: - CheckMemoryLeakClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ); - ~CheckMemoryLeakClass(); - void CheckMemoryLeak(); - -private: - - enum AllocType { No, Malloc, gMalloc, New, NewA, FOPEN, POPEN }; - - // Extra allocation.. - class AllocFunc - { - public: - const char *funcname; - AllocType alloctype; - - AllocFunc(const char f[], AllocType a) - { - funcname = f; - alloctype = a; - } - }; - - void CheckMemoryLeak_ClassMembers_Variable( const std::vector &classname, const char varname[] ); - void CheckMemoryLeak_ClassMembers_ParseClass( const TOKEN *tok1, std::vector &classname ); - void CheckMemoryLeak_ClassMembers(); - void CheckMemoryLeak_InFunction(); - void CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] ); - - /** - * Simplify code e.g. by replacing empty "{ }" with ";" - * @param tok first token. The tokens list can be modified. - */ - void simplifycode(TOKEN *tok); - - /** - * Delete tokens between begin and end. E.g. if begin = 1 - * and end = 5, tokens 2,3 and 4 would be erased. - * - * @param begin Tokens after this will be erased. - * @param end Tokens before this will be erased. - */ - void erase(TOKEN *begin, const TOKEN *end); - - /** - * Extract a new tokens list that is easier to parse than the "tokens" - * @param tok start parse token - * @param callstack callstack - * @param varname name of variable - * @param alloctype - * @param dealloctype - * @return Newly allocated token array. Caller needs to release reserved - * memory by calling Tokenizer::deleteTokens(returnValue); - */ - TOKEN *getcode(const TOKEN *tok, std::list callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype); - bool notvar(const TOKEN *tok, const char *varnames[]); - void instoken(TOKEN *tok, const char str[]); - void MemoryLeak( const TOKEN *tok, const char varname[], AllocType alloctype ); - void MismatchError( const TOKEN *Tok1, const std::list &callstack, const char varname[] ); - const char * call_func( const TOKEN *tok, std::list callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype ); - AllocType GetDeallocationType( const TOKEN *tok, const char *varnames[]); - AllocType GetAllocationType( const TOKEN *tok2 ); - AllocType GetReallocationType( const TOKEN *tok2 ); - bool isclass( const TOKEN *typestr ); - - const Tokenizer *_tokenizer; - ErrorLogger *_errorLogger; - const Settings _settings; - std::list _listAllocFunc; -}; - -//--------------------------------------------------------------------------- -#endif +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +class CheckMemoryLeakClass +{ +public: + CheckMemoryLeakClass( const Tokenizer *tokenizer, const Settings &settings, ErrorLogger *errorLogger ); + ~CheckMemoryLeakClass(); + void CheckMemoryLeak(); + +private: + + enum AllocType { No, Malloc, gMalloc, New, NewA, FOPEN, POPEN }; + + // Extra allocation.. + class AllocFunc + { + public: + const char *funcname; + AllocType alloctype; + + AllocFunc(const char f[], AllocType a) + { + funcname = f; + alloctype = a; + } + }; + + void CheckMemoryLeak_ClassMembers_Variable( const std::vector &classname, const char varname[] ); + void CheckMemoryLeak_ClassMembers_ParseClass( const TOKEN *tok1, std::vector &classname ); + void CheckMemoryLeak_ClassMembers(); + void CheckMemoryLeak_InFunction(); + void CheckMemoryLeak_CheckScope( const TOKEN *Tok1, const char varname[] ); + + /** + * Simplify code e.g. by replacing empty "{ }" with ";" + * @param tok first token. The tokens list can be modified. + */ + void simplifycode(TOKEN *tok); + + /** + * Delete tokens between begin and end. E.g. if begin = 1 + * and end = 5, tokens 2,3 and 4 would be erased. + * + * @param begin Tokens after this will be erased. + * @param end Tokens before this will be erased. + */ + void erase(TOKEN *begin, const TOKEN *end); + + /** + * Extract a new tokens list that is easier to parse than the "tokens" + * @param tok start parse token + * @param callstack callstack + * @param varname name of variable + * @param alloctype + * @param dealloctype + * @return Newly allocated token array. Caller needs to release reserved + * memory by calling Tokenizer::deleteTokens(returnValue); + */ + TOKEN *getcode(const TOKEN *tok, std::list callstack, const char varname[], AllocType &alloctype, AllocType &dealloctype); + bool notvar(const TOKEN *tok, const char *varnames[]); + void instoken(TOKEN *tok, const char str[]); + void MemoryLeak( const TOKEN *tok, const char varname[], AllocType alloctype ); + void MismatchError( const TOKEN *Tok1, const std::list &callstack, const char varname[] ); + const char * call_func( const TOKEN *tok, std::list callstack, const char *varnames[], AllocType &alloctype, AllocType &dealloctype ); + AllocType GetDeallocationType( const TOKEN *tok, const char *varnames[]); + AllocType GetAllocationType( const TOKEN *tok2 ); + AllocType GetReallocationType( const TOKEN *tok2 ); + bool isclass( const TOKEN *typestr ); + + const Tokenizer *_tokenizer; + ErrorLogger *_errorLogger; + const Settings _settings; + std::list _listAllocFunc; +}; + +//--------------------------------------------------------------------------- +#endif diff --git a/checkother.cpp b/checkother.cpp index 2d36313f7..91a9a3eda 100644 --- a/checkother.cpp +++ b/checkother.cpp @@ -1,134 +1,134 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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, ErrorLogger *errorLogger ) -{ - _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 char *pattern[] = {"class","",NULL}; - pattern[1] = tok->strAt(1); - if (!TOKEN::findtoken(_tokenizer->tokens(), pattern)) - continue; - - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": C-style pointer casting"; - _errorLogger->reportErr(ostr.str()); - } -} - - - - -//--------------------------------------------------------------------------- -// 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; - - if ( tok2->str() == "{" ) - tok2 = tok2->next(); - - bool err = false; - 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) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Redundant condition. It is safe to deallocate a NULL pointer"; - _errorLogger->reportErr(ostr.str()); - } - } - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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, ErrorLogger *errorLogger ) +{ + _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 char *pattern[] = {"class","",NULL}; + pattern[1] = tok->strAt(1); + if (!TOKEN::findtoken(_tokenizer->tokens(), pattern)) + continue; + + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": C-style pointer casting"; + _errorLogger->reportErr(ostr.str()); + } +} + + + + +//--------------------------------------------------------------------------- +// 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; + + if ( tok2->str() == "{" ) + tok2 = tok2->next(); + + bool err = false; + 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) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Redundant condition. It is safe to deallocate a NULL pointer"; + _errorLogger->reportErr(ostr.str()); + } + } + + + + // Redundant condition + // if (haystack.find(needle) != haystack.end()) // haystack.remove(needle); redundantCondition2(); -} -//--------------------------------------------------------------------------- - +} +//--------------------------------------------------------------------------- + void CheckOther::redundantCondition2() { const char pattern[] = "if ( %var% . find ( %any% ) != %var% . end ( ) ) " @@ -152,744 +152,744 @@ void CheckOther::redundantCondition2() var2->str() == var3->str() && any1->str() == any2->str() ) { - std::ostringstream errmsg; + std::ostringstream errmsg; errmsg << _tokenizer->fileLine(tok) - << ": Redundant condition found. The remove function in the STL will not do anything if element doesn't exist"; + << ": Redundant condition found. The remove function in the STL will not do anything if element doesn't exist"; _errorLogger->reportErr(errmsg.str()); } tok = TOKEN::findmatch( tok->next(), pattern ); } } -//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------- -// if (condition) .... -//--------------------------------------------------------------------------- - -void CheckOther::WarningIf() -{ - - // Search for 'if (condition);' - for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (tok->str() == "if") - { - 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 (strcmp(tok2->strAt(1), ";") == 0 && - strcmp(tok2->strAt(2), "else") != 0) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Found \"if (condition);\""; - _errorLogger->reportErr(ostr.str()); - } - break; - } - } - } - } - } - - // 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. - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok->tokAt(4)) << ": The condition is always "; - for (int i = 0; i < 6; i++) - { - if (strcmp(cond, p[i]) == 0) - ostr << (i < 3 ? "True" : "False"); - } - _errorLogger->reportErr(ostr.str()); - } -} -//--------------------------------------------------------------------------- - - - - -//--------------------------------------------------------------------------- -// strtol(str, 0, radix) <- radix must be 0 or 2-36 -//--------------------------------------------------------------------------- - -void CheckOther::InvalidFunctionUsage() -{ - 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))) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok2) << ": Invalid radix in call to strtol or strtoul. Must be 0 or 2-36"; - _errorLogger->reportErr(ostr.str()); - } - } - break; - } - } - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// Assignment in condition -//--------------------------------------------------------------------------- - -void CheckOther::CheckIfAssignment() -{ - for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) - { - if (TOKEN::Match(tok, "if ( %var% = %num% )") || - TOKEN::Match(tok, "if ( %var% = %str% )") || - TOKEN::Match(tok, "if ( %var% = %var% )") ) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok) << ": Possible bug. Should it be '==' instead of '='?"; - _errorLogger->reportErr(ostr.str()); - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// 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%")) - { - 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.. - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok->next()) << ": Warning: Division with signed and unsigned operators"; - _errorLogger->reportErr(ostr.str()); - } - } - - else if (!TOKEN::Match(tok,"[).]") && TOKEN::Match(tok->next(), "%var% / - %num%")) - { - const char *varname1 = tok->strAt(1); - char sign1 = varsign[varname1]; - if ( sign1 == 'u' ) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok->next()) << ": Unsigned division. The result will be wrong."; - _errorLogger->reportErr(ostr.str()); - } - } - - else if (TOKEN::Match(tok, "[([=*/+-] - %num% / %var%")) - { - const char *varname2 = tok->strAt(4); - char sign2 = varsign[varname2]; - if ( sign2 == 'u' ) - { - std::ostringstream ostr; - ostr << _tokenizer->fileLine(tok->next()) << ": Unsigned division. The result will be wrong."; - _errorLogger->reportErr(ostr.str()); - } - } - } -} -//--------------------------------------------------------------------------- - - - -//--------------------------------------------------------------------------- -// 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 _indentlevel = 0; - for (tok = tok2; tok; tok = tok->next()) - { - if ( tok->str() == "{" ) - { - _indentlevel++; - } - if ( tok->str() == "}" ) - { - _indentlevel--; - if ( _indentlevel <= 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 - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok1) << " The scope of the variable '" << varname << "' can be limited"; - _errorLogger->reportErr( errmsg.str() ); -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// 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% [,)]") ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(5) << " is passed by value, it could be passed by reference/pointer instead"; - _errorLogger->reportErr( errmsg.str() ); - } - - else if ( TOKEN::Match(tok,"[,(] const %type% %var% [,)]") ) - { - // Check if type is a struct or class. - const char *pattern[3] = {"class","type",0}; - pattern[1] = tok->strAt( 2); - if ( TOKEN::findtoken(_tokenizer->tokens(), pattern) ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(3) << " is passed by value, it could be passed by reference/pointer instead"; - _errorLogger->reportErr( errmsg.str() ); - } - pattern[0] = "struct"; - if ( TOKEN::findtoken(_tokenizer->tokens(), pattern) ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(3) << " is passed by value, it could be passed by reference/pointer instead"; - _errorLogger->reportErr( errmsg.str() ); - } - } - } -} -//--------------------------------------------------------------------------- - - -//--------------------------------------------------------------------------- -// 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 ( tok->str() == "}" ) - structname = 0; - if ( TOKEN::Match(tok, "struct %type% {") ) - structname = tok->strAt( 1); - - 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( 2 ); - 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 char *varnames[2]; - varnames[0] = varname; - varnames[1] = 0; - bool used = false; - for ( const TOKEN *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next() ) - { - if ( tok->fileIndex() != 0 ) - continue; - - if (TOKEN::Match(tok2, ". %var%", varnames)) - { - if ( strcmp("=", tok2->strAt(2)) == 0 ) - continue; - used = true; - break; - } - } - - if ( ! used ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok) << ": struct member '" << structname << "::" << varname << "' is never read"; - _errorLogger->reportErr(errmsg.str()); - } - } - } -} - - - - - -//--------------------------------------------------------------------------- -// 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% [;=,)]") ) - { - const char *varname[2] = {0}; - varname[0] = tok->strAt( 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; - } - - if ((tok2->str() != ".") && TOKEN::Match(tok2->next(), "%var% [ %var1% ]", varname)) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok2->next()) << ": Warning - using char variable as array index"; - _errorLogger->reportErr(errmsg.str()); - break; - } - - if ( TOKEN::Match(tok2, "%var% [&|] %var1%", varname) || TOKEN::Match(tok2, "%var1% [&|]", varname) ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok2) << ": Warning - using char variable in bit operation"; - _errorLogger->reportErr(errmsg.str()); - 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), ",") ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok->next()) << ": Redundant code: Found a statement that begins with string constant"; - _errorLogger->reportErr(errmsg.str()); - } - - if ( !TOKEN::Match(tok,"#") && TOKEN::Match(tok->next(),"; %num%") && !TOKEN::Match(tok->tokAt(3), ",") ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok->next()) << ": Redundant code: Found a statement that begins with numeric constant"; - _errorLogger->reportErr(errmsg.str()); - } - } -} -//--------------------------------------------------------------------------- - - - - - - -//--------------------------------------------------------------------------- -// Unreachable code below a 'return' -//--------------------------------------------------------------------------- - -void CheckOther::unreachableCode() -{ - const TOKEN *tok = TOKEN::findmatch( _tokenizer->tokens(), "[;{}] return" ); - while ( tok ) - { - // Goto the 'return' token - tok = tok->next(); - - // Locate the end of the 'return' statement - while ( tok && ! TOKEN::Match(tok, ";") ) - tok = tok->next(); - while ( tok && TOKEN::Match(tok->next(), ";") ) - tok = tok->next(); - - // If there is a statement below the return it is unreachable - if (!TOKEN::Match(tok, "; case|default|}|#") && !TOKEN::Match(tok, "; %var% :")) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok->next()) << ": Unreachable code below a 'return'"; - _errorLogger->reportErr(errmsg.str()); - } - - // Find the next 'return' statement - tok = TOKEN::findmatch( tok, "[;{}] return" ); - } -} -//--------------------------------------------------------------------------- - - - - - - -//--------------------------------------------------------------------------- -// Usage of function variables -//--------------------------------------------------------------------------- - -static bool isOp(const TOKEN *tok) -{ - return bool(tok && - (tok->str() == "&&" || - tok->str() == "||" || - tok->str() == "==" || - tok->str() == "!=" || - tok->str() == "<" || - tok->str() == "<=" || - tok->str() == ">" || - tok->str() == ">=" || - tok->str() == "<<" || - TOKEN::Match(tok, "[+-*/&|,[]]"))); -} - -void CheckOther::functionVariableUsage() -{ - // Parse all executing scopes.. - const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), ") const| {" ); - while ( tok1 ) - { - // Varname, usage {1=declare, 2=read, 4=write} - std::map varUsage; - static const unsigned int USAGE_DECLARE = 1; - static const unsigned int USAGE_READ = 2; - static const unsigned int USAGE_WRITE = 4; - - int indentlevel = 0; - for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) - { - if ( tok->str() == "{" ) - ++indentlevel; - else if ( tok->str() == "}" ) - { - --indentlevel; - if ( indentlevel <= 0 ) - break; - } - - if ( TOKEN::Match(tok, "[;{}] bool|char|short|int|long|float|double %var% ;|=") ) - varUsage[ tok->strAt(2) ] = USAGE_DECLARE; - - if ( TOKEN::Match(tok, "[;{}] bool|char|short|int|long|float|double * %var% ;|=") ) - varUsage[ tok->strAt(3) ] = USAGE_DECLARE; - - if ( TOKEN::Match(tok, "delete|return %var%") ) - varUsage[ tok->strAt(1) ] |= USAGE_READ; - - if ( TOKEN::Match(tok, "%var% =") ) - varUsage[ tok->str() ] |= USAGE_WRITE; - - if ( TOKEN::Match(tok, "else %var% =") ) - varUsage[ tok->strAt(1) ] |= USAGE_WRITE; - - if ( TOKEN::Match(tok, ">>|& %var%") ) - varUsage[ tok->strAt(1) ] |= USAGE_WRITE; - - if ((TOKEN::Match(tok,"[(=&!]") || isOp(tok)) && TOKEN::Match(tok->next(), "%var%")) - varUsage[ tok->strAt(1) ] |= USAGE_READ; - - if (TOKEN::Match(tok, "-=|+=|*=|/= %var%")) - varUsage[ tok->strAt(1) ] |= USAGE_READ; - - if (TOKEN::Match(tok, "%var%") && (tok->next()->str()==")" || isOp(tok->next()))) - varUsage[ tok->str() ] |= USAGE_READ; - - if ( TOKEN::Match(tok, "[(,] %var% [,)]") ) - varUsage[ tok->strAt(1) ] |= USAGE_WRITE; - } - - // Check usage of all variables in the current scope.. - for ( std::map::const_iterator it = varUsage.begin(); it != varUsage.end(); ++it ) - { - std::string varname = it->first; - unsigned int usage = it->second; - - if (!std::isalpha(varname[0])) - continue; - - if ( ! ( usage & USAGE_DECLARE ) ) - continue; - - if ( usage == USAGE_DECLARE ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok1->next()) << ": Unused variable '" << varname << "'"; - _errorLogger->reportErr(errmsg.str()); - } - - else if ( ! (usage & USAGE_READ) ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok1->next()) << ": Variable '" << varname << "' is assigned a value that is never used"; - _errorLogger->reportErr(errmsg.str()); - } - - else if ( ! (usage & USAGE_WRITE) ) - { - std::ostringstream errmsg; - errmsg << _tokenizer->fileLine(tok1->next()) << ": Variable '" << varname << "' is not assigned a value"; - _errorLogger->reportErr(errmsg.str()); - } - } - - // Goto next executing scope.. - tok1 = TOKEN::findmatch( tok1->next(), ") const| {" ); - } -} + + + +//--------------------------------------------------------------------------- +// if (condition) .... +//--------------------------------------------------------------------------- + +void CheckOther::WarningIf() +{ + + // Search for 'if (condition);' + for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (tok->str() == "if") + { + 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 (strcmp(tok2->strAt(1), ";") == 0 && + strcmp(tok2->strAt(2), "else") != 0) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Found \"if (condition);\""; + _errorLogger->reportErr(ostr.str()); + } + break; + } + } + } + } + } + + // 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. + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok->tokAt(4)) << ": The condition is always "; + for (int i = 0; i < 6; i++) + { + if (strcmp(cond, p[i]) == 0) + ostr << (i < 3 ? "True" : "False"); + } + _errorLogger->reportErr(ostr.str()); + } +} +//--------------------------------------------------------------------------- + + + + +//--------------------------------------------------------------------------- +// strtol(str, 0, radix) <- radix must be 0 or 2-36 +//--------------------------------------------------------------------------- + +void CheckOther::InvalidFunctionUsage() +{ + 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))) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok2) << ": Invalid radix in call to strtol or strtoul. Must be 0 or 2-36"; + _errorLogger->reportErr(ostr.str()); + } + } + break; + } + } + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Assignment in condition +//--------------------------------------------------------------------------- + +void CheckOther::CheckIfAssignment() +{ + for (const TOKEN *tok = _tokenizer->tokens(); tok; tok = tok->next()) + { + if (TOKEN::Match(tok, "if ( %var% = %num% )") || + TOKEN::Match(tok, "if ( %var% = %str% )") || + TOKEN::Match(tok, "if ( %var% = %var% )") ) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok) << ": Possible bug. Should it be '==' instead of '='?"; + _errorLogger->reportErr(ostr.str()); + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// 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%")) + { + 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.. + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok->next()) << ": Warning: Division with signed and unsigned operators"; + _errorLogger->reportErr(ostr.str()); + } + } + + else if (!TOKEN::Match(tok,"[).]") && TOKEN::Match(tok->next(), "%var% / - %num%")) + { + const char *varname1 = tok->strAt(1); + char sign1 = varsign[varname1]; + if ( sign1 == 'u' ) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok->next()) << ": Unsigned division. The result will be wrong."; + _errorLogger->reportErr(ostr.str()); + } + } + + else if (TOKEN::Match(tok, "[([=*/+-] - %num% / %var%")) + { + const char *varname2 = tok->strAt(4); + char sign2 = varsign[varname2]; + if ( sign2 == 'u' ) + { + std::ostringstream ostr; + ostr << _tokenizer->fileLine(tok->next()) << ": Unsigned division. The result will be wrong."; + _errorLogger->reportErr(ostr.str()); + } + } + } +} +//--------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// 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 _indentlevel = 0; + for (tok = tok2; tok; tok = tok->next()) + { + if ( tok->str() == "{" ) + { + _indentlevel++; + } + if ( tok->str() == "}" ) + { + _indentlevel--; + if ( _indentlevel <= 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 + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok1) << " The scope of the variable '" << varname << "' can be limited"; + _errorLogger->reportErr( errmsg.str() ); +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// 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% [,)]") ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(5) << " is passed by value, it could be passed by reference/pointer instead"; + _errorLogger->reportErr( errmsg.str() ); + } + + else if ( TOKEN::Match(tok,"[,(] const %type% %var% [,)]") ) + { + // Check if type is a struct or class. + const char *pattern[3] = {"class","type",0}; + pattern[1] = tok->strAt( 2); + if ( TOKEN::findtoken(_tokenizer->tokens(), pattern) ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(3) << " is passed by value, it could be passed by reference/pointer instead"; + _errorLogger->reportErr( errmsg.str() ); + } + pattern[0] = "struct"; + if ( TOKEN::findtoken(_tokenizer->tokens(), pattern) ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok) << " " << tok->strAt(3) << " is passed by value, it could be passed by reference/pointer instead"; + _errorLogger->reportErr( errmsg.str() ); + } + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// 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 ( tok->str() == "}" ) + structname = 0; + if ( TOKEN::Match(tok, "struct %type% {") ) + structname = tok->strAt( 1); + + 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( 2 ); + 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 char *varnames[2]; + varnames[0] = varname; + varnames[1] = 0; + bool used = false; + for ( const TOKEN *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next() ) + { + if ( tok->fileIndex() != 0 ) + continue; + + if (TOKEN::Match(tok2, ". %var%", varnames)) + { + if ( strcmp("=", tok2->strAt(2)) == 0 ) + continue; + used = true; + break; + } + } + + if ( ! used ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok) << ": struct member '" << structname << "::" << varname << "' is never read"; + _errorLogger->reportErr(errmsg.str()); + } + } + } +} + + + + + +//--------------------------------------------------------------------------- +// 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% [;=,)]") ) + { + const char *varname[2] = {0}; + varname[0] = tok->strAt( 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; + } + + if ((tok2->str() != ".") && TOKEN::Match(tok2->next(), "%var% [ %var1% ]", varname)) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok2->next()) << ": Warning - using char variable as array index"; + _errorLogger->reportErr(errmsg.str()); + break; + } + + if ( TOKEN::Match(tok2, "%var% [&|] %var1%", varname) || TOKEN::Match(tok2, "%var1% [&|]", varname) ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok2) << ": Warning - using char variable in bit operation"; + _errorLogger->reportErr(errmsg.str()); + 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), ",") ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok->next()) << ": Redundant code: Found a statement that begins with string constant"; + _errorLogger->reportErr(errmsg.str()); + } + + if ( !TOKEN::Match(tok,"#") && TOKEN::Match(tok->next(),"; %num%") && !TOKEN::Match(tok->tokAt(3), ",") ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok->next()) << ": Redundant code: Found a statement that begins with numeric constant"; + _errorLogger->reportErr(errmsg.str()); + } + } +} +//--------------------------------------------------------------------------- + + + + + + +//--------------------------------------------------------------------------- +// Unreachable code below a 'return' +//--------------------------------------------------------------------------- + +void CheckOther::unreachableCode() +{ + const TOKEN *tok = TOKEN::findmatch( _tokenizer->tokens(), "[;{}] return" ); + while ( tok ) + { + // Goto the 'return' token + tok = tok->next(); + + // Locate the end of the 'return' statement + while ( tok && ! TOKEN::Match(tok, ";") ) + tok = tok->next(); + while ( tok && TOKEN::Match(tok->next(), ";") ) + tok = tok->next(); + + // If there is a statement below the return it is unreachable + if (!TOKEN::Match(tok, "; case|default|}|#") && !TOKEN::Match(tok, "; %var% :")) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok->next()) << ": Unreachable code below a 'return'"; + _errorLogger->reportErr(errmsg.str()); + } + + // Find the next 'return' statement + tok = TOKEN::findmatch( tok, "[;{}] return" ); + } +} +//--------------------------------------------------------------------------- + + + + + + +//--------------------------------------------------------------------------- +// Usage of function variables +//--------------------------------------------------------------------------- + +static bool isOp(const TOKEN *tok) +{ + return bool(tok && + (tok->str() == "&&" || + tok->str() == "||" || + tok->str() == "==" || + tok->str() == "!=" || + tok->str() == "<" || + tok->str() == "<=" || + tok->str() == ">" || + tok->str() == ">=" || + tok->str() == "<<" || + TOKEN::Match(tok, "[+-*/&|,[]]"))); +} + +void CheckOther::functionVariableUsage() +{ + // Parse all executing scopes.. + const TOKEN *tok1 = TOKEN::findmatch( _tokenizer->tokens(), ") const| {" ); + while ( tok1 ) + { + // Varname, usage {1=declare, 2=read, 4=write} + std::map varUsage; + static const unsigned int USAGE_DECLARE = 1; + static const unsigned int USAGE_READ = 2; + static const unsigned int USAGE_WRITE = 4; + + int indentlevel = 0; + for ( const TOKEN *tok = tok1; tok; tok = tok->next() ) + { + if ( tok->str() == "{" ) + ++indentlevel; + else if ( tok->str() == "}" ) + { + --indentlevel; + if ( indentlevel <= 0 ) + break; + } + + if ( TOKEN::Match(tok, "[;{}] bool|char|short|int|long|float|double %var% ;|=") ) + varUsage[ tok->strAt(2) ] = USAGE_DECLARE; + + if ( TOKEN::Match(tok, "[;{}] bool|char|short|int|long|float|double * %var% ;|=") ) + varUsage[ tok->strAt(3) ] = USAGE_DECLARE; + + if ( TOKEN::Match(tok, "delete|return %var%") ) + varUsage[ tok->strAt(1) ] |= USAGE_READ; + + if ( TOKEN::Match(tok, "%var% =") ) + varUsage[ tok->str() ] |= USAGE_WRITE; + + if ( TOKEN::Match(tok, "else %var% =") ) + varUsage[ tok->strAt(1) ] |= USAGE_WRITE; + + if ( TOKEN::Match(tok, ">>|& %var%") ) + varUsage[ tok->strAt(1) ] |= USAGE_WRITE; + + if ((TOKEN::Match(tok,"[(=&!]") || isOp(tok)) && TOKEN::Match(tok->next(), "%var%")) + varUsage[ tok->strAt(1) ] |= USAGE_READ; + + if (TOKEN::Match(tok, "-=|+=|*=|/= %var%")) + varUsage[ tok->strAt(1) ] |= USAGE_READ; + + if (TOKEN::Match(tok, "%var%") && (tok->next()->str()==")" || isOp(tok->next()))) + varUsage[ tok->str() ] |= USAGE_READ; + + if ( TOKEN::Match(tok, "[(,] %var% [,)]") ) + varUsage[ tok->strAt(1) ] |= USAGE_WRITE; + } + + // Check usage of all variables in the current scope.. + for ( std::map::const_iterator it = varUsage.begin(); it != varUsage.end(); ++it ) + { + std::string varname = it->first; + unsigned int usage = it->second; + + if (!std::isalpha(varname[0])) + continue; + + if ( ! ( usage & USAGE_DECLARE ) ) + continue; + + if ( usage == USAGE_DECLARE ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok1->next()) << ": Unused variable '" << varname << "'"; + _errorLogger->reportErr(errmsg.str()); + } + + else if ( ! (usage & USAGE_READ) ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok1->next()) << ": Variable '" << varname << "' is assigned a value that is never used"; + _errorLogger->reportErr(errmsg.str()); + } + + else if ( ! (usage & USAGE_WRITE) ) + { + std::ostringstream errmsg; + errmsg << _tokenizer->fileLine(tok1->next()) << ": Variable '" << varname << "' is not assigned a value"; + _errorLogger->reportErr(errmsg.str()); + } + } + + // Goto next executing scope.. + tok1 = TOKEN::findmatch( tok1->next(), ") const| {" ); + } +} diff --git a/checkother.h b/checkother.h index c7f8ec025..4ccface04 100644 --- a/checkother.h +++ b/checkother.h @@ -1,90 +1,90 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -//--------------------------------------------------------------------------- - -CppCheck::CppCheck( ErrorLogger &errorLogger ) : _checkFunctionUsage( this ) -{ - _errorLogger = &errorLogger; -} - -CppCheck::~CppCheck() -{ - -} - -void CppCheck::settings( const Settings &settings ) -{ - _settings = settings; -} - -void CppCheck::addFile( const std::string &path ) -{ - _filenames.push_back( path ); -} - -void CppCheck::addFile( const std::string &path, const std::string &content ) -{ - _filenames.push_back( path ); - _fileContents[ path ] = content; -} - -std::string CppCheck::parseFromArgs( int argc, const char* const argv[] ) -{ - std::vector pathnames; - bool Recursive = false; - - for (int i = 1; i < argc; i++) - { - // Flag used for various purposes during debugging - if (strcmp(argv[i],"--debug") == 0) - _settings._debug = true; - - // Show all messages - else if (strcmp(argv[i],"--all") == 0) - _settings._showAll = true; - - // Only print something when there are errors - else if (strcmp(argv[i],"--errorsonly")==0) - _settings._errorsOnly = true; - - // Checking coding style - else if (strcmp(argv[i],"--style")==0) - _settings._checkCodingStyle = true; - - // Recursively check source files - else if (strcmp(argv[i],"--recursive")==0) - Recursive = true; - - // Verbose error messages (configuration info) - else if (strcmp(argv[i],"--verbose")==0) - _settings._verbose = true; - - else - pathnames.push_back( argv[i] ); - } - - // --recursive was used - if ( Recursive ) - { - if( pathnames.size() == 0 ) - { - // Handle situation: cppcheck --recursive - FileLister::RecursiveAddFiles( _filenames, "", true ); - } - else - { - // Handle situation: cppcheck --recursive path1 path2 - - // Execute RecursiveAddFiles() to each given file parameter - std::vector::const_iterator iter; - for(iter=pathnames.begin(); iter!=pathnames.end(); iter++) - FileLister::RecursiveAddFiles( _filenames, iter->c_str(), true ); - } - } - else - { - std::vector::const_iterator iter; - for(iter=pathnames.begin(); iter!=pathnames.end(); iter++) - FileLister::RecursiveAddFiles( _filenames, iter->c_str(), false ); - } - - if (_filenames.empty()) - { - std::ostringstream oss; - oss << "c++check 1.26\n" - "\n" - "C/C++ code checking\n" - "\n" - "Syntax:\n" - " cppcheck [--all] [--errorsonly] [--recursive] [--style] [--verbose] [filename1] [filename2]\n" - "\n" - "Options:\n" - " --all Make the checking more sensitive. More bugs are detected,\n" - " but there are also more false positives\n" - " --errorsonly Only print error messages\n" - " --recursive Recursively check all *.cpp, *.cxx, *.cc and *.c files\n" - " --style Check coding style\n" - " --verbose More detailed error reports\n"; - return oss.str(); - } - - // Check function usage if "--style" and "--all" was given. - // There will be false positives for exported library functions - if ( _settings._showAll && _settings._checkCodingStyle ) - _settings._checkFunctionUsage = true; - - return ""; -} - -void CppCheck::check() -{ - std::sort( _filenames.begin(), _filenames.end() ); - for (unsigned int c = 0; c < _filenames.size(); c++) - { - _errout.str(""); - std::string fname = _filenames[c]; - - // If only errors are printed, print filename after the check - if ( _settings._errorsOnly == false ) - _errorLogger->reportOut( std::string( "Checking " ) + fname + std::string( "..." ) ); - - Preprocessor preprocessor; - std::map code; - if( _fileContents.size() > 0 && _fileContents.find( _filenames[c] ) != _fileContents.end() ) - { - // File content was given as a string - std::istringstream iss( _fileContents[ _filenames[c] ] ); - preprocessor.preprocess(iss, code, fname); - } - else - { - // Only file name was given, read the content from file - std::ifstream fin( fname.c_str() ); - preprocessor.preprocess(fin, code, fname); - } - - for ( std::map::const_iterator it = code.begin(); it != code.end(); ++it ) - { - cfg = it->first; - checkFile(it->second, _filenames[c].c_str()); - } - - if ( _settings._errorsOnly == false && _errout.str().empty() ) - _errorLogger->reportOut( "No errors found" ); - } - - // This generates false positives - especially for libraries - _settings._verbose = false; - if ( _settings._checkFunctionUsage ) - { - _errout.str(""); - if( _settings._errorsOnly == false ) - _errorLogger->reportOut( "Checking usage of global functions (this may take several minutes).." ); - - _checkFunctionUsage.check(); - } - - _errorList.clear(); -} - - -//--------------------------------------------------------------------------- -// CppCheck - A function that checks a specified file -//--------------------------------------------------------------------------- - -void CppCheck::checkFile(const std::string &code, const char FileName[]) -{ - Tokenizer _tokenizer; - - // Tokenize the file - { - std::istringstream istr(code); - _tokenizer.tokenize(istr, FileName); - } - - // Set variable id - _tokenizer.setVarId(); - - _tokenizer.fillFunctionList(); - - // Check that the memsets are valid. - // The 'memset' function can do dangerous things if used wrong. - // Important: The checking doesn't work on simplified tokens list. - CheckClass checkClass( &_tokenizer, _settings, this ); - checkClass.CheckMemset(); - - - // Check for unsigned divisions where one operand is signed - // Very important to run it before 'SimplifyTokenList' - CheckOther checkOther( &_tokenizer, this ); - if ( _settings._checkCodingStyle ) - checkOther.CheckUnsignedDivision(); - - // Give warning when using char variable as array index - // Doesn't work on simplified token list ('unsigned') - if ( _settings._checkCodingStyle ) - checkOther.CheckCharVariable(); - - - // Including header which is not needed (too many false positives) -// if ( _settings._checkCodingStyle ) -// { -// CheckHeaders checkHeaders( &tokenizer ); -// checkHeaders.WarningIncludeHeader(); -// } - - - _tokenizer.simplifyTokenList(); - - - if ( _settings._checkFunctionUsage ) - _checkFunctionUsage.parseTokens(_tokenizer); - - // Memory leak - CheckMemoryLeakClass checkMemoryLeak( &_tokenizer, _settings, this ); - checkMemoryLeak.CheckMemoryLeak(); - - // Buffer overruns.. - CheckBufferOverrunClass checkBufferOverrun( &_tokenizer, _settings, this ); - checkBufferOverrun.CheckBufferOverrun(); - - // Check that all class constructors are ok. - checkClass.CheckConstructors(); - - // Check that all base classes have virtual destructors - checkClass.virtualDestructor(); - - if (_settings._showAll) - { - // Check for "if (a=b)" - checkOther.CheckIfAssignment(); - - // Check for case without break - // Disabled because it generates many false positives - // CheckCaseWithoutBreak(); - - // Dangerous usage of strtok - // Disabled because it generates false positives - //WarningStrTok(); - } - - - - // Dangerous functions, such as 'gets' and 'scanf' - checkBufferOverrun.WarningDangerousFunctions(); - - - // Invalid function usage.. - checkOther.InvalidFunctionUsage(); - - - if (_settings._checkCodingStyle) - { - // Check that all private functions are called. - checkClass.CheckUnusedPrivateFunctions(); - - // Warning upon c-style pointer casts - const char *ext = strrchr(FileName, '.'); - if (ext && strcmp(ext,".cpp")==0) - checkOther.WarningOldStylePointerCast(); - - checkClass.CheckOperatorEq1(); - - // if (a) delete a; - checkOther.WarningRedundantCode(); - - // if (condition); - checkOther.WarningIf(); - - // Variable scope (check if the scope could be limited) - //CheckVariableScope(); - - // Check if a constant function parameter is passed by value - checkOther.CheckConstantFunctionParameter(); - - // Unused struct members.. - checkOther.CheckStructMemberUsage(); - - // Check for various types of incomplete statements that could for example - // mean that an ';' has been added by accident - checkOther.CheckIncompleteStatement(); - - // Unreachable code below a 'return' statement - checkOther.unreachableCode(); - - // Usage of local functions - checkOther.functionVariableUsage(); - } -} -//--------------------------------------------------------------------------- - -void CppCheck::reportErr( const std::string &errmsg) -{ - if ( /*OnlyReportUniqueErrors*/ true ) - { - if ( std::find( _errorList.begin(), _errorList.end(), errmsg ) != _errorList.end() ) - return; - _errorList.push_back( errmsg ); - } - - std::string errmsg2( errmsg ); - if ( _settings._verbose ) - { - errmsg2 += "\n Defines=\'" + cfg + "\'\n"; - } - - - _errorLogger->reportErr( errmsg2 ); - - _errout << errmsg2 << std::endl; -} - -void CppCheck::reportOut( const std::string &outmsg) -{ - // This is currently never called. It is here just to comply with - // the interface. -} +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +//--------------------------------------------------------------------------- + +CppCheck::CppCheck( ErrorLogger &errorLogger ) : _checkFunctionUsage( this ) +{ + _errorLogger = &errorLogger; +} + +CppCheck::~CppCheck() +{ + +} + +void CppCheck::settings( const Settings &settings ) +{ + _settings = settings; +} + +void CppCheck::addFile( const std::string &path ) +{ + _filenames.push_back( path ); +} + +void CppCheck::addFile( const std::string &path, const std::string &content ) +{ + _filenames.push_back( path ); + _fileContents[ path ] = content; +} + +std::string CppCheck::parseFromArgs( int argc, const char* const argv[] ) +{ + std::vector pathnames; + bool Recursive = false; + + for (int i = 1; i < argc; i++) + { + // Flag used for various purposes during debugging + if (strcmp(argv[i],"--debug") == 0) + _settings._debug = true; + + // Show all messages + else if (strcmp(argv[i],"--all") == 0) + _settings._showAll = true; + + // Only print something when there are errors + else if (strcmp(argv[i],"--errorsonly")==0) + _settings._errorsOnly = true; + + // Checking coding style + else if (strcmp(argv[i],"--style")==0) + _settings._checkCodingStyle = true; + + // Recursively check source files + else if (strcmp(argv[i],"--recursive")==0) + Recursive = true; + + // Verbose error messages (configuration info) + else if (strcmp(argv[i],"--verbose")==0) + _settings._verbose = true; + + else + pathnames.push_back( argv[i] ); + } + + // --recursive was used + if ( Recursive ) + { + if( pathnames.size() == 0 ) + { + // Handle situation: cppcheck --recursive + FileLister::RecursiveAddFiles( _filenames, "", true ); + } + else + { + // Handle situation: cppcheck --recursive path1 path2 + + // Execute RecursiveAddFiles() to each given file parameter + std::vector::const_iterator iter; + for(iter=pathnames.begin(); iter!=pathnames.end(); iter++) + FileLister::RecursiveAddFiles( _filenames, iter->c_str(), true ); + } + } + else + { + std::vector::const_iterator iter; + for(iter=pathnames.begin(); iter!=pathnames.end(); iter++) + FileLister::RecursiveAddFiles( _filenames, iter->c_str(), false ); + } + + if (_filenames.empty()) + { + std::ostringstream oss; + oss << "c++check 1.26\n" + "\n" + "C/C++ code checking\n" + "\n" + "Syntax:\n" + " cppcheck [--all] [--errorsonly] [--recursive] [--style] [--verbose] [filename1] [filename2]\n" + "\n" + "Options:\n" + " --all Make the checking more sensitive. More bugs are detected,\n" + " but there are also more false positives\n" + " --errorsonly Only print error messages\n" + " --recursive Recursively check all *.cpp, *.cxx, *.cc and *.c files\n" + " --style Check coding style\n" + " --verbose More detailed error reports\n"; + return oss.str(); + } + + // Check function usage if "--style" and "--all" was given. + // There will be false positives for exported library functions + if ( _settings._showAll && _settings._checkCodingStyle ) + _settings._checkFunctionUsage = true; + + return ""; +} + +void CppCheck::check() +{ + std::sort( _filenames.begin(), _filenames.end() ); + for (unsigned int c = 0; c < _filenames.size(); c++) + { + _errout.str(""); + std::string fname = _filenames[c]; + + // If only errors are printed, print filename after the check + if ( _settings._errorsOnly == false ) + _errorLogger->reportOut( std::string( "Checking " ) + fname + std::string( "..." ) ); + + Preprocessor preprocessor; + std::map code; + if( _fileContents.size() > 0 && _fileContents.find( _filenames[c] ) != _fileContents.end() ) + { + // File content was given as a string + std::istringstream iss( _fileContents[ _filenames[c] ] ); + preprocessor.preprocess(iss, code, fname); + } + else + { + // Only file name was given, read the content from file + std::ifstream fin( fname.c_str() ); + preprocessor.preprocess(fin, code, fname); + } + + for ( std::map::const_iterator it = code.begin(); it != code.end(); ++it ) + { + cfg = it->first; + checkFile(it->second, _filenames[c].c_str()); + } + + if ( _settings._errorsOnly == false && _errout.str().empty() ) + _errorLogger->reportOut( "No errors found" ); + } + + // This generates false positives - especially for libraries + _settings._verbose = false; + if ( _settings._checkFunctionUsage ) + { + _errout.str(""); + if( _settings._errorsOnly == false ) + _errorLogger->reportOut( "Checking usage of global functions (this may take several minutes).." ); + + _checkFunctionUsage.check(); + } + + _errorList.clear(); +} + + +//--------------------------------------------------------------------------- +// CppCheck - A function that checks a specified file +//--------------------------------------------------------------------------- + +void CppCheck::checkFile(const std::string &code, const char FileName[]) +{ + Tokenizer _tokenizer; + + // Tokenize the file + { + std::istringstream istr(code); + _tokenizer.tokenize(istr, FileName); + } + + // Set variable id + _tokenizer.setVarId(); + + _tokenizer.fillFunctionList(); + + // Check that the memsets are valid. + // The 'memset' function can do dangerous things if used wrong. + // Important: The checking doesn't work on simplified tokens list. + CheckClass checkClass( &_tokenizer, _settings, this ); + checkClass.CheckMemset(); + + + // Check for unsigned divisions where one operand is signed + // Very important to run it before 'SimplifyTokenList' + CheckOther checkOther( &_tokenizer, this ); + if ( _settings._checkCodingStyle ) + checkOther.CheckUnsignedDivision(); + + // Give warning when using char variable as array index + // Doesn't work on simplified token list ('unsigned') + if ( _settings._checkCodingStyle ) + checkOther.CheckCharVariable(); + + + // Including header which is not needed (too many false positives) +// if ( _settings._checkCodingStyle ) +// { +// CheckHeaders checkHeaders( &tokenizer ); +// checkHeaders.WarningIncludeHeader(); +// } + + + _tokenizer.simplifyTokenList(); + + + if ( _settings._checkFunctionUsage ) + _checkFunctionUsage.parseTokens(_tokenizer); + + // Memory leak + CheckMemoryLeakClass checkMemoryLeak( &_tokenizer, _settings, this ); + checkMemoryLeak.CheckMemoryLeak(); + + // Buffer overruns.. + CheckBufferOverrunClass checkBufferOverrun( &_tokenizer, _settings, this ); + checkBufferOverrun.CheckBufferOverrun(); + + // Check that all class constructors are ok. + checkClass.CheckConstructors(); + + // Check that all base classes have virtual destructors + checkClass.virtualDestructor(); + + if (_settings._showAll) + { + // Check for "if (a=b)" + checkOther.CheckIfAssignment(); + + // Check for case without break + // Disabled because it generates many false positives + // CheckCaseWithoutBreak(); + + // Dangerous usage of strtok + // Disabled because it generates false positives + //WarningStrTok(); + } + + + + // Dangerous functions, such as 'gets' and 'scanf' + checkBufferOverrun.WarningDangerousFunctions(); + + + // Invalid function usage.. + checkOther.InvalidFunctionUsage(); + + + if (_settings._checkCodingStyle) + { + // Check that all private functions are called. + checkClass.CheckUnusedPrivateFunctions(); + + // Warning upon c-style pointer casts + const char *ext = strrchr(FileName, '.'); + if (ext && strcmp(ext,".cpp")==0) + checkOther.WarningOldStylePointerCast(); + + checkClass.CheckOperatorEq1(); + + // if (a) delete a; + checkOther.WarningRedundantCode(); + + // if (condition); + checkOther.WarningIf(); + + // Variable scope (check if the scope could be limited) + //CheckVariableScope(); + + // Check if a constant function parameter is passed by value + checkOther.CheckConstantFunctionParameter(); + + // Unused struct members.. + checkOther.CheckStructMemberUsage(); + + // Check for various types of incomplete statements that could for example + // mean that an ';' has been added by accident + checkOther.CheckIncompleteStatement(); + + // Unreachable code below a 'return' statement + checkOther.unreachableCode(); + + // Usage of local functions + checkOther.functionVariableUsage(); + } +} +//--------------------------------------------------------------------------- + +void CppCheck::reportErr( const std::string &errmsg) +{ + if ( /*OnlyReportUniqueErrors*/ true ) + { + if ( std::find( _errorList.begin(), _errorList.end(), errmsg ) != _errorList.end() ) + return; + _errorList.push_back( errmsg ); + } + + std::string errmsg2( errmsg ); + if ( _settings._verbose ) + { + errmsg2 += "\n Defines=\'" + cfg + "\'\n"; + } + + + _errorLogger->reportErr( errmsg2 ); + + _errout << errmsg2 << std::endl; +} + +void CppCheck::reportOut( const std::string &outmsg) +{ + // This is currently never called. It is here just to comply with + // the interface. +} diff --git a/cppcheck.h b/cppcheck.h index 8f5a6204e..72e88a23e 100644 --- a/cppcheck.h +++ b/cppcheck.h @@ -1,126 +1,126 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 "settings.h" -#include "errorlogger.h" -#include "checkfunctionusage.h" - -/** - * This is the base class which will use other classes to do - * static code analysis for C and C++ code to find possible - * errors or places that could be improved. - * Usage: See check() for more info. - */ -class CppCheck : public ErrorLogger -{ - public: - /** - * Constructor. - */ - CppCheck( ErrorLogger &errorLogger ); - - /** - * Destructor. - */ - virtual ~CppCheck(); - - /** - * This starts the actual checking. Note that you must call - * parseFromArgs() or settings() and addFile() before calling this. - */ - void check(); - - /** - * Adjust the settings before doing the check. E.g. show only - * actual bugs or also coding style issues. - * - * @param settings New settings which will overwrite the old. - */ - void settings( const Settings &settings ); - - /** - * Add new file to be checked. - * - * @param path Relative or absolute path to the file to be checked, - * e.g. "cppcheck.cpp". Note that only source files (.c, .cc or .cpp) - * should be added to the list. Include filese are gathered automatically. - */ - void addFile( const std::string &path ); - - /** - * Add new unreal file to be checked. - * - * @param path File name (used for error reporting). - * @param content If the file would be a real file, this should be - * the content of the file. - */ - void addFile( const std::string &path, const std::string &content ); - - /** - * Parse command line args and get settings and file lists - * from there. - * - * @param argc argc from main() - * @param argv argv from main() - * @return Empty string if parameters were accepted, or - * string containing "help" text if no files were found to be - * checked. - */ - std::string parseFromArgs( int argc, const char* const argv[] ); - - private: - void checkFile(const std::string &code, const char FileName[]); - - /** - * Errors and warnings are directed here. - * - * @param errmsg Errors messages are normally in format - * "[filepath:line number] Message", e.g. - * "[main.cpp:4] Uninitialized member variable" - */ - virtual void reportErr( const std::string &errmsg); - - /** - * Information about progress is directed here. - * - * @param outmsg, E.g. "Checking main.cpp..." - */ - virtual void reportOut( const std::string &outmsg); - - std::list _errorList; - std::ostringstream _errout; - Settings _settings; - std::vector _filenames; - /** Key is file name, and value is the content of the file */ - std::map _fileContents; - CheckFunctionUsage _checkFunctionUsage; - ErrorLogger *_errorLogger; - - /** Current configuration */ - std::string cfg; -}; - -#endif // CPPCHECK_H +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 "settings.h" +#include "errorlogger.h" +#include "checkfunctionusage.h" + +/** + * This is the base class which will use other classes to do + * static code analysis for C and C++ code to find possible + * errors or places that could be improved. + * Usage: See check() for more info. + */ +class CppCheck : public ErrorLogger +{ + public: + /** + * Constructor. + */ + CppCheck( ErrorLogger &errorLogger ); + + /** + * Destructor. + */ + virtual ~CppCheck(); + + /** + * This starts the actual checking. Note that you must call + * parseFromArgs() or settings() and addFile() before calling this. + */ + void check(); + + /** + * Adjust the settings before doing the check. E.g. show only + * actual bugs or also coding style issues. + * + * @param settings New settings which will overwrite the old. + */ + void settings( const Settings &settings ); + + /** + * Add new file to be checked. + * + * @param path Relative or absolute path to the file to be checked, + * e.g. "cppcheck.cpp". Note that only source files (.c, .cc or .cpp) + * should be added to the list. Include filese are gathered automatically. + */ + void addFile( const std::string &path ); + + /** + * Add new unreal file to be checked. + * + * @param path File name (used for error reporting). + * @param content If the file would be a real file, this should be + * the content of the file. + */ + void addFile( const std::string &path, const std::string &content ); + + /** + * Parse command line args and get settings and file lists + * from there. + * + * @param argc argc from main() + * @param argv argv from main() + * @return Empty string if parameters were accepted, or + * string containing "help" text if no files were found to be + * checked. + */ + std::string parseFromArgs( int argc, const char* const argv[] ); + + private: + void checkFile(const std::string &code, const char FileName[]); + + /** + * Errors and warnings are directed here. + * + * @param errmsg Errors messages are normally in format + * "[filepath:line number] Message", e.g. + * "[main.cpp:4] Uninitialized member variable" + */ + virtual void reportErr( const std::string &errmsg); + + /** + * Information about progress is directed here. + * + * @param outmsg, E.g. "Checking main.cpp..." + */ + virtual void reportOut( const std::string &outmsg); + + std::list _errorList; + std::ostringstream _errout; + Settings _settings; + std::vector _filenames; + /** Key is file name, and value is the content of the file */ + std::map _fileContents; + CheckFunctionUsage _checkFunctionUsage; + ErrorLogger *_errorLogger; + + /** Current configuration */ + std::string cfg; +}; + +#endif // CPPCHECK_H diff --git a/cppcheckexecutor.cpp b/cppcheckexecutor.cpp index ade1f7a6e..ae058ac20 100644 --- a/cppcheckexecutor.cpp +++ b/cppcheckexecutor.cpp @@ -1,51 +1,51 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -CppCheckExecutor::CppCheckExecutor() -{ - //ctor -} - -CppCheckExecutor::~CppCheckExecutor() -{ - //dtor -} - -void CppCheckExecutor::check( int argc, const char* const argv[] ) -{ - CppCheck cppCheck( *this ); - std::string result = cppCheck.parseFromArgs( argc, argv ); - if( result.length() == 0 ) - cppCheck.check(); - else - std::cout << result; -} - -void CppCheckExecutor::reportErr( const std::string &errmsg) -{ - std::cerr << errmsg << std::endl; -} - -void CppCheckExecutor::reportOut( const std::string &outmsg) -{ - std::cout << outmsg << std::endl; -} +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +CppCheckExecutor::CppCheckExecutor() +{ + //ctor +} + +CppCheckExecutor::~CppCheckExecutor() +{ + //dtor +} + +void CppCheckExecutor::check( int argc, const char* const argv[] ) +{ + CppCheck cppCheck( *this ); + std::string result = cppCheck.parseFromArgs( argc, argv ); + if( result.length() == 0 ) + cppCheck.check(); + else + std::cout << result; +} + +void CppCheckExecutor::reportErr( const std::string &errmsg) +{ + std::cerr << errmsg << std::endl; +} + +void CppCheckExecutor::reportOut( const std::string &outmsg) +{ + std::cout << outmsg << std::endl; +} diff --git a/cppcheckexecutor.h b/cppcheckexecutor.h index 167c1f3d8..cf6e2be87 100644 --- a/cppcheckexecutor.h +++ b/cppcheckexecutor.h @@ -1,71 +1,71 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -/** - * This is an interface, which the class responsible of error logging - * should implement. - */ -class ErrorLogger -{ -public: - virtual ~ErrorLogger() {} - - /** - * Errors and warnings are directed here. - * - * @param errmsg Errors messages are normally in format - * "[filepath:line number] Message", e.g. - * "[main.cpp:4] Uninitialized member variable" - */ - virtual void reportErr( const std::string &errmsg) = 0; - - /** - * Information about progress is directed here. - * - * @param outmsg, E.g. "Checking main.cpp..." - */ - virtual void reportOut( const std::string &outmsg) = 0; -}; - -#endif // #ifndef ERRORLOGGER_H +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +/** + * This is an interface, which the class responsible of error logging + * should implement. + */ +class ErrorLogger +{ +public: + virtual ~ErrorLogger() {} + + /** + * Errors and warnings are directed here. + * + * @param errmsg Errors messages are normally in format + * "[filepath:line number] Message", e.g. + * "[main.cpp:4] Uninitialized member variable" + */ + virtual void reportErr( const std::string &errmsg) = 0; + + /** + * Information about progress is directed here. + * + * @param outmsg, E.g. "Checking main.cpp..." + */ + virtual void reportOut( const std::string &outmsg) = 0; +}; + +#endif // #ifndef ERRORLOGGER_H diff --git a/filelister.cpp b/filelister.cpp index efa8f20a3..d25198ee3 100644 --- a/filelister.cpp +++ b/filelister.cpp @@ -1,210 +1,210 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -#ifdef __GNUC__ -#include -#include -#endif -#if defined(__BORLANDC__) || defined(_MSC_VER) -#include -#endif - -std::string FileLister::simplifyPath( const char *originalPath ) -{ - std::string subPath = ""; - std::vector pathParts; - for( ; *originalPath; ++originalPath ) - { - if( *originalPath == '/' ) - { - if( subPath.length() > 0 ) - { - pathParts.push_back( subPath ); - subPath = ""; - } - - pathParts.push_back( "/" ); - } - else - subPath.append( 1, *originalPath ); - } - - if( subPath.length() > 0 ) - pathParts.push_back( subPath ); - - for( std::vector::size_type i = 0; i < pathParts.size(); ++i ) - { - if( pathParts[i] == ".." && i > 1 ) - { - pathParts.erase( pathParts.begin() + i ); - pathParts.erase( pathParts.begin()+i-1 ); - pathParts.erase( pathParts.begin()+i-2 ); - i = 0; - } - else if( i > 0 && pathParts[i] == "." ) - { - pathParts.erase( pathParts.begin()+i ); - i = 0; - } - else if( pathParts[i] == "/" && i > 0 && pathParts[i-1] == "/" ) - { - pathParts.erase( pathParts.begin()+i-1 ); - i = 0; - } - } - - std::ostringstream oss; - for( std::vector::size_type i = 0; i < pathParts.size(); ++i ) - { - oss << pathParts[i]; - } - - return oss.str(); -} - - - -bool FileLister::AcceptFile( const std::string &filename ) -{ - std::string::size_type dotLocation = filename.find_last_of ( '.' ); - if ( dotLocation == std::string::npos ) - return false; - - std::string extension = filename.substr( dotLocation ); - - if( extension == ".cpp" || - extension == ".cxx" || - extension == ".cc" || - extension == ".c" ) - { - return true; - } - - return false; -} - -/////////////////////////////////////////////////////////////////////////////// -////// This code is for __GNUC__ only ///////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -#ifdef __GNUC__ -// gcc / cygwin.. -void FileLister::RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ) -{ - std::ostringstream oss; - oss << path; - if( recursive ) - { - if ( path.length() > 0 && path[path.length()-1] != '/' ) - oss << "/"; - - oss << "*"; - } - - glob_t glob_results; - glob( oss.str().c_str(), GLOB_MARK, 0, &glob_results); - for ( unsigned int i = 0; i < glob_results.gl_pathc; i++ ) - { - std::string filename = glob_results.gl_pathv[i]; - if ( filename == "." || filename == ".." || filename.length() == 0 ) - continue; - - if ( filename[filename.length()-1] != '/' ) - { - // File - - // If recursive is not used, accept all files given by user - if( !recursive || FileLister::AcceptFile( filename ) ) - filenames.push_back( filename ); - } - else if( recursive ) - { - // Directory - FileLister::RecursiveAddFiles( filenames, filename, recursive ); - } - } - globfree(&glob_results); -} -#endif - - -/////////////////////////////////////////////////////////////////////////////// -////// This code is for Borland C++ and Visual C++ //////////////////////////// -/////////////////////////////////////////////////////////////////////////////// - -#if defined(__BORLANDC__) || defined(_MSC_VER) - -void FileLister::RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ) -{ - std::ostringstream bdir, oss; - oss << path; - if (recursive) - { - bdir << path; - if ( path.length() > 0 && path[path.length()-1] != '/' ) - { - bdir << "/"; - oss << "/"; - } - - oss << "*"; - } - - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(oss.str().c_str(), &ffd); - if ( INVALID_HANDLE_VALUE == hFind ) - return; - - do - { - std::ostringstream fname; - fname << bdir.str().c_str() << ffd.cFileName; - - if ( ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0' ) - continue; - - if ( ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) - { - // File - - // If recursive is not used, accept all files given by user - if ( !recursive || FileLister::AcceptFile( ffd.cFileName ) ) - filenames.push_back( fname.str() ); - } - else if ( recursive ) - { - // Directory - fname << "/"; - FileLister::RecursiveAddFiles( filenames, fname.str().c_str(), recursive ); - } - } - while (FindNextFile(hFind, &ffd) != FALSE); - - FindClose(hFind); -} - -#endif - - - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +#ifdef __GNUC__ +#include +#include +#endif +#if defined(__BORLANDC__) || defined(_MSC_VER) +#include +#endif + +std::string FileLister::simplifyPath( const char *originalPath ) +{ + std::string subPath = ""; + std::vector pathParts; + for( ; *originalPath; ++originalPath ) + { + if( *originalPath == '/' ) + { + if( subPath.length() > 0 ) + { + pathParts.push_back( subPath ); + subPath = ""; + } + + pathParts.push_back( "/" ); + } + else + subPath.append( 1, *originalPath ); + } + + if( subPath.length() > 0 ) + pathParts.push_back( subPath ); + + for( std::vector::size_type i = 0; i < pathParts.size(); ++i ) + { + if( pathParts[i] == ".." && i > 1 ) + { + pathParts.erase( pathParts.begin() + i ); + pathParts.erase( pathParts.begin()+i-1 ); + pathParts.erase( pathParts.begin()+i-2 ); + i = 0; + } + else if( i > 0 && pathParts[i] == "." ) + { + pathParts.erase( pathParts.begin()+i ); + i = 0; + } + else if( pathParts[i] == "/" && i > 0 && pathParts[i-1] == "/" ) + { + pathParts.erase( pathParts.begin()+i-1 ); + i = 0; + } + } + + std::ostringstream oss; + for( std::vector::size_type i = 0; i < pathParts.size(); ++i ) + { + oss << pathParts[i]; + } + + return oss.str(); +} + + + +bool FileLister::AcceptFile( const std::string &filename ) +{ + std::string::size_type dotLocation = filename.find_last_of ( '.' ); + if ( dotLocation == std::string::npos ) + return false; + + std::string extension = filename.substr( dotLocation ); + + if( extension == ".cpp" || + extension == ".cxx" || + extension == ".cc" || + extension == ".c" ) + { + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +////// This code is for __GNUC__ only ///////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUC__ +// gcc / cygwin.. +void FileLister::RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ) +{ + std::ostringstream oss; + oss << path; + if( recursive ) + { + if ( path.length() > 0 && path[path.length()-1] != '/' ) + oss << "/"; + + oss << "*"; + } + + glob_t glob_results; + glob( oss.str().c_str(), GLOB_MARK, 0, &glob_results); + for ( unsigned int i = 0; i < glob_results.gl_pathc; i++ ) + { + std::string filename = glob_results.gl_pathv[i]; + if ( filename == "." || filename == ".." || filename.length() == 0 ) + continue; + + if ( filename[filename.length()-1] != '/' ) + { + // File + + // If recursive is not used, accept all files given by user + if( !recursive || FileLister::AcceptFile( filename ) ) + filenames.push_back( filename ); + } + else if( recursive ) + { + // Directory + FileLister::RecursiveAddFiles( filenames, filename, recursive ); + } + } + globfree(&glob_results); +} +#endif + + +/////////////////////////////////////////////////////////////////////////////// +////// This code is for Borland C++ and Visual C++ //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(__BORLANDC__) || defined(_MSC_VER) + +void FileLister::RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ) +{ + std::ostringstream bdir, oss; + oss << path; + if (recursive) + { + bdir << path; + if ( path.length() > 0 && path[path.length()-1] != '/' ) + { + bdir << "/"; + oss << "/"; + } + + oss << "*"; + } + + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile(oss.str().c_str(), &ffd); + if ( INVALID_HANDLE_VALUE == hFind ) + return; + + do + { + std::ostringstream fname; + fname << bdir.str().c_str() << ffd.cFileName; + + if ( ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0' ) + continue; + + if ( ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) + { + // File + + // If recursive is not used, accept all files given by user + if ( !recursive || FileLister::AcceptFile( ffd.cFileName ) ) + filenames.push_back( fname.str() ); + } + else if ( recursive ) + { + // Directory + fname << "/"; + FileLister::RecursiveAddFiles( filenames, fname.str().c_str(), recursive ); + } + } + while (FindNextFile(hFind, &ffd) != FALSE); + + FindClose(hFind); +} + +#endif + + + + diff --git a/filelister.h b/filelister.h index 19338e34d..28b73ec67 100644 --- a/filelister.h +++ b/filelister.h @@ -1,47 +1,47 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -// Check that the compiler are supported -// This program should be compiled with either GCC/BORLAND/MSC to work.. -#ifndef __GNUC__ -#ifndef __BORLANDC__ -#ifndef _MSC_VER -#error "C++Check must be compiled by either GCC/BORLAND/MSC to work fully.\n" -#error "Please report that you couldn't compile c++check through the web page:\n" -#error " https://sourceforge.net/projects/cppcheck/" -#endif -#endif -#endif - - -class FileLister -{ -public: - static void RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ); - static std::string simplifyPath( const char *originalPath ); -private: - static bool AcceptFile( const std::string &filename ); -}; - -#endif // #ifndef FILELISTER_H +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +// Check that the compiler are supported +// This program should be compiled with either GCC/BORLAND/MSC to work.. +#ifndef __GNUC__ +#ifndef __BORLANDC__ +#ifndef _MSC_VER +#error "C++Check must be compiled by either GCC/BORLAND/MSC to work fully.\n" +#error "Please report that you couldn't compile c++check through the web page:\n" +#error " https://sourceforge.net/projects/cppcheck/" +#endif +#endif +#endif + + +class FileLister +{ +public: + static void RecursiveAddFiles( std::vector &filenames, const std::string &path, bool recursive ); + static std::string simplifyPath( const char *originalPath ); +private: + static bool AcceptFile( const std::string &filename ); +}; + +#endif // #ifndef FILELISTER_H diff --git a/main.cpp b/main.cpp index c0517ec8d..e90cc2e22 100644 --- a/main.cpp +++ b/main.cpp @@ -1,33 +1,33 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -#ifdef __BORLANDC__ -#include -#endif - - -Preprocessor::Preprocessor() -{ - -} - -/** Just read the code into a string. Perform simple cleanup of the code */ -std::string Preprocessor::read(std::istream &istr, const std::string &filename) -{ - // Get filedata from stream.. - bool ignoreSpace = true; - - // For the error report - int lineno = 1; - - std::ostringstream code; - for (char ch = (char)istr.get(); istr.good(); ch = (char)istr.get()) - { - if ( ch < 0 ) - continue; - - if ( ch == '\n' ) - ++lineno; - - // Replace assorted special chars with spaces.. - if ( (ch != '\n') && (isspace(ch) || iscntrl(ch)) ) - ch = ' '; - - // Skip spaces after ' ' and after '#' - if ( ch == ' ' && ignoreSpace ) - continue; - ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '/'); - - // Remove comments.. - if ( ch == '/' ) - { - char chNext = (char)istr.get(); - - if ( chNext == '/' ) - { - while (istr.good() && ch!='\n') - ch = (char)istr.get(); - code << "\n"; - ++lineno; - } - - else if ( chNext == '*' ) - { - char chPrev = 0; - while (istr.good() && (chPrev!='*' || ch!='/')) - { - chPrev = ch; - ch = (char)istr.get(); - if (ch == '\n') - { - code << "\n"; - ++lineno; - } - } - } - - else - { - if ( chNext == '\n' ) - ++lineno; - code << std::string(1,ch) << std::string(1,chNext); - } - } - - // String constants.. - else if ( ch == '\"' ) - { - code << "\""; - do - { - ch = (char)istr.get(); - code << std::string(1,ch); - if ( ch == '\\' ) - { - ch = (char)istr.get(); - code << std::string(1,ch); - - // Avoid exiting loop if string contains "-characters - ch = 0; - } - } while ( istr.good() && ch != '\"' ); - } - - // char constants.. - else if ( ch == '\'' ) - { - code << "\'"; - ch = (char)istr.get(); - code << std::string(1,ch); - if ( ch == '\\' ) - { - ch = (char)istr.get(); - code << std::string(1,ch); - } - ch = (char)istr.get(); - code << "\'"; - } - - // Just some code.. - else - { - code << std::string(1, ch); - } - } - - return code.str(); -} - -/** - * Extract the code for each configuration - * \param istr The (file/string) stream to read from. - * \param result The map that will get the results - */ -void Preprocessor::preprocess(std::istream &istr, std::map &result, const std::string &filename) -{ - std::string codestr( read(istr, filename) ); - - // Replace all tabs with spaces.. - std::string::size_type loc = 0; - while ( (loc = codestr.find("\t", loc)) != std::string::npos ) - codestr[loc] = ' '; - - // Remove all indentation.. - if ( !codestr.empty() && codestr[0] == ' ' ) - codestr.erase( 0, codestr.find_first_not_of(" ") ); - loc = 0; - while ( (loc = codestr.find("\n ", loc)) != std::string::npos ) - codestr.erase( 1 + loc, 1 ); - - // Remove all trailing spaces.. - loc = 0; - while ( (loc = codestr.find(" \n", loc)) != std::string::npos ) - { - codestr.erase( loc, 1 ); - if ( loc > 0 ) - --loc; - } - - // Using the backslash at the end of a line.. - while ( (loc = codestr.rfind("\\\n")) != std::string::npos ) - { - codestr.erase(loc, 2); - if (loc > 0 && codestr[loc-1] != ' ') - codestr.insert(loc, " "); - if ( (loc = codestr.find("\n", loc)) != std::string::npos) - codestr.insert( loc, "\n" ); - } - - // Get all possible configurations.. - std::list cfgs = getcfgs( codestr ); - - // Extract the code for each possible configuration.. - result.clear(); - for ( std::list::const_iterator it = cfgs.begin(); it != cfgs.end(); ++it ) - { - result[ *it ] = getcode( codestr, *it ); - } -} - - - -// Get the DEF in this line: "#ifdef DEF" -std::string Preprocessor::getdef(std::string line, bool def) -{ - // If def is true, the line must start with "#ifdef" - if ( def && line.find("#ifdef ") != 0 && line.find("#if ") != 0 && line.find("#elif ") != 0 ) - { - return ""; - } - - // If def is false, the line must start with "#ifndef" - if ( !def && line.find("#ifndef ") != 0 ) - { - return ""; - } - - // Remove the "#ifdef" or "#ifndef" - line.erase( 0, line.find(" ") ); - - // Remove all spaces. - while ( line.find(" ") != std::string::npos ) - line.erase( line.find(" "), 1 ); - - // The remaining string is our result. - return line; -} - - - -std::list Preprocessor::getcfgs( const std::string &filedata ) -{ - std::list ret; - ret.push_back(""); - - std::list deflist; - - std::istringstream istr(filedata); - std::string line; - while ( getline(istr, line) ) - { - std::string def = getdef(line, true) + getdef(line, false); - if (!def.empty()) - { - if ( ! deflist.empty() && line.find("#elif ") == 0 ) - deflist.pop_back(); - deflist.push_back(def); - def = ""; - for ( std::list::const_iterator it = deflist.begin(); it != deflist.end(); ++it) - { - if ( *it == "0" ) - break; - if ( *it == "1" ) - continue; - if ( ! def.empty() ) - def += ";"; - def += *it; - } - - if (std::find(ret.begin(), ret.end(), def) == ret.end()) - ret.push_back( def ); - } - - if ( line.find("#else") == 0 && ! deflist.empty() ) - { - std::string def( ( deflist.back() == "1" ) ? "0" : "1" ); - deflist.pop_back(); - deflist.push_back( def ); - } - - if ( line.find("#endif") == 0 && ! deflist.empty() ) - deflist.pop_back(); - } - - return ret; -} - - - -bool Preprocessor::match_cfg_def( std::string cfg, const std::string &def ) -{ - if ( def == "0" ) - return false; - - if ( def == "1" ) - return true; - - if ( cfg.empty() ) - return false; - - while ( ! cfg.empty() ) - { - if ( cfg.find(";") == std::string::npos ) - return bool(cfg == def); - std::string _cfg = cfg.substr( 0, cfg.find(";") ); - if ( _cfg == def ) - return true; - cfg.erase( 0, cfg.find(";") + 1 ); - } - - return false; -} - - -std::string Preprocessor::getcode(const std::string &filedata, std::string cfg) -{ - std::ostringstream ret; - - bool match = true; - std::list matching_ifdef; - std::list matched_ifdef; - - std::istringstream istr(filedata); - std::string line; - while ( getline(istr, line) ) - { - std::string def = getdef( line, true ); - std::string ndef = getdef( line, false ); - - if ( line.find("#elif ") == 0 ) - { - if ( matched_ifdef.back() ) - { - matching_ifdef.back() = false; - } - else - { - if ( match_cfg_def(cfg, def) ) - { - matching_ifdef.back() = true; - matched_ifdef.back() = true; - } - } - } - - else if ( ! def.empty() ) - { - matching_ifdef.push_back( match_cfg_def(cfg, def) ); - matched_ifdef.push_back( matching_ifdef.back() ); - } - - else if ( ! ndef.empty() ) - { - matching_ifdef.push_back( ! match_cfg_def(cfg, ndef) ); - matched_ifdef.push_back( matching_ifdef.back() ); - } - - else if ( line == "#else" ) - { - if ( ! matched_ifdef.empty() ) - matching_ifdef.back() = ! matched_ifdef.back(); - } - - else if ( line == "#endif" ) - { - if ( ! matched_ifdef.empty() ) - matched_ifdef.pop_back(); - if ( ! matching_ifdef.empty() ) - matching_ifdef.pop_back(); - } - - if ( !line.empty() && line[0] == '#' ) - { - match = true; - for ( std::list::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it ) - match &= bool(*it); - } - if ( ! match ) - line = ""; - - if ( line.find("#if") == 0 || - line.find("#else") == 0 || - line.find("#elif") == 0 || - line.find("#endif") == 0 ) - line = ""; - - ret << line << "\n"; - } - - return ret.str(); -} - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +#ifdef __BORLANDC__ +#include +#endif + + +Preprocessor::Preprocessor() +{ + +} + +/** Just read the code into a string. Perform simple cleanup of the code */ +std::string Preprocessor::read(std::istream &istr, const std::string &filename) +{ + // Get filedata from stream.. + bool ignoreSpace = true; + + // For the error report + int lineno = 1; + + std::ostringstream code; + for (char ch = (char)istr.get(); istr.good(); ch = (char)istr.get()) + { + if ( ch < 0 ) + continue; + + if ( ch == '\n' ) + ++lineno; + + // Replace assorted special chars with spaces.. + if ( (ch != '\n') && (isspace(ch) || iscntrl(ch)) ) + ch = ' '; + + // Skip spaces after ' ' and after '#' + if ( ch == ' ' && ignoreSpace ) + continue; + ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '/'); + + // Remove comments.. + if ( ch == '/' ) + { + char chNext = (char)istr.get(); + + if ( chNext == '/' ) + { + while (istr.good() && ch!='\n') + ch = (char)istr.get(); + code << "\n"; + ++lineno; + } + + else if ( chNext == '*' ) + { + char chPrev = 0; + while (istr.good() && (chPrev!='*' || ch!='/')) + { + chPrev = ch; + ch = (char)istr.get(); + if (ch == '\n') + { + code << "\n"; + ++lineno; + } + } + } + + else + { + if ( chNext == '\n' ) + ++lineno; + code << std::string(1,ch) << std::string(1,chNext); + } + } + + // String constants.. + else if ( ch == '\"' ) + { + code << "\""; + do + { + ch = (char)istr.get(); + code << std::string(1,ch); + if ( ch == '\\' ) + { + ch = (char)istr.get(); + code << std::string(1,ch); + + // Avoid exiting loop if string contains "-characters + ch = 0; + } + } while ( istr.good() && ch != '\"' ); + } + + // char constants.. + else if ( ch == '\'' ) + { + code << "\'"; + ch = (char)istr.get(); + code << std::string(1,ch); + if ( ch == '\\' ) + { + ch = (char)istr.get(); + code << std::string(1,ch); + } + ch = (char)istr.get(); + code << "\'"; + } + + // Just some code.. + else + { + code << std::string(1, ch); + } + } + + return code.str(); +} + +/** + * Extract the code for each configuration + * \param istr The (file/string) stream to read from. + * \param result The map that will get the results + */ +void Preprocessor::preprocess(std::istream &istr, std::map &result, const std::string &filename) +{ + std::string codestr( read(istr, filename) ); + + // Replace all tabs with spaces.. + std::string::size_type loc = 0; + while ( (loc = codestr.find("\t", loc)) != std::string::npos ) + codestr[loc] = ' '; + + // Remove all indentation.. + if ( !codestr.empty() && codestr[0] == ' ' ) + codestr.erase( 0, codestr.find_first_not_of(" ") ); + loc = 0; + while ( (loc = codestr.find("\n ", loc)) != std::string::npos ) + codestr.erase( 1 + loc, 1 ); + + // Remove all trailing spaces.. + loc = 0; + while ( (loc = codestr.find(" \n", loc)) != std::string::npos ) + { + codestr.erase( loc, 1 ); + if ( loc > 0 ) + --loc; + } + + // Using the backslash at the end of a line.. + while ( (loc = codestr.rfind("\\\n")) != std::string::npos ) + { + codestr.erase(loc, 2); + if (loc > 0 && codestr[loc-1] != ' ') + codestr.insert(loc, " "); + if ( (loc = codestr.find("\n", loc)) != std::string::npos) + codestr.insert( loc, "\n" ); + } + + // Get all possible configurations.. + std::list cfgs = getcfgs( codestr ); + + // Extract the code for each possible configuration.. + result.clear(); + for ( std::list::const_iterator it = cfgs.begin(); it != cfgs.end(); ++it ) + { + result[ *it ] = getcode( codestr, *it ); + } +} + + + +// Get the DEF in this line: "#ifdef DEF" +std::string Preprocessor::getdef(std::string line, bool def) +{ + // If def is true, the line must start with "#ifdef" + if ( def && line.find("#ifdef ") != 0 && line.find("#if ") != 0 && line.find("#elif ") != 0 ) + { + return ""; + } + + // If def is false, the line must start with "#ifndef" + if ( !def && line.find("#ifndef ") != 0 ) + { + return ""; + } + + // Remove the "#ifdef" or "#ifndef" + line.erase( 0, line.find(" ") ); + + // Remove all spaces. + while ( line.find(" ") != std::string::npos ) + line.erase( line.find(" "), 1 ); + + // The remaining string is our result. + return line; +} + + + +std::list Preprocessor::getcfgs( const std::string &filedata ) +{ + std::list ret; + ret.push_back(""); + + std::list deflist; + + std::istringstream istr(filedata); + std::string line; + while ( getline(istr, line) ) + { + std::string def = getdef(line, true) + getdef(line, false); + if (!def.empty()) + { + if ( ! deflist.empty() && line.find("#elif ") == 0 ) + deflist.pop_back(); + deflist.push_back(def); + def = ""; + for ( std::list::const_iterator it = deflist.begin(); it != deflist.end(); ++it) + { + if ( *it == "0" ) + break; + if ( *it == "1" ) + continue; + if ( ! def.empty() ) + def += ";"; + def += *it; + } + + if (std::find(ret.begin(), ret.end(), def) == ret.end()) + ret.push_back( def ); + } + + if ( line.find("#else") == 0 && ! deflist.empty() ) + { + std::string def( ( deflist.back() == "1" ) ? "0" : "1" ); + deflist.pop_back(); + deflist.push_back( def ); + } + + if ( line.find("#endif") == 0 && ! deflist.empty() ) + deflist.pop_back(); + } + + return ret; +} + + + +bool Preprocessor::match_cfg_def( std::string cfg, const std::string &def ) +{ + if ( def == "0" ) + return false; + + if ( def == "1" ) + return true; + + if ( cfg.empty() ) + return false; + + while ( ! cfg.empty() ) + { + if ( cfg.find(";") == std::string::npos ) + return bool(cfg == def); + std::string _cfg = cfg.substr( 0, cfg.find(";") ); + if ( _cfg == def ) + return true; + cfg.erase( 0, cfg.find(";") + 1 ); + } + + return false; +} + + +std::string Preprocessor::getcode(const std::string &filedata, std::string cfg) +{ + std::ostringstream ret; + + bool match = true; + std::list matching_ifdef; + std::list matched_ifdef; + + std::istringstream istr(filedata); + std::string line; + while ( getline(istr, line) ) + { + std::string def = getdef( line, true ); + std::string ndef = getdef( line, false ); + + if ( line.find("#elif ") == 0 ) + { + if ( matched_ifdef.back() ) + { + matching_ifdef.back() = false; + } + else + { + if ( match_cfg_def(cfg, def) ) + { + matching_ifdef.back() = true; + matched_ifdef.back() = true; + } + } + } + + else if ( ! def.empty() ) + { + matching_ifdef.push_back( match_cfg_def(cfg, def) ); + matched_ifdef.push_back( matching_ifdef.back() ); + } + + else if ( ! ndef.empty() ) + { + matching_ifdef.push_back( ! match_cfg_def(cfg, ndef) ); + matched_ifdef.push_back( matching_ifdef.back() ); + } + + else if ( line == "#else" ) + { + if ( ! matched_ifdef.empty() ) + matching_ifdef.back() = ! matched_ifdef.back(); + } + + else if ( line == "#endif" ) + { + if ( ! matched_ifdef.empty() ) + matched_ifdef.pop_back(); + if ( ! matching_ifdef.empty() ) + matching_ifdef.pop_back(); + } + + if ( !line.empty() && line[0] == '#' ) + { + match = true; + for ( std::list::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it ) + match &= bool(*it); + } + if ( ! match ) + line = ""; + + if ( line.find("#if") == 0 || + line.find("#else") == 0 || + line.find("#elif") == 0 || + line.find("#endif") == 0 ) + line = ""; + + ret << line << "\n"; + } + + return ret.str(); +} + diff --git a/preprocessor.h b/preprocessor.h index a7291c142..465bc2e1f 100644 --- a/preprocessor.h +++ b/preprocessor.h @@ -1,58 +1,58 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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(); - - void preprocess(std::istream &istr, std::map &result, const std::string &filename); - - /** Just read the code into a string. Perform simple cleanup of the code */ - std::string read(std::istream &istr, const std::string &filename); - -private: - /** - * Get preprocessed code for a given configuration - */ - std::string getcode(const std::string &filedata, std::string cfg); - - /** - * Get all possible configurations. By looking at the ifdefs and ifndefs in filedata - */ - std::list getcfgs( const std::string &filedata ); - - std::string getdef(std::string line, bool def); - - bool match_cfg_def( std::string cfg, const std::string &def ); -}; - -//--------------------------------------------------------------------------- -#endif - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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(); + + void preprocess(std::istream &istr, std::map &result, const std::string &filename); + + /** Just read the code into a string. Perform simple cleanup of the code */ + std::string read(std::istream &istr, const std::string &filename); + +private: + /** + * Get preprocessed code for a given configuration + */ + std::string getcode(const std::string &filedata, std::string cfg); + + /** + * Get all possible configurations. By looking at the ifdefs and ifndefs in filedata + */ + std::list getcfgs( const std::string &filedata ); + + std::string getdef(std::string line, bool def); + + bool match_cfg_def( std::string cfg, const std::string &def ); +}; + +//--------------------------------------------------------------------------- +#endif + diff --git a/settings.cpp b/settings.cpp index dd8e736ba..095d84c8d 100644 --- a/settings.cpp +++ b/settings.cpp @@ -1,34 +1,34 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -extern std::ostringstream errout; - -class TestBufferOverrun : public TestFixture -{ -public: - TestBufferOverrun() : TestFixture("TestBufferOverrun") - { } - -private: - - - - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Assign variable ids - tokenizer.setVarId(); - - // Fill function list - tokenizer.fillFunctionList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for buffer overruns.. - Settings settings; - settings._showAll = true; - CheckBufferOverrunClass checkBufferOverrun( &tokenizer, settings, this ); - checkBufferOverrun.CheckBufferOverrun(); - } - - void run() - { - TEST_CASE( noerr1 ); - TEST_CASE( noerr2 ); - TEST_CASE( noerr3 ); - TEST_CASE( noerr4 ); - - TEST_CASE( array_index_1 ); - TEST_CASE( array_index_2 ); - TEST_CASE( array_index_3 ); - TEST_CASE( array_index_4 ); - TEST_CASE( array_index_5 ); - TEST_CASE( array_index_6 ); - TEST_CASE( array_index_7 ); - TEST_CASE( array_index_8 ); - TEST_CASE( array_index_9 ); - TEST_CASE( array_index_10 ); - TEST_CASE( array_index_11 ); - TEST_CASE( array_index_12 ); - - TEST_CASE( buffer_overrun_1 ); - TEST_CASE( buffer_overrun_2 ); - - TEST_CASE( varid1 ); - TEST_CASE( varid2 ); - } - - - - void noerr1() - { - check( "void f()\n" - "{\n" - " if (ab)\n" - " {\n" - " char str[50];\n" - " }\n" - " if (ab)\n" - " {\n" - " char str[50];\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void noerr2() - { - check( "void f1(char *str)\n" - "{\n" - " strcpy(buf,str);\n" - "}\n" - "void f2(char *str)\n" - "{\n" - " strcat(buf,str);\n" - "}\n" - "void f3(char *str)\n" - "{\n" - " sprintf(buf,\"%s\",str);\n" - "}\n" - "void f4(const char str[])\n" - "{\n" - " strcpy(buf, str);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void noerr3() - { - check( "static void f()\n" - "{\n" - " char data[1];\n" - " return abc.data[1];\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void noerr4() - { - // The memory isn't read or written and therefore there is no error. - check( "static void f()\n" - "{\n" - " char data[100];\n" - " const char *p = &data[100];\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void array_index_1() - { - check("void f()\n" - "{\n" - " char str[0x10];\n" - " str[15] = 0;\n" - " str[16] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_2() - { - check("void f()\n" - "{\n" - " char *str = new char[0x10];\n" - " str[15] = 0;\n" - " str[16] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_3() - { - check( "void f()\n" - "{\n" - " int val[50];\n" - " for (i = 0; i < 100; i++)\n" - " sum += val[i];\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Buffer overrun\n"), errout.str() ); - } - - - void array_index_4() - { - check( "const int SIZE = 10;\n" - "void f()\n" - "{\n" - " int i[SIZE];\n" - " i[SIZE] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_5() - { - check( "void f()\n" - "{\n" - " int i[10];\n" - " i[ sizeof(i) - 1 ] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_6() - { - check( "struct ABC\n" - "{\n" - " char str[10];\n" - "};\n" - "\n" - "static void f()\n" - "{\n" - " struct ABC abc;\n" - " abc.str[10] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:9]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_7() - { - check( "struct ABC\n" - "{\n" - " char str[10];\n" - "};\n" - "\n" - "static void f(ABC *abc)\n" - "{\n" - " abc->str[10] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_8() - { - check( "const int SIZE = 10;\n" - "\n" - "struct ABC\n" - "{\n" - " char str[SIZE];\n" - "};\n" - "\n" - "static void f()\n" - "{\n" - " struct ABC abc;\n" - " abc.str[SIZE] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:11]: Array index out of bounds\n"), errout.str() ); - } - - void array_index_9() - { - check( "static void memclr( char *data )\n" - "{\n" - " data[10] = 0;\n" - "}\n" - "\n" - "static void f()\n" - "{\n" - " char str[5];\n" - " memclr( str ); // ERROR\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:9] -> [test.cpp:3]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_10() - { - check( "struct ABC\n" - "{\n" - " char str[10];\n" - "};\n" - "\n" - "static void memclr( char *data )\n" - "{\n" - " data[10] = 0;\n" - "}\n" - "\n" - "static void f(ABC *abc)\n" - "{\n" - " memclr(abc->str);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:13] -> [test.cpp:8]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_11() - { - check( "class ABC\n" - "{\n" - "public:\n" - " ABC();\n" - " char *str[10];\n" - " struct ABC *next;" - "};\n" - "\n" - "static void f()\n" - "{\n" - " for ( ABC *abc = abc1; abc; abc = abc->next() )\n" - " {\n" - " abc->str[10] = 0;\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:12]: Array index out of bounds\n"), errout.str() ); - } - - - void array_index_12() - { - check( "class Fred\n" - "{\n" - "private:\n" - " char str[10];\n" - "public:\n" - " Fred();\n" - "};\n" - "Fred::Fred()\n" - "{\n" - " str[10] = 0;\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string("[test.cpp:10]: Array index out of bounds\n"), err ); - } - - - void buffer_overrun_1() - { - check( "void f()\n" - "{\n" - " char str[3];\n" - " strcpy(str, \"abc\");\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Buffer overrun\n"), errout.str() ); - } - - - void buffer_overrun_2() - { - check( "struct ABC\n" - "{\n" - " char str[5];\n" - "};\n" - "\n" - "static void f(ABC *abc)\n" - "{\n" - " strcpy( abc->str, \"abcdef\" );\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8]: Buffer overrun\n"), errout.str() ); - } - - - - void varid1() - { - check( "void foo()\n" - "{\n" - " char str[10];\n" - " if (str[0])\n" - " {\n" - " char str[50];\n" - " str[30] = 0;\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void varid2() - { - check( "void foo()\n" - "{\n" - " char str[10];\n" - " if (str[0])\n" - " {\n" - " char str[50];\n" - " memset(str,0,50);\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - -}; - -REGISTER_TEST( TestBufferOverrun ) - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestBufferOverrun : public TestFixture +{ +public: + TestBufferOverrun() : TestFixture("TestBufferOverrun") + { } + +private: + + + + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Assign variable ids + tokenizer.setVarId(); + + // Fill function list + tokenizer.fillFunctionList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for buffer overruns.. + Settings settings; + settings._showAll = true; + CheckBufferOverrunClass checkBufferOverrun( &tokenizer, settings, this ); + checkBufferOverrun.CheckBufferOverrun(); + } + + void run() + { + TEST_CASE( noerr1 ); + TEST_CASE( noerr2 ); + TEST_CASE( noerr3 ); + TEST_CASE( noerr4 ); + + TEST_CASE( array_index_1 ); + TEST_CASE( array_index_2 ); + TEST_CASE( array_index_3 ); + TEST_CASE( array_index_4 ); + TEST_CASE( array_index_5 ); + TEST_CASE( array_index_6 ); + TEST_CASE( array_index_7 ); + TEST_CASE( array_index_8 ); + TEST_CASE( array_index_9 ); + TEST_CASE( array_index_10 ); + TEST_CASE( array_index_11 ); + TEST_CASE( array_index_12 ); + + TEST_CASE( buffer_overrun_1 ); + TEST_CASE( buffer_overrun_2 ); + + TEST_CASE( varid1 ); + TEST_CASE( varid2 ); + } + + + + void noerr1() + { + check( "void f()\n" + "{\n" + " if (ab)\n" + " {\n" + " char str[50];\n" + " }\n" + " if (ab)\n" + " {\n" + " char str[50];\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void noerr2() + { + check( "void f1(char *str)\n" + "{\n" + " strcpy(buf,str);\n" + "}\n" + "void f2(char *str)\n" + "{\n" + " strcat(buf,str);\n" + "}\n" + "void f3(char *str)\n" + "{\n" + " sprintf(buf,\"%s\",str);\n" + "}\n" + "void f4(const char str[])\n" + "{\n" + " strcpy(buf, str);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void noerr3() + { + check( "static void f()\n" + "{\n" + " char data[1];\n" + " return abc.data[1];\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void noerr4() + { + // The memory isn't read or written and therefore there is no error. + check( "static void f()\n" + "{\n" + " char data[100];\n" + " const char *p = &data[100];\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void array_index_1() + { + check("void f()\n" + "{\n" + " char str[0x10];\n" + " str[15] = 0;\n" + " str[16] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_2() + { + check("void f()\n" + "{\n" + " char *str = new char[0x10];\n" + " str[15] = 0;\n" + " str[16] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_3() + { + check( "void f()\n" + "{\n" + " int val[50];\n" + " for (i = 0; i < 100; i++)\n" + " sum += val[i];\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Buffer overrun\n"), errout.str() ); + } + + + void array_index_4() + { + check( "const int SIZE = 10;\n" + "void f()\n" + "{\n" + " int i[SIZE];\n" + " i[SIZE] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_5() + { + check( "void f()\n" + "{\n" + " int i[10];\n" + " i[ sizeof(i) - 1 ] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_6() + { + check( "struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC abc;\n" + " abc.str[10] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:9]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_7() + { + check( "struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static void f(ABC *abc)\n" + "{\n" + " abc->str[10] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_8() + { + check( "const int SIZE = 10;\n" + "\n" + "struct ABC\n" + "{\n" + " char str[SIZE];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC abc;\n" + " abc.str[SIZE] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:11]: Array index out of bounds\n"), errout.str() ); + } + + void array_index_9() + { + check( "static void memclr( char *data )\n" + "{\n" + " data[10] = 0;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char str[5];\n" + " memclr( str ); // ERROR\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:9] -> [test.cpp:3]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_10() + { + check( "struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static void memclr( char *data )\n" + "{\n" + " data[10] = 0;\n" + "}\n" + "\n" + "static void f(ABC *abc)\n" + "{\n" + " memclr(abc->str);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:13] -> [test.cpp:8]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_11() + { + check( "class ABC\n" + "{\n" + "public:\n" + " ABC();\n" + " char *str[10];\n" + " struct ABC *next;" + "};\n" + "\n" + "static void f()\n" + "{\n" + " for ( ABC *abc = abc1; abc; abc = abc->next() )\n" + " {\n" + " abc->str[10] = 0;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:12]: Array index out of bounds\n"), errout.str() ); + } + + + void array_index_12() + { + check( "class Fred\n" + "{\n" + "private:\n" + " char str[10];\n" + "public:\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{\n" + " str[10] = 0;\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string("[test.cpp:10]: Array index out of bounds\n"), err ); + } + + + void buffer_overrun_1() + { + check( "void f()\n" + "{\n" + " char str[3];\n" + " strcpy(str, \"abc\");\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Buffer overrun\n"), errout.str() ); + } + + + void buffer_overrun_2() + { + check( "struct ABC\n" + "{\n" + " char str[5];\n" + "};\n" + "\n" + "static void f(ABC *abc)\n" + "{\n" + " strcpy( abc->str, \"abcdef\" );\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Buffer overrun\n"), errout.str() ); + } + + + + void varid1() + { + check( "void foo()\n" + "{\n" + " char str[10];\n" + " if (str[0])\n" + " {\n" + " char str[50];\n" + " str[30] = 0;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void varid2() + { + check( "void foo()\n" + "{\n" + " char str[10];\n" + " if (str[0])\n" + " {\n" + " char str[50];\n" + " memset(str,0,50);\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + +}; + +REGISTER_TEST( TestBufferOverrun ) + + diff --git a/testcharvar.cpp b/testcharvar.cpp index f5a7dfcf0..153d924f3 100644 --- a/testcharvar.cpp +++ b/testcharvar.cpp @@ -1,105 +1,105 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestCharVar : public TestFixture -{ -public: - TestCharVar() : TestFixture("TestCharVar") - { } - -private: - - - void run() - { - TEST_CASE( array_index ); - TEST_CASE( bitop1 ); - TEST_CASE( bitop2 ); - } - - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - - // Clear the error buffer.. - errout.str(""); - - // Check char variable usage.. - CheckOther checkOther( &tokenizer, this ); - checkOther.CheckCharVariable(); - } - - void array_index() - { - check( "void foo()\n" - "{\n" - " unsigned char ch = 0x80;\n" - " buf[ch] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - - check( "void foo()\n" - "{\n" - " char ch = 0x80;\n" - " buf[ch] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Warning - using char variable as array index\n"), errout.str() ); - - check( "void foo(char ch)\n" - "{\n" - " buf[ch] = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:3]: Warning - using char variable as array index\n"), errout.str() ); - } - - - void bitop1() - { - check( "void foo()\n" - "{\n" - " char ch;\n" - " result = a | ch;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Warning - using char variable in bit operation\n"), errout.str() ); - } - - void bitop2() - { - check( "void foo()\n" - "{\n" - " char ch;\n" - " func(&ch);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } -}; - -REGISTER_TEST( TestCharVar ) - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestCharVar : public TestFixture +{ +public: + TestCharVar() : TestFixture("TestCharVar") + { } + +private: + + + void run() + { + TEST_CASE( array_index ); + TEST_CASE( bitop1 ); + TEST_CASE( bitop2 ); + } + + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + + // Clear the error buffer.. + errout.str(""); + + // Check char variable usage.. + CheckOther checkOther( &tokenizer, this ); + checkOther.CheckCharVariable(); + } + + void array_index() + { + check( "void foo()\n" + "{\n" + " unsigned char ch = 0x80;\n" + " buf[ch] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + + check( "void foo()\n" + "{\n" + " char ch = 0x80;\n" + " buf[ch] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Warning - using char variable as array index\n"), errout.str() ); + + check( "void foo(char ch)\n" + "{\n" + " buf[ch] = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:3]: Warning - using char variable as array index\n"), errout.str() ); + } + + + void bitop1() + { + check( "void foo()\n" + "{\n" + " char ch;\n" + " result = a | ch;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Warning - using char variable in bit operation\n"), errout.str() ); + } + + void bitop2() + { + check( "void foo()\n" + "{\n" + " char ch;\n" + " func(&ch);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } +}; + +REGISTER_TEST( TestCharVar ) + diff --git a/testclass.cpp b/testclass.cpp index 915957116..4e05ddca3 100644 --- a/testclass.cpp +++ b/testclass.cpp @@ -1,115 +1,115 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestClass : public TestFixture -{ -public: - TestClass() : TestFixture("TestClass") - { } - -private: - - void run() - { - TEST_CASE( virtualDestructor1 ); // Base class not found => no error - TEST_CASE( virtualDestructor2 ); // Base class doesn't have a destructor - TEST_CASE( virtualDestructor3 ); // Base class has a destructor, but it's not virtual - TEST_CASE( virtualDestructor4 ); // Derived class doesn't have a destructor => no error - } - - // Check that base classes have virtual destructors - void checkVirtualDestructor(const char code[]) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Clear the error log - errout.str(""); - - // Check.. - Settings settings; - CheckClass checkClass( &tokenizer, settings, this ); - checkClass.virtualDestructor(); - } - - - void virtualDestructor1() - { - // Base class not found - - checkVirtualDestructor("class Derived : public Base { };"); - ASSERT_EQUALS( std::string(""), errout.str() ); - - checkVirtualDestructor("class Derived : Base { };"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void virtualDestructor2() - { - // Base class doesn't have a destructor - - checkVirtualDestructor("class Base { };\n" - "class Derived : public Base { public: ~Derived() { (void)11; } };"); - ASSERT_EQUALS( std::string("[test.cpp:1]: Base class Base doesn't have a virtual destructor\n"), errout.str() ); - - checkVirtualDestructor("class Base { };\n" - "class Derived : Base { public: ~Derived() { (void)11; } };"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void virtualDestructor3() - { - // Base class has a destructor, but it's not virtual - - checkVirtualDestructor("class Base { public: ~Base(); };\n" - "class Derived : public Base { public: ~Derived() { (void)11; } };"); - ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() ); - - checkVirtualDestructor("class Base { public: ~Base(); };\n" - "class Derived : private Fred, public Base { public: ~Derived() { (void)11; } };"); - ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() ); - } - - void virtualDestructor4() - { - // Derived class doesn't have a destructor => no error - - checkVirtualDestructor("class Base { public: ~Base(); };\n" - "class Derived : public Base { };"); - ASSERT_EQUALS( std::string(""), errout.str() ); - - checkVirtualDestructor("class Base { public: ~Base(); };\n" - "class Derived : private Fred, public Base { };"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } -}; - -REGISTER_TEST( TestClass ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestClass : public TestFixture +{ +public: + TestClass() : TestFixture("TestClass") + { } + +private: + + void run() + { + TEST_CASE( virtualDestructor1 ); // Base class not found => no error + TEST_CASE( virtualDestructor2 ); // Base class doesn't have a destructor + TEST_CASE( virtualDestructor3 ); // Base class has a destructor, but it's not virtual + TEST_CASE( virtualDestructor4 ); // Derived class doesn't have a destructor => no error + } + + // Check that base classes have virtual destructors + void checkVirtualDestructor(const char code[]) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Clear the error log + errout.str(""); + + // Check.. + Settings settings; + CheckClass checkClass( &tokenizer, settings, this ); + checkClass.virtualDestructor(); + } + + + void virtualDestructor1() + { + // Base class not found + + checkVirtualDestructor("class Derived : public Base { };"); + ASSERT_EQUALS( std::string(""), errout.str() ); + + checkVirtualDestructor("class Derived : Base { };"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void virtualDestructor2() + { + // Base class doesn't have a destructor + + checkVirtualDestructor("class Base { };\n" + "class Derived : public Base { public: ~Derived() { (void)11; } };"); + ASSERT_EQUALS( std::string("[test.cpp:1]: Base class Base doesn't have a virtual destructor\n"), errout.str() ); + + checkVirtualDestructor("class Base { };\n" + "class Derived : Base { public: ~Derived() { (void)11; } };"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void virtualDestructor3() + { + // Base class has a destructor, but it's not virtual + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { public: ~Derived() { (void)11; } };"); + ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() ); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : private Fred, public Base { public: ~Derived() { (void)11; } };"); + ASSERT_EQUALS( std::string("[test.cpp:1]: The destructor for the base class Base is not virtual\n"), errout.str() ); + } + + void virtualDestructor4() + { + // Derived class doesn't have a destructor => no error + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { };"); + ASSERT_EQUALS( std::string(""), errout.str() ); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : private Fred, public Base { };"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } +}; + +REGISTER_TEST( TestClass ) diff --git a/testconstructors.cpp b/testconstructors.cpp index fb68dc6ce..e600c328b 100644 --- a/testconstructors.cpp +++ b/testconstructors.cpp @@ -1,326 +1,326 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestConstructors : public TestFixture -{ -public: - TestConstructors() : TestFixture("TestConstructors") - { } - -private: - - - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check class constructors.. - Settings settings; - settings._checkCodingStyle = true; - CheckClass checkClass( &tokenizer, settings, this ); - checkClass.CheckConstructors(); - } - - void run() - { - TEST_CASE( simple1 ); - TEST_CASE( simple2 ); - TEST_CASE( simple3 ); - TEST_CASE( simple4 ); - - TEST_CASE( initvar_with_this ); // BUG 2190300 - TEST_CASE( initvar_if ); // BUG 2190290 - TEST_CASE( initvar_operator_eq1 ); // BUG 2190376 - TEST_CASE( initvar_operator_eq2 ); // BUG 2190376 - TEST_CASE( initvar_operator_eq3 ); - TEST_CASE( initvar_same_classname ); // BUG 2208157 - TEST_CASE( initvar_chained_assign ); // BUG 2270433 - TEST_CASE( initvar_2constructors ); // BUG 2270353 - - TEST_CASE( initvar_private_constructor ); // BUG 2354171 - private constructor - - TEST_CASE( initvar_destructor ); // No variables need to be initialized in a destructor - } - - - void simple1() - { - check( "class Fred\n" - "{\n" - "public:\n" - " int i;\n" - "};\n" ); - std::string actual( errout.str() ); - std::string expected( "[test.cpp:1] The class 'Fred' has no constructor\n" ); - ASSERT_EQUALS( expected, actual ); - } - - - void simple2() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred() { }\n" - " int i;\n" - "};\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4] Uninitialized member variable 'Fred::i'\n"), errout.str() ); - } - - - void simple3() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred();\n" - " int i;\n" - "};\n" - "Fred::Fred()\n" - "{ }\n" ); - ASSERT_EQUALS( std::string("[test.cpp:7] Uninitialized member variable 'Fred::i'\n"), errout.str() ); - } - - - void simple4() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred();\n" - " Fred(int _i);\n" - " int i;\n" - "};\n" - "Fred::Fred()\n" - "{ }\n" - "Fred::Fred(int _i)\n" - "{\n" - " i = _i;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8] Uninitialized member variable 'Fred::i'\n"), errout.str() ); - } - - - void initvar_with_this() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred()\n" - " { this->i = 0; }\n" - " int i;\n" - "};\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void initvar_if() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred()\n" - " {\n" - " if (true)\n" - " i = 0;\n" - " else\n" - " i = 1;\n" - " }\n" - " int i;\n" - "};\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void initvar_operator_eq1() - { - // Bug 2190376 - False positive, Uninitialized member variable with operator= - - check( "class Fred\n" - "{\n" - "private:\n" - " int i;\n" - "\n" - "public:\n" - " Fred()\n" - " { i = 0; }\n" - "\n" - " Fred(const Fred &fred)\n" - " { *this = fred; }\n" - "\n" - " const Fred & operator=(const Fred &fred)\n" - " { i = fred.i; return *this; }\n" - "};\n" ); - - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), err ); - } - - - void initvar_operator_eq2() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred() { i = 0; }\n" - " void operator=() { }\n" - " int i;\n" - "};\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5] Uninitialized member variable 'Fred::i'\n"), errout.str() ); - } - - void initvar_operator_eq3() - { - check( "class Fred\n" - "{\n" - "public:\n" - " Fred() { Init(); }\n" - " void operator=() { Init(); }\n" - "private:\n" - " Init() { i = 0; }\n" - " int i;\n" - "};\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void initvar_same_classname() - { - // Bug 2208157 - False positive: Uninitialized variable, same class name - - check( "void func1()\n" - "{\n" - " class Fred\n" - " {\n" - " int a;\n" - " Fred() { a = 0; }\n" - " };\n" - "}\n" - "\n" - "void func2()\n" - "{\n" - " class Fred\n" - " {\n" - " int b;\n" - " Fred() { b = 0; }\n" - " };\n" - "}\n" ); - - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), err ); - } - - void initvar_chained_assign() - { - // Bug 2270433 - Uninitialized variable false positive on chained assigns - - check( "class c\n" - "{\n" - " c();\n" - "\n" - " int m_iMyInt1;\n" - " int m_iMyInt2;\n" - "}\n" - "\n" - "c::c()\n" - "{\n" - " m_iMyInt1 = m_iMyInt2 = 0;\n" - "}\n" ); - - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), err ); - } - - - void initvar_2constructors() - { - check( "class c\n" - "{\n" - " c();\n" - " c(bool b);" - "\n" - " void InitInt();\n" - "\n" - " int m_iMyInt;\n" - " int m_bMyBool;\n" - "}\n" - "\n" - "c::c()\n" - "{\n" - " m_bMyBool = false;\n" - " InitInt();" - "}\n" - "\n" - "c::c(bool b)\n" - "{\n" - " m_bMyBool = b;\n" - " InitInt();\n" - "}\n" - "\n" - "void c::InitInt()\n" - "{\n" - " m_iMyInt = 0;\n" - "}\n" ); - - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), err ); - } - - - void initvar_private_constructor() - { - check( "class Fred\n" - "{\n" - "private:\n" - " int var;\n" - " Fred();\n" - "};\n" - "Fred::Fred()\n" - "{ }" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void initvar_destructor() - { - check( "class Fred\n" - "{\n" - "private:\n" - " int var;\n" - "public:\n" - " Fred() : var(0) {}\n" - " ~Fred() {}\n" - "};\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } -}; - -REGISTER_TEST( TestConstructors ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestConstructors : public TestFixture +{ +public: + TestConstructors() : TestFixture("TestConstructors") + { } + +private: + + + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check class constructors.. + Settings settings; + settings._checkCodingStyle = true; + CheckClass checkClass( &tokenizer, settings, this ); + checkClass.CheckConstructors(); + } + + void run() + { + TEST_CASE( simple1 ); + TEST_CASE( simple2 ); + TEST_CASE( simple3 ); + TEST_CASE( simple4 ); + + TEST_CASE( initvar_with_this ); // BUG 2190300 + TEST_CASE( initvar_if ); // BUG 2190290 + TEST_CASE( initvar_operator_eq1 ); // BUG 2190376 + TEST_CASE( initvar_operator_eq2 ); // BUG 2190376 + TEST_CASE( initvar_operator_eq3 ); + TEST_CASE( initvar_same_classname ); // BUG 2208157 + TEST_CASE( initvar_chained_assign ); // BUG 2270433 + TEST_CASE( initvar_2constructors ); // BUG 2270353 + + TEST_CASE( initvar_private_constructor ); // BUG 2354171 - private constructor + + TEST_CASE( initvar_destructor ); // No variables need to be initialized in a destructor + } + + + void simple1() + { + check( "class Fred\n" + "{\n" + "public:\n" + " int i;\n" + "};\n" ); + std::string actual( errout.str() ); + std::string expected( "[test.cpp:1] The class 'Fred' has no constructor\n" ); + ASSERT_EQUALS( expected, actual ); + } + + + void simple2() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred() { }\n" + " int i;\n" + "};\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4] Uninitialized member variable 'Fred::i'\n"), errout.str() ); + } + + + void simple3() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred();\n" + " int i;\n" + "};\n" + "Fred::Fred()\n" + "{ }\n" ); + ASSERT_EQUALS( std::string("[test.cpp:7] Uninitialized member variable 'Fred::i'\n"), errout.str() ); + } + + + void simple4() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred();\n" + " Fred(int _i);\n" + " int i;\n" + "};\n" + "Fred::Fred()\n" + "{ }\n" + "Fred::Fred(int _i)\n" + "{\n" + " i = _i;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8] Uninitialized member variable 'Fred::i'\n"), errout.str() ); + } + + + void initvar_with_this() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred()\n" + " { this->i = 0; }\n" + " int i;\n" + "};\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void initvar_if() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred()\n" + " {\n" + " if (true)\n" + " i = 0;\n" + " else\n" + " i = 1;\n" + " }\n" + " int i;\n" + "};\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void initvar_operator_eq1() + { + // Bug 2190376 - False positive, Uninitialized member variable with operator= + + check( "class Fred\n" + "{\n" + "private:\n" + " int i;\n" + "\n" + "public:\n" + " Fred()\n" + " { i = 0; }\n" + "\n" + " Fred(const Fred &fred)\n" + " { *this = fred; }\n" + "\n" + " const Fred & operator=(const Fred &fred)\n" + " { i = fred.i; return *this; }\n" + "};\n" ); + + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), err ); + } + + + void initvar_operator_eq2() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred() { i = 0; }\n" + " void operator=() { }\n" + " int i;\n" + "};\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5] Uninitialized member variable 'Fred::i'\n"), errout.str() ); + } + + void initvar_operator_eq3() + { + check( "class Fred\n" + "{\n" + "public:\n" + " Fred() { Init(); }\n" + " void operator=() { Init(); }\n" + "private:\n" + " Init() { i = 0; }\n" + " int i;\n" + "};\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void initvar_same_classname() + { + // Bug 2208157 - False positive: Uninitialized variable, same class name + + check( "void func1()\n" + "{\n" + " class Fred\n" + " {\n" + " int a;\n" + " Fred() { a = 0; }\n" + " };\n" + "}\n" + "\n" + "void func2()\n" + "{\n" + " class Fred\n" + " {\n" + " int b;\n" + " Fred() { b = 0; }\n" + " };\n" + "}\n" ); + + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), err ); + } + + void initvar_chained_assign() + { + // Bug 2270433 - Uninitialized variable false positive on chained assigns + + check( "class c\n" + "{\n" + " c();\n" + "\n" + " int m_iMyInt1;\n" + " int m_iMyInt2;\n" + "}\n" + "\n" + "c::c()\n" + "{\n" + " m_iMyInt1 = m_iMyInt2 = 0;\n" + "}\n" ); + + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), err ); + } + + + void initvar_2constructors() + { + check( "class c\n" + "{\n" + " c();\n" + " c(bool b);" + "\n" + " void InitInt();\n" + "\n" + " int m_iMyInt;\n" + " int m_bMyBool;\n" + "}\n" + "\n" + "c::c()\n" + "{\n" + " m_bMyBool = false;\n" + " InitInt();" + "}\n" + "\n" + "c::c(bool b)\n" + "{\n" + " m_bMyBool = b;\n" + " InitInt();\n" + "}\n" + "\n" + "void c::InitInt()\n" + "{\n" + " m_iMyInt = 0;\n" + "}\n" ); + + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), err ); + } + + + void initvar_private_constructor() + { + check( "class Fred\n" + "{\n" + "private:\n" + " int var;\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{ }" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void initvar_destructor() + { + check( "class Fred\n" + "{\n" + "private:\n" + " int var;\n" + "public:\n" + " Fred() : var(0) {}\n" + " ~Fred() {}\n" + "};\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } +}; + +REGISTER_TEST( TestConstructors ) diff --git a/testdivision.cpp b/testdivision.cpp index c793997d6..27f4f1929 100644 --- a/testdivision.cpp +++ b/testdivision.cpp @@ -1,150 +1,150 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestDivision : public TestFixture -{ -public: - TestDivision() : TestFixture("TestDivision") - { } - -private: - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - - // Clear the error buffer.. - errout.str(""); - - // Check for unsigned divisions.. - CheckOther checkOther( &tokenizer, this ); - checkOther.CheckUnsignedDivision(); - } - - void run() - { - TEST_CASE( division1 ); - TEST_CASE( division2 ); - TEST_CASE( division3 ); - TEST_CASE( division4 ); - TEST_CASE( division5 ); - TEST_CASE( division6 ); - TEST_CASE( division7 ); - } - - void division1() - { - check( "void f()\n" - "{\n" - " int ivar = -2;\n" - " unsigned int uvar = 2;\n" - " return ivar / uvar;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Warning: Division with signed and unsigned operators\n"), errout.str() ); - } - - void division2() - { - check( "void f()\n" - "{\n" - " int ivar = -2;\n" - " unsigned int uvar = 2;\n" - " return uvar / ivar;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Warning: Division with signed and unsigned operators\n"), errout.str() ); - } - - void division3() - { - check( "typedef int s32;\n" - "typedef unsigned int u32;\n" - "void f()\n" - "{\n" - " s32 ivar = -2;\n" - " u32 uvar = 2;\n" - " return uvar / ivar;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:7]: Warning: Division with signed and unsigned operators\n"), errout.str() ); - } - - void division4() - { - check( "void f1()\n" - "{\n" - " int i1;\n" - "}\n" - "\n" - "void f2(unsigned int i1)\n" - "{\n" - " unsigned int i2;\n" - " result = i2 / i1;\n" - ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void division5() - { - check( "#define USER_HASH (16)\n" - "void foo()\n" - "{\n" - " unsigned int val = 32;\n" - " val = val / USER_HASH;\n" - ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void division6() - { - check( "void foo()\n" - "{\n" - " unsigned int val = 32;\n" - " int i = val / -2;\n" - ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Unsigned division. The result will be wrong.\n"), errout.str() ); - } - - void division7() - { - check( "void foo()\n" - "{\n" - " unsigned int val = 32;\n" - " int i = -96 / val;\n" - ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Unsigned division. The result will be wrong.\n"), errout.str() ); - } -}; - -REGISTER_TEST( TestDivision ) - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestDivision : public TestFixture +{ +public: + TestDivision() : TestFixture("TestDivision") + { } + +private: + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + + // Clear the error buffer.. + errout.str(""); + + // Check for unsigned divisions.. + CheckOther checkOther( &tokenizer, this ); + checkOther.CheckUnsignedDivision(); + } + + void run() + { + TEST_CASE( division1 ); + TEST_CASE( division2 ); + TEST_CASE( division3 ); + TEST_CASE( division4 ); + TEST_CASE( division5 ); + TEST_CASE( division6 ); + TEST_CASE( division7 ); + } + + void division1() + { + check( "void f()\n" + "{\n" + " int ivar = -2;\n" + " unsigned int uvar = 2;\n" + " return ivar / uvar;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Warning: Division with signed and unsigned operators\n"), errout.str() ); + } + + void division2() + { + check( "void f()\n" + "{\n" + " int ivar = -2;\n" + " unsigned int uvar = 2;\n" + " return uvar / ivar;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Warning: Division with signed and unsigned operators\n"), errout.str() ); + } + + void division3() + { + check( "typedef int s32;\n" + "typedef unsigned int u32;\n" + "void f()\n" + "{\n" + " s32 ivar = -2;\n" + " u32 uvar = 2;\n" + " return uvar / ivar;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:7]: Warning: Division with signed and unsigned operators\n"), errout.str() ); + } + + void division4() + { + check( "void f1()\n" + "{\n" + " int i1;\n" + "}\n" + "\n" + "void f2(unsigned int i1)\n" + "{\n" + " unsigned int i2;\n" + " result = i2 / i1;\n" + ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void division5() + { + check( "#define USER_HASH (16)\n" + "void foo()\n" + "{\n" + " unsigned int val = 32;\n" + " val = val / USER_HASH;\n" + ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void division6() + { + check( "void foo()\n" + "{\n" + " unsigned int val = 32;\n" + " int i = val / -2;\n" + ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Unsigned division. The result will be wrong.\n"), errout.str() ); + } + + void division7() + { + check( "void foo()\n" + "{\n" + " unsigned int val = 32;\n" + " int i = -96 / val;\n" + ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Unsigned division. The result will be wrong.\n"), errout.str() ); + } +}; + +REGISTER_TEST( TestDivision ) + + diff --git a/testfilelister.cpp b/testfilelister.cpp index f2333b8b2..dcd0eb4ad 100644 --- a/testfilelister.cpp +++ b/testfilelister.cpp @@ -1,53 +1,53 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 "testsuite.h" -#include "filelister.h" - -class TestFileLister : public TestFixture -{ -public: - TestFileLister() : TestFixture("TestFileLister") - { } - -private: - - void run() - { - TEST_CASE( simplify_path ); - } - - void simplify_path() - { - ASSERT_EQUALS( std::string( "index.h" ), FileLister::simplifyPath( "index.h" ) ); - ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/index.h" ) ); - ASSERT_EQUALS( std::string( "/path/" ), FileLister::simplifyPath( "/path/" ) ); - ASSERT_EQUALS( std::string( "/" ), FileLister::simplifyPath( "/" ) ); - ASSERT_EQUALS( std::string( "./index.h" ), FileLister::simplifyPath( "./index.h" ) ); - ASSERT_EQUALS( std::string( "../index.h" ), FileLister::simplifyPath( "../index.h" ) ); - ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../index.h" ) ); - ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../other/../index.h" ) ); - ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../other///././../index.h" ) ); - ASSERT_EQUALS( std::string( "../path/index.h" ), FileLister::simplifyPath( "../path/other/../index.h" ) ); - } - - -}; - -REGISTER_TEST( TestFileLister ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 "testsuite.h" +#include "filelister.h" + +class TestFileLister : public TestFixture +{ +public: + TestFileLister() : TestFixture("TestFileLister") + { } + +private: + + void run() + { + TEST_CASE( simplify_path ); + } + + void simplify_path() + { + ASSERT_EQUALS( std::string( "index.h" ), FileLister::simplifyPath( "index.h" ) ); + ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/index.h" ) ); + ASSERT_EQUALS( std::string( "/path/" ), FileLister::simplifyPath( "/path/" ) ); + ASSERT_EQUALS( std::string( "/" ), FileLister::simplifyPath( "/" ) ); + ASSERT_EQUALS( std::string( "./index.h" ), FileLister::simplifyPath( "./index.h" ) ); + ASSERT_EQUALS( std::string( "../index.h" ), FileLister::simplifyPath( "../index.h" ) ); + ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../index.h" ) ); + ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../other/../index.h" ) ); + ASSERT_EQUALS( std::string( "/index.h" ), FileLister::simplifyPath( "/path/../other///././../index.h" ) ); + ASSERT_EQUALS( std::string( "../path/index.h" ), FileLister::simplifyPath( "../path/other/../index.h" ) ); + } + + +}; + +REGISTER_TEST( TestFileLister ) diff --git a/testfunctionusage.cpp b/testfunctionusage.cpp index 395108672..6ecc12622 100644 --- a/testfunctionusage.cpp +++ b/testfunctionusage.cpp @@ -1,105 +1,105 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestFunctionUsage : public TestFixture -{ -public: - TestFunctionUsage() : TestFixture("TestFunctionUsage") - { } - -private: - - - void run() - { - TEST_CASE( incondition ); - TEST_CASE( return1 ); - TEST_CASE( callback1 ); - TEST_CASE( else1 ); - } - - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - - // Clear the error buffer.. - errout.str(""); - - // Check for unused functions.. - CheckFunctionUsage checkFunctionUsage(this); - checkFunctionUsage.parseTokens( tokenizer ); - checkFunctionUsage.check(); - } - - void incondition() - { - check( "int f1()\n" - "{\n" - " if (f1())\n" - " { }\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void return1() - { - check( "int f1()\n" - "{\n" - " return f1();\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void callback1() - { - check( "void f1()\n" - "{\n" - " void (*f)() = cond ? f1 : NULL;\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void else1() - { - check( "void f1()\n" - "{\n" - " if (cond) ;\n" - " else f1();\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } -}; - -REGISTER_TEST( TestFunctionUsage ) - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestFunctionUsage : public TestFixture +{ +public: + TestFunctionUsage() : TestFixture("TestFunctionUsage") + { } + +private: + + + void run() + { + TEST_CASE( incondition ); + TEST_CASE( return1 ); + TEST_CASE( callback1 ); + TEST_CASE( else1 ); + } + + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + + // Clear the error buffer.. + errout.str(""); + + // Check for unused functions.. + CheckFunctionUsage checkFunctionUsage(this); + checkFunctionUsage.parseTokens( tokenizer ); + checkFunctionUsage.check(); + } + + void incondition() + { + check( "int f1()\n" + "{\n" + " if (f1())\n" + " { }\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void return1() + { + check( "int f1()\n" + "{\n" + " return f1();\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void callback1() + { + check( "void f1()\n" + "{\n" + " void (*f)() = cond ? f1 : NULL;\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void else1() + { + check( "void f1()\n" + "{\n" + " if (cond) ;\n" + " else f1();\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } +}; + +REGISTER_TEST( TestFunctionUsage ) + diff --git a/testincompletestatement.cpp b/testincompletestatement.cpp index b7de36522..d51fb1871 100644 --- a/testincompletestatement.cpp +++ b/testincompletestatement.cpp @@ -1,91 +1,91 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestIncompleteStatement : public TestFixture -{ -public: - TestIncompleteStatement() : TestFixture("TestIncompleteStatement") - { } - -private: - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for unused variables.. - CheckOther checkOther( &tokenizer, this ); - checkOther.CheckIncompleteStatement(); - } - - void run() - { - TEST_CASE( test1 ); - TEST_CASE( test2 ); - } - - void test1() - { - check( "void foo()\n" - "{\n" - " const char def[] =\n" - "#ifdef ABC\n" - " \"abc\";\n" - "#else\n" - " \"not abc\";\n" - "#endif\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void test2() - { - // Todo: remove the ';' before the string - - check( "void foo()\n" - "{\n" - " ;\"abc\";\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant code: Found a statement that begins with string constant\n"), errout.str() ); - } -}; - -REGISTER_TEST( TestIncompleteStatement ) - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestIncompleteStatement : public TestFixture +{ +public: + TestIncompleteStatement() : TestFixture("TestIncompleteStatement") + { } + +private: + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for unused variables.. + CheckOther checkOther( &tokenizer, this ); + checkOther.CheckIncompleteStatement(); + } + + void run() + { + TEST_CASE( test1 ); + TEST_CASE( test2 ); + } + + void test1() + { + check( "void foo()\n" + "{\n" + " const char def[] =\n" + "#ifdef ABC\n" + " \"abc\";\n" + "#else\n" + " \"not abc\";\n" + "#endif\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void test2() + { + // Todo: remove the ';' before the string + + check( "void foo()\n" + "{\n" + " ;\"abc\";\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant code: Found a statement that begins with string constant\n"), errout.str() ); + } +}; + +REGISTER_TEST( TestIncompleteStatement ) + + diff --git a/testmemleak.cpp b/testmemleak.cpp index 36c8bd37c..bfd561677 100644 --- a/testmemleak.cpp +++ b/testmemleak.cpp @@ -1,1233 +1,1233 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestMemleak : public TestFixture -{ -public: - TestMemleak() : TestFixture("TestMemleak") - { } - -private: - void check( const char code[], bool showAll = false ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.setVarId(); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for memory leaks.. - Settings settings; - settings._debug = true; - settings._showAll = showAll; - tokenizer.fillFunctionList(); - CheckMemoryLeakClass checkMemoryLeak( &tokenizer, settings, this ); - checkMemoryLeak.CheckMemoryLeak(); - } - - void run() - { - TEST_CASE( simple1 ); - TEST_CASE( simple2 ); - TEST_CASE( simple3 ); - TEST_CASE( simple4 ); - TEST_CASE( simple5 ); - TEST_CASE( simple6 ); - TEST_CASE( simple7 ); +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestMemleak : public TestFixture +{ +public: + TestMemleak() : TestFixture("TestMemleak") + { } + +private: + void check( const char code[], bool showAll = false ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.setVarId(); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for memory leaks.. + Settings settings; + settings._debug = true; + settings._showAll = showAll; + tokenizer.fillFunctionList(); + CheckMemoryLeakClass checkMemoryLeak( &tokenizer, settings, this ); + checkMemoryLeak.CheckMemoryLeak(); + } + + void run() + { + TEST_CASE( simple1 ); + TEST_CASE( simple2 ); + TEST_CASE( simple3 ); + TEST_CASE( simple4 ); + TEST_CASE( simple5 ); + TEST_CASE( simple6 ); + TEST_CASE( simple7 ); TEST_CASE( simple8 ); - TEST_CASE( simple9 ); // Bug 2435468 - member function "free" - - TEST_CASE( use1 ); - TEST_CASE( use2 ); - - TEST_CASE( ifelse1 ); - TEST_CASE( ifelse2 ); - TEST_CASE( ifelse3 ); - TEST_CASE( ifelse4 ); - TEST_CASE( ifelse5 ); - TEST_CASE( ifelse6 ); - TEST_CASE( ifelse7 ); - TEST_CASE( ifelse8 ); - TEST_CASE( ifelse9 ); - - TEST_CASE( if1 ); - TEST_CASE( if2 ); - TEST_CASE( if3 ); - TEST_CASE( if4 ); - TEST_CASE( if5 ); + TEST_CASE( simple9 ); // Bug 2435468 - member function "free" + + TEST_CASE( use1 ); + TEST_CASE( use2 ); + + TEST_CASE( ifelse1 ); + TEST_CASE( ifelse2 ); + TEST_CASE( ifelse3 ); + TEST_CASE( ifelse4 ); + TEST_CASE( ifelse5 ); + TEST_CASE( ifelse6 ); + TEST_CASE( ifelse7 ); + TEST_CASE( ifelse8 ); + TEST_CASE( ifelse9 ); + + TEST_CASE( if1 ); + TEST_CASE( if2 ); + TEST_CASE( if3 ); + TEST_CASE( if4 ); + TEST_CASE( if5 ); TEST_CASE( if6 ); // Bug 2432631 - TEST_CASE( if7 ); // Bug 2401436 - - TEST_CASE( alwaysTrue ); - - TEST_CASE( forwhile1 ); - TEST_CASE( forwhile2 ); - TEST_CASE( forwhile3 ); - TEST_CASE( forwhile4 ); - TEST_CASE( forwhile5 ); - TEST_CASE( forwhile6 ); - TEST_CASE( forwhile7 ); - TEST_CASE( forwhile8 ); // Bug 2429936 - - TEST_CASE( dowhile1 ); - - TEST_CASE( switch1 ); - TEST_CASE( switch2 ); - - TEST_CASE( ret1 ); - TEST_CASE( ret2 ); - TEST_CASE( ret3 ); - TEST_CASE( ret4 ); - - TEST_CASE( mismatch1 ); - - TEST_CASE( func1 ); - TEST_CASE( func2 ); - TEST_CASE( func3 ); - TEST_CASE( func4 ); - TEST_CASE( func5 ); - TEST_CASE( func6 ); - // TODO TEST_CASE( func7 ); - TEST_CASE( func8 ); // Using callback - TEST_CASE( func9 ); // Embedding the function call in a if-condition - - TEST_CASE( class1 ); - TEST_CASE( class2 ); - - TEST_CASE( throw1 ); - - TEST_CASE( linux_list_1 ); - - TEST_CASE( sizeof1 ); - - TEST_CASE( realloc1 ); - TEST_CASE( realloc2 ); - - TEST_CASE( assign ); - - // TODO TEST_CASE( varid ); - - TEST_CASE( cast1 ); - TEST_CASE( cast2 ); - TEST_CASE( cast3 ); - - - // TODO TEST_CASE( structmember1 ); - } - - - - void simple1() - { - check( "void f()\n" - "{\n" - " int *a = new int[10];\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() ); - } - - void simple2() - { - check( "Fred *NewFred()\n" - "{\n" - " Fred *f = new Fred;\n" - " return f;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void simple3() - { - check( "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " return (char *)s;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void simple4() - { - check( "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " return 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: s\n"), errout.str() ); - } - - - void simple5() - { - check( "static char *f()\n" - "{\n" - " struct *str = new strlist;\n" - " return &str->s;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void simple6() - { - check( "static void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " char *str2 = (char *)str;\n" - " free(str2);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void simple7() - { - // A garbage collector may delete f automaticly - check( "class Fred;\n" - "void foo()\n" - "{\n" - " Fred *f = new Fred;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void simple8() - { - check( "char * foo ()\n" - "{\n" - " char *str = strdup(\"abc\");\n" - " if (somecondition)\n" - " for (i = 0; i < 2; i++)\n" - " { }\n" - " return str;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - + TEST_CASE( if7 ); // Bug 2401436 - void simple9() - { - check( "void foo()\n" - "{\n" - " MyClass *c = new MyClass();\n" - " c->free(c);\n" - " delete c;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } + TEST_CASE( alwaysTrue ); + + TEST_CASE( forwhile1 ); + TEST_CASE( forwhile2 ); + TEST_CASE( forwhile3 ); + TEST_CASE( forwhile4 ); + TEST_CASE( forwhile5 ); + TEST_CASE( forwhile6 ); + TEST_CASE( forwhile7 ); + TEST_CASE( forwhile8 ); // Bug 2429936 + + TEST_CASE( dowhile1 ); + + TEST_CASE( switch1 ); + TEST_CASE( switch2 ); + + TEST_CASE( ret1 ); + TEST_CASE( ret2 ); + TEST_CASE( ret3 ); + TEST_CASE( ret4 ); + + TEST_CASE( mismatch1 ); + + TEST_CASE( func1 ); + TEST_CASE( func2 ); + TEST_CASE( func3 ); + TEST_CASE( func4 ); + TEST_CASE( func5 ); + TEST_CASE( func6 ); + // TODO TEST_CASE( func7 ); + TEST_CASE( func8 ); // Using callback + TEST_CASE( func9 ); // Embedding the function call in a if-condition + + TEST_CASE( class1 ); + TEST_CASE( class2 ); + + TEST_CASE( throw1 ); + + TEST_CASE( linux_list_1 ); + + TEST_CASE( sizeof1 ); + + TEST_CASE( realloc1 ); + TEST_CASE( realloc2 ); + + TEST_CASE( assign ); + + // TODO TEST_CASE( varid ); + + TEST_CASE( cast1 ); + TEST_CASE( cast2 ); + TEST_CASE( cast3 ); + + + // TODO TEST_CASE( structmember1 ); + } - - - - - void use1() - { - check( "void foo()\n" - "{\n" - " char *str;\n" - " if (somecondition)\n" - " str = strdup(\"abc\");\n" - " if (somecondition)\n" - " DeleteString(str);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void use2() - { - check( "void foo()\n" - "{\n" - " char *str = strdup(\"abc\");\n" - " if ( abc ) { memset(str, 0, 3); }\n" - " *somestr = str;\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - - - void ifelse1() - { - check( "void f()\n" - "{\n" - " int *a = new int[10];\n" - " if (a)\n" - " {\n" - " delete [] a;\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ifelse2() - { - check( "void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " if (somecondition)\n" - " {\n" - " return;\n" - " }\n" - " free(str);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: str\n"), errout.str() ); - } - - - void ifelse3() - { - check( "void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " if (a==b)\n" - " {\n" - " free(str);\n" - " return;\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); + void simple1() + { + check( "void f()\n" + "{\n" + " int *a = new int[10];\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() ); + } + + void simple2() + { + check( "Fred *NewFred()\n" + "{\n" + " Fred *f = new Fred;\n" + " return f;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void simple3() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " return (char *)s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple4() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " return 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: s\n"), errout.str() ); + } + + + void simple5() + { + check( "static char *f()\n" + "{\n" + " struct *str = new strlist;\n" + " return &str->s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple6() + { + check( "static void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " char *str2 = (char *)str;\n" + " free(str2);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple7() + { + // A garbage collector may delete f automaticly + check( "class Fred;\n" + "void foo()\n" + "{\n" + " Fred *f = new Fred;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple8() + { + check( "char * foo ()\n" + "{\n" + " char *str = strdup(\"abc\");\n" + " if (somecondition)\n" + " for (i = 0; i < 2; i++)\n" + " { }\n" + " return str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void simple9() + { + check( "void foo()\n" + "{\n" + " MyClass *c = new MyClass();\n" + " c->free(c);\n" + " delete c;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + + + void use1() + { + check( "void foo()\n" + "{\n" + " char *str;\n" + " if (somecondition)\n" + " str = strdup(\"abc\");\n" + " if (somecondition)\n" + " DeleteString(str);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void use2() + { + check( "void foo()\n" + "{\n" + " char *str = strdup(\"abc\");\n" + " if ( abc ) { memset(str, 0, 3); }\n" + " *somestr = str;\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + + void ifelse1() + { + check( "void f()\n" + "{\n" + " int *a = new int[10];\n" + " if (a)\n" + " {\n" + " delete [] a;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse2() + { + check( "void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " if (somecondition)\n" + " {\n" + " return;\n" + " }\n" + " free(str);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: str\n"), errout.str() ); + } + + + void ifelse3() + { + check( "void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " if (a==b)\n" + " {\n" + " free(str);\n" + " return;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + + check( "void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " if (a==b)\n" + " {\n" + " free(str);\n" + " return;\n" + " }\n" + "}\n", true ); + ASSERT_EQUALS( std::string("[test.cpp:9]: Memory leak: str\n"), errout.str() ); + } + + + void ifelse4() + { + check( "void f()\n" + "{\n" + " char *str = new char[10];\n" + " if (a==b)\n" + " {\n" + " delete [] str;\n" + " return;\n" + " }\n" + " delete [] str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse5() + { + check( "void f()\n" + "{\n" + " char *str;\n" + " if (somecondition)\n" + " {\n" + " str = new char[100];\n" + " }\n" + " else\n" + " {\n" + " return;\n" + " }\n" + " delete [] str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse6() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[100];\n" + " if ( a == b )\n" + " {\n" + " return s;\n" + " }\n" + " return NULL;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: s\n"), errout.str() ); + } + + + void ifelse7() + { + check( "static char *f()\n" + "{\n" + " char *s;\n" + " if ( abc )\n" + " {\n" + " s = new char[10];\n" + " }\n" + " return s;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse8() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[10];\n" + " if ( s )\n" + " {\n" + " return s;\n" + " }\n" + " return 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ifelse9() + { + check( "static char *f()\n" + "{\n" + " char *s = new char[10];\n" + " if ( ghfgf )\n" + " {\n" + " delete [] s;\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + void if1() + { + check( "void f()\n" + "{\n" + " struct abc *p = new abc;\n" + " p->a = new char[100];\n" + " if ( ! p->a )\n" + " return;\n" + " foo(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: p\n"), errout.str() ); + } + + void if2() + { + check( "void f()\n" + "{\n" + " struct smp_alt_module *smp;\n" + " smp = kzalloc(sizeof(*smp), GFP_KERNEL);\n" + " if (NULL == smp)\n" + " return;\n" + " kfree( smp );\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void if3() + { + check( "void f()\n" + "{\n" + " char *s = new char[100];\n" + " if (0 != s)\n" + " foo(s);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void if4() + { + check( "void f()\n" + "{\n" + " char *s;\n" + " bool b = true;\n" + " if (b && (s = malloc(256)))\n" + " ;\n" + " if (b)\n" + " free(s);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void if5() + { + check( "void f()\n" + "{\n" + " char *p = malloc(256);\n" + " if (somecondition && !p)\n" + " return;\n" + " free(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void if6() + { + check( "void f()\n" + "{\n" + " FILE *a = 0;\n" + " a = fopen(\"test.txt\", \"rw\");\n" + " if( a == 0 )\n" + " {\n" + " a = fopen(\"test.txt\", \"r\");\n" + " }\n" + "\n" + " fclose( a );\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void if7() + { + check( "void f( bool b )\n" + "{\n" + " int *a=0;\n" + " if( b )\n" + " {\n" + " a = new int[10];\n" + " }\n" + "\n" + " if( b )\n" + " delete [] a;\n" + " else {}\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + void alwaysTrue() + { + check( "void f()\n" + "{\n" + " char *p = 0;\n" + " for (;;)\n" + " {\n" + " p = malloc(256);\n" + " if (1)\n" + " break;\n" + " }\n" + " free(p);\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string(""), err ); + } + + + + + + void forwhile1() + { + check("void f()\n" + "{\n" + " char *str = strdup(\"hello\");\n" + " while (condition)\n" + " {\n" + " if (condition)\n" + " {\n" + " break;\n" + " }\n" + " }\n" + " free(str);\n" + "}\n"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void forwhile2() + { + check("void f()\n" + "{\n" + " for (int i = 0; i < j; i++)\n" + " {\n" + " char *str = strdup(\"hello\");\n" + " if (condition)\n" + " continue;\n" + " free(str);\n" + " }\n" + "}\n"); + ASSERT_EQUALS( std::string("[test.cpp:7]: Memory leak: str\n"), errout.str() ); + } + + + void forwhile3() + { + check( "void f()\n" + "{\n" + " char *str = 0;\n" + " for (int i = 0; i < 10; i++)\n" + " {\n" + " str = strdup(\"hello\");\n" + " }\n" + " free(str);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: str\n"), errout.str() ); + } + + + void forwhile4() + { + check( "void f()\n" + "{\n" + " char *str = 0;\n" + " for (int i = 0; i < 10; i++)\n" + " {\n" + " str = strdup(\"hello\");\n" + " if (str) { }\n" + " }\n" + " free(str);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: str\n"), errout.str() ); + } + + + void forwhile5() + { + check( "void f(const char **a)\n" + "{\n" + " char *str = 0;\n" + " for (int i = 0; i < 10 && !str; ++i)\n" + " {\n" + " str = strdup(a[i]);\n" + " }\n" + " return str;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void forwhile6() + { + check( "void f(const char **a)\n" + "{\n" + " char *str = 0;\n" + " for (int i = 0; i < 10 && !str; ++i)\n" + " {\n" + " str = strdup(a[i]);\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: str\n"), errout.str() ); + } + + + void forwhile7() + { + check("void f()\n" + "{\n" + " for (int i = 0; i < j; i++)\n" + " {\n" + " char *str = strdup(\"hello\");\n" + " if (condition)\n" + " break;\n" + " free(str);\n" + " }\n" + "}\n"); + ASSERT_EQUALS( std::string("[test.cpp:7]: Memory leak: str\n"), errout.str() ); + } + + + void forwhile8() + { + check("char *f()\n" + "{\n" + " char *a = 0;\n" + " int i = 0;\n" + " for( ;; )\n" + " {\n" + " i++;\n" + " a = realloc( a, i );\n" + " if( !a )\n" + " return 0;\n" + "\n" + " if( i > 10 )\n" + " break;\n" + " }\n" + "\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + void dowhile1() + { + check( "void f()\n" + "{\n" + " char *str = strdup(\"abc\");\n" + " do\n" + " {\n" + " str = strdup(\"def\");\n" + " }\n" + " while (!str);\n" + " return str;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: str\n"), errout.str() ); + } + + + + void switch1() + { + check("void f()\n" + "{\n" + " char *str = new char[10];\n" + " switch (abc)\n" + " {\n" + " case 1:\n" + " break;\n" + " };\n" + " delete [] str;\n" + "}\n"); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void switch2() + { + check("void f()\n" + "{\n" + " char *str = new char[10];\n" + " switch (abc)\n" + " {\n" + " case 1:\n" + " delete [] str;\n" + " break;\n" + " default:\n" + " break;\n" + " };\n" + "}\n"); + ASSERT_EQUALS( std::string("[test.cpp:12]: Memory leak: str\n"), errout.str() ); + } + + + + + + void ret1() + { + check( "char *f( char **str )\n" + "{\n" + " char *ret = malloc( 10 );\n" + " return *str = ret;\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void ret2() + { + check( "void foo()\n" + "{\n" + " struct ABC *abc = new ABC;\n" + " abc->a = new char[10];\n" + " if ( ! abc->a )\n" + " return;\n" + " delete [] abc->a;\n" + " delete abc;\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: abc\n"), errout.str() ); + } + + void ret3() + { + check( "void foo()\n" + "{\n" + " FILE *filep = fopen(\"myfile.txt\",\"w\");\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:4]: Resource leak: filep\n"), errout.str() ); + } + + void ret4() + { + check( "void foo()\n" + "{\n" + " FILE *p = popen( \"ls -l\", \"r\");\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:4]: Resource leak: p\n"), errout.str() ); + } + + void mismatch1() + { + check( "void f()\n" + "{\n" + " int *a = new int[10];\n" + " free(a);\n" + "}\n"); + ASSERT_EQUALS( std::string("[test.cpp:4]: Mismatching allocation and deallocation: a\n"), errout.str() ); + } + + + + + + //////////////////////////////////////////////// + // function calls + //////////////////////////////////////////////// + + + void func1() + { + check( "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void func2() + { + check( "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo.add(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void func3() + { + check( "static void foo(const char *str)\n" + "{ }\n" + "\n" + "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: p\n"), errout.str() ); + } + + + void func4() + { + check( "static void foo(char *str)\n" + "{\n" + " delete [] str;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void func5() + { + check( "static void foo(char *str)\n" + "{\n" + " delete str;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string("[test.cpp:9] -> [test.cpp:3]: Mismatching allocation and deallocation: str\n"), err ); + } + + + void func6() + { + check( "static void foo(char *str)\n" + "{\n" + " goto abc;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string("[test.cpp:10]: Memory leak: p\n"), err ); + } + + + void func7() + { + check( "static void foo(char *str)\n" + "{\n" + " if (abc)\n" + " return;" + " delete [] str;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char *p = new char[100];\n" + " foo(p);\n" + "}\n" ); + std::string err( errout.str() ); + ASSERT_EQUALS( std::string("[test.cpp:11]: Memory leak: p\n"), err ); + } + + + void func8() + { + check( "static void foo()\n" + "{\n" + " char *str = new char[100];" + " (*release)(str);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void func9() + { + check( "int b()\n" + "{\n" + " return 0;\n" + "}\n" + "\n" + "void a()\n" + "{\n" + " char *a = new char[10];\n" + " if (b())\n" + " return;\n" + " delete [] a;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + +/* + void func3() + { + check( "static char *dmalloc()\n" + "{\n" + " char *p = new char[100];\n" + " return p;\n" + "}\n" + "static void f()\n" + "{\n" + " char *p = dmalloc();\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:9]: Memory leak: p\n"), errout.str() ); + } + + + void func4() + { + check( "static char *dmalloc()\n" + "{\n" + " char *p = new char[100];\n" + " return p;\n" + "}\n" + "static void f()\n" + "{\n" + " char *p = dmalloc();\n" + " delete p;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:9]: Mismatching allocation and deallocation: p\n"), errout.str() ); + } +*/ + + + + + + void class1() + { + check( "class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + " char *str2;\n" + "public:\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + "}\n" + "\n" + "Fred::~Fred()\n" + "{\n" + " delete [] str2;\n" + "}\n", true ); + + ASSERT_EQUALS( std::string("[test.cpp:1]: Memory leak: Fred::str1\n"), errout.str() ); + } + + + void class2() + { + check( "class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + "public:\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + "}\n" + "\n" + "Fred::~Fred()\n" + "{\n" + " free(str1);\n" + "}\n", true ); + + ASSERT_EQUALS( std::string("[test.cpp:17]: Mismatching allocation and deallocation: Fred::str1\n"), errout.str() ); + } + + + + + void throw1() + { + check( "void foo()\n" + "{\n" + " char *str = new char[10];\n" + " if ( ! abc )\n" + " throw 123;\n" + " delete [] str;\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: str\n"), errout.str() ); + } + + + + + void linux_list_1() + { + check( "struct AB\n" + "{\n" + " int a;\n" + " int b;\n" + "};\n" + "void foo()\n" + "{\n" + " struct AB *ab = new AB;\n" + " func(&ab->a);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + void sizeof1() + { + check( "void f()\n" + "{\n" + " struct s_t s1;\n" + " struct s_t cont *p = &s1;\n" + " struct s_t *s2;\n" + "\n" + " memset(p, 0, sizeof(*p));\n" + "\n" + " s2 = (struct s_t *) malloc(sizeof(*s2));\n" + "\n" + " if (s2->value != 0)\n" + " return;\n" + "\n" + " free(s2);\n" + "\n" + " return;\n" + "}\n" ); + + std::string err( errout.str() ); + + ASSERT_EQUALS( std::string("[test.cpp:12]: Memory leak: s2\n"), err ); + } + + + void realloc1() + { + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = realloc(a, 100);\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: a\n"), errout.str() ); + } + + void realloc2() + { + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = (char *)realloc(a, 100);\n" + " free(a);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void assign() + { + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = 0;\n" + " free(a);\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:3]: Memory leak: a\n"), errout.str() ); + + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " char *p = a;\n" + " free(p);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a += 10;\n" + " free(a - 10);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + + check( "void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = (void *)a + 10;\n" + " free(a - 10);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + void varid() + { + check( "void foo()\n" + "{\n" + " char *p = malloc(100);\n" + " {\n" + " char *p = 0;\n" + " delete p;\n" + " }\n" + " free(p);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void cast1() + { + check( "void foo()\n" + "{\n" + " char *a = reinterpret_cast(malloc(10));\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() ); + } + + void cast2() + { + check( "void foo()\n" + "{\n" + " char *a = malloc(10);\n" + " free((void *)a);\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void cast3() + { + check( "void foo()\n" + "{\n" + " char *a = malloc(10);\n" + " free(reinterpret_cast(a));\n" + "}\n" ); + + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + void structmember1() + { + check( "void f()\n" + "{\n" + " struct ABC *abc = new ABC;\n" + " abc->a = new char[100];\n" + " delete abc;\n" + "}\n" ); + + ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: abc.a\n"), errout.str() ); + } + +}; + +REGISTER_TEST( TestMemleak ) + - check( "void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " if (a==b)\n" - " {\n" - " free(str);\n" - " return;\n" - " }\n" - "}\n", true ); - ASSERT_EQUALS( std::string("[test.cpp:9]: Memory leak: str\n"), errout.str() ); - } - - - void ifelse4() - { - check( "void f()\n" - "{\n" - " char *str = new char[10];\n" - " if (a==b)\n" - " {\n" - " delete [] str;\n" - " return;\n" - " }\n" - " delete [] str;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ifelse5() - { - check( "void f()\n" - "{\n" - " char *str;\n" - " if (somecondition)\n" - " {\n" - " str = new char[100];\n" - " }\n" - " else\n" - " {\n" - " return;\n" - " }\n" - " delete [] str;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ifelse6() - { - check( "static char *f()\n" - "{\n" - " char *s = new char[100];\n" - " if ( a == b )\n" - " {\n" - " return s;\n" - " }\n" - " return NULL;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: s\n"), errout.str() ); - } - - - void ifelse7() - { - check( "static char *f()\n" - "{\n" - " char *s;\n" - " if ( abc )\n" - " {\n" - " s = new char[10];\n" - " }\n" - " return s;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ifelse8() - { - check( "static char *f()\n" - "{\n" - " char *s = new char[10];\n" - " if ( s )\n" - " {\n" - " return s;\n" - " }\n" - " return 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ifelse9() - { - check( "static char *f()\n" - "{\n" - " char *s = new char[10];\n" - " if ( ghfgf )\n" - " {\n" - " delete [] s;\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - void if1() - { - check( "void f()\n" - "{\n" - " struct abc *p = new abc;\n" - " p->a = new char[100];\n" - " if ( ! p->a )\n" - " return;\n" - " foo(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: p\n"), errout.str() ); - } - - void if2() - { - check( "void f()\n" - "{\n" - " struct smp_alt_module *smp;\n" - " smp = kzalloc(sizeof(*smp), GFP_KERNEL);\n" - " if (NULL == smp)\n" - " return;\n" - " kfree( smp );\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void if3() - { - check( "void f()\n" - "{\n" - " char *s = new char[100];\n" - " if (0 != s)\n" - " foo(s);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void if4() - { - check( "void f()\n" - "{\n" - " char *s;\n" - " bool b = true;\n" - " if (b && (s = malloc(256)))\n" - " ;\n" - " if (b)\n" - " free(s);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void if5() - { - check( "void f()\n" - "{\n" - " char *p = malloc(256);\n" - " if (somecondition && !p)\n" - " return;\n" - " free(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void if6() - { - check( "void f()\n" - "{\n" - " FILE *a = 0;\n" - " a = fopen(\"test.txt\", \"rw\");\n" - " if( a == 0 )\n" - " {\n" - " a = fopen(\"test.txt\", \"r\");\n" - " }\n" - "\n" - " fclose( a );\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void if7() - { - check( "void f( bool b )\n" - "{\n" - " int *a=0;\n" - " if( b )\n" - " {\n" - " a = new int[10];\n" - " }\n" - "\n" - " if( b )\n" - " delete [] a;\n" - " else {}\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - - void alwaysTrue() - { - check( "void f()\n" - "{\n" - " char *p = 0;\n" - " for (;;)\n" - " {\n" - " p = malloc(256);\n" - " if (1)\n" - " break;\n" - " }\n" - " free(p);\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string(""), err ); - } - - - - - - void forwhile1() - { - check("void f()\n" - "{\n" - " char *str = strdup(\"hello\");\n" - " while (condition)\n" - " {\n" - " if (condition)\n" - " {\n" - " break;\n" - " }\n" - " }\n" - " free(str);\n" - "}\n"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void forwhile2() - { - check("void f()\n" - "{\n" - " for (int i = 0; i < j; i++)\n" - " {\n" - " char *str = strdup(\"hello\");\n" - " if (condition)\n" - " continue;\n" - " free(str);\n" - " }\n" - "}\n"); - ASSERT_EQUALS( std::string("[test.cpp:7]: Memory leak: str\n"), errout.str() ); - } - - - void forwhile3() - { - check( "void f()\n" - "{\n" - " char *str = 0;\n" - " for (int i = 0; i < 10; i++)\n" - " {\n" - " str = strdup(\"hello\");\n" - " }\n" - " free(str);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: str\n"), errout.str() ); - } - - - void forwhile4() - { - check( "void f()\n" - "{\n" - " char *str = 0;\n" - " for (int i = 0; i < 10; i++)\n" - " {\n" - " str = strdup(\"hello\");\n" - " if (str) { }\n" - " }\n" - " free(str);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: str\n"), errout.str() ); - } - - - void forwhile5() - { - check( "void f(const char **a)\n" - "{\n" - " char *str = 0;\n" - " for (int i = 0; i < 10 && !str; ++i)\n" - " {\n" - " str = strdup(a[i]);\n" - " }\n" - " return str;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void forwhile6() - { - check( "void f(const char **a)\n" - "{\n" - " char *str = 0;\n" - " for (int i = 0; i < 10 && !str; ++i)\n" - " {\n" - " str = strdup(a[i]);\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: str\n"), errout.str() ); - } - - - void forwhile7() - { - check("void f()\n" - "{\n" - " for (int i = 0; i < j; i++)\n" - " {\n" - " char *str = strdup(\"hello\");\n" - " if (condition)\n" - " break;\n" - " free(str);\n" - " }\n" - "}\n"); - ASSERT_EQUALS( std::string("[test.cpp:7]: Memory leak: str\n"), errout.str() ); - } - - - void forwhile8() - { - check("char *f()\n" - "{\n" - " char *a = 0;\n" - " int i = 0;\n" - " for( ;; )\n" - " {\n" - " i++;\n" - " a = realloc( a, i );\n" - " if( !a )\n" - " return 0;\n" - "\n" - " if( i > 10 )\n" - " break;\n" - " }\n" - "\n" - " return a;\n" - "}\n"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - - void dowhile1() - { - check( "void f()\n" - "{\n" - " char *str = strdup(\"abc\");\n" - " do\n" - " {\n" - " str = strdup(\"def\");\n" - " }\n" - " while (!str);\n" - " return str;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: str\n"), errout.str() ); - } - - - - void switch1() - { - check("void f()\n" - "{\n" - " char *str = new char[10];\n" - " switch (abc)\n" - " {\n" - " case 1:\n" - " break;\n" - " };\n" - " delete [] str;\n" - "}\n"); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void switch2() - { - check("void f()\n" - "{\n" - " char *str = new char[10];\n" - " switch (abc)\n" - " {\n" - " case 1:\n" - " delete [] str;\n" - " break;\n" - " default:\n" - " break;\n" - " };\n" - "}\n"); - ASSERT_EQUALS( std::string("[test.cpp:12]: Memory leak: str\n"), errout.str() ); - } - - - - - - void ret1() - { - check( "char *f( char **str )\n" - "{\n" - " char *ret = malloc( 10 );\n" - " return *str = ret;\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void ret2() - { - check( "void foo()\n" - "{\n" - " struct ABC *abc = new ABC;\n" - " abc->a = new char[10];\n" - " if ( ! abc->a )\n" - " return;\n" - " delete [] abc->a;\n" - " delete abc;\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:6]: Memory leak: abc\n"), errout.str() ); - } - - void ret3() - { - check( "void foo()\n" - "{\n" - " FILE *filep = fopen(\"myfile.txt\",\"w\");\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:4]: Resource leak: filep\n"), errout.str() ); - } - - void ret4() - { - check( "void foo()\n" - "{\n" - " FILE *p = popen( \"ls -l\", \"r\");\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:4]: Resource leak: p\n"), errout.str() ); - } - - void mismatch1() - { - check( "void f()\n" - "{\n" - " int *a = new int[10];\n" - " free(a);\n" - "}\n"); - ASSERT_EQUALS( std::string("[test.cpp:4]: Mismatching allocation and deallocation: a\n"), errout.str() ); - } - - - - - - //////////////////////////////////////////////// - // function calls - //////////////////////////////////////////////// - - - void func1() - { - check( "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void func2() - { - check( "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo.add(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void func3() - { - check( "static void foo(const char *str)\n" - "{ }\n" - "\n" - "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:8]: Memory leak: p\n"), errout.str() ); - } - - - void func4() - { - check( "static void foo(char *str)\n" - "{\n" - " delete [] str;\n" - "}\n" - "\n" - "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void func5() - { - check( "static void foo(char *str)\n" - "{\n" - " delete str;\n" - "}\n" - "\n" - "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string("[test.cpp:9] -> [test.cpp:3]: Mismatching allocation and deallocation: str\n"), err ); - } - - - void func6() - { - check( "static void foo(char *str)\n" - "{\n" - " goto abc;\n" - "}\n" - "\n" - "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string("[test.cpp:10]: Memory leak: p\n"), err ); - } - - - void func7() - { - check( "static void foo(char *str)\n" - "{\n" - " if (abc)\n" - " return;" - " delete [] str;\n" - "}\n" - "\n" - "static void f()\n" - "{\n" - " char *p = new char[100];\n" - " foo(p);\n" - "}\n" ); - std::string err( errout.str() ); - ASSERT_EQUALS( std::string("[test.cpp:11]: Memory leak: p\n"), err ); - } - - - void func8() - { - check( "static void foo()\n" - "{\n" - " char *str = new char[100];" - " (*release)(str);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void func9() - { - check( "int b()\n" - "{\n" - " return 0;\n" - "}\n" - "\n" - "void a()\n" - "{\n" - " char *a = new char[10];\n" - " if (b())\n" - " return;\n" - " delete [] a;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - -/* - void func3() - { - check( "static char *dmalloc()\n" - "{\n" - " char *p = new char[100];\n" - " return p;\n" - "}\n" - "static void f()\n" - "{\n" - " char *p = dmalloc();\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:9]: Memory leak: p\n"), errout.str() ); - } - - - void func4() - { - check( "static char *dmalloc()\n" - "{\n" - " char *p = new char[100];\n" - " return p;\n" - "}\n" - "static void f()\n" - "{\n" - " char *p = dmalloc();\n" - " delete p;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:9]: Mismatching allocation and deallocation: p\n"), errout.str() ); - } -*/ - - - - - - void class1() - { - check( "class Fred\n" - "{\n" - "private:\n" - " char *str1;\n" - " char *str2;\n" - "public:\n" - " Fred();\n" - " ~Fred();\n" - "};\n" - "\n" - "Fred::Fred()\n" - "{\n" - " str1 = new char[10];\n" - " str2 = new char[10];\n" - "}\n" - "\n" - "Fred::~Fred()\n" - "{\n" - " delete [] str2;\n" - "}\n", true ); - - ASSERT_EQUALS( std::string("[test.cpp:1]: Memory leak: Fred::str1\n"), errout.str() ); - } - - - void class2() - { - check( "class Fred\n" - "{\n" - "private:\n" - " char *str1;\n" - "public:\n" - " Fred();\n" - " ~Fred();\n" - "};\n" - "\n" - "Fred::Fred()\n" - "{\n" - " str1 = new char[10];\n" - "}\n" - "\n" - "Fred::~Fred()\n" - "{\n" - " free(str1);\n" - "}\n", true ); - - ASSERT_EQUALS( std::string("[test.cpp:17]: Mismatching allocation and deallocation: Fred::str1\n"), errout.str() ); - } - - - - - void throw1() - { - check( "void foo()\n" - "{\n" - " char *str = new char[10];\n" - " if ( ! abc )\n" - " throw 123;\n" - " delete [] str;\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: str\n"), errout.str() ); - } - - - - - void linux_list_1() - { - check( "struct AB\n" - "{\n" - " int a;\n" - " int b;\n" - "};\n" - "void foo()\n" - "{\n" - " struct AB *ab = new AB;\n" - " func(&ab->a);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - void sizeof1() - { - check( "void f()\n" - "{\n" - " struct s_t s1;\n" - " struct s_t cont *p = &s1;\n" - " struct s_t *s2;\n" - "\n" - " memset(p, 0, sizeof(*p));\n" - "\n" - " s2 = (struct s_t *) malloc(sizeof(*s2));\n" - "\n" - " if (s2->value != 0)\n" - " return;\n" - "\n" - " free(s2);\n" - "\n" - " return;\n" - "}\n" ); - - std::string err( errout.str() ); - - ASSERT_EQUALS( std::string("[test.cpp:12]: Memory leak: s2\n"), err ); - } - - - void realloc1() - { - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " a = realloc(a, 100);\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: a\n"), errout.str() ); - } - - void realloc2() - { - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " a = (char *)realloc(a, 100);\n" - " free(a);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void assign() - { - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " a = 0;\n" - " free(a);\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:3]: Memory leak: a\n"), errout.str() ); - - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " char *p = a;\n" - " free(p);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " a += 10;\n" - " free(a - 10);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - - check( "void foo()\n" - "{\n" - " char *a = (char *)malloc(10);\n" - " a = (void *)a + 10;\n" - " free(a - 10);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - void varid() - { - check( "void foo()\n" - "{\n" - " char *p = malloc(100);\n" - " {\n" - " char *p = 0;\n" - " delete p;\n" - " }\n" - " free(p);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void cast1() - { - check( "void foo()\n" - "{\n" - " char *a = reinterpret_cast(malloc(10));\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() ); - } - - void cast2() - { - check( "void foo()\n" - "{\n" - " char *a = malloc(10);\n" - " free((void *)a);\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void cast3() - { - check( "void foo()\n" - "{\n" - " char *a = malloc(10);\n" - " free(reinterpret_cast(a));\n" - "}\n" ); - - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - - void structmember1() - { - check( "void f()\n" - "{\n" - " struct ABC *abc = new ABC;\n" - " abc->a = new char[100];\n" - " delete abc;\n" - "}\n" ); - - ASSERT_EQUALS( std::string("[test.cpp:5]: Memory leak: abc.a\n"), errout.str() ); - } - -}; - -REGISTER_TEST( TestMemleak ) - - diff --git a/testpreprocessor.cpp b/testpreprocessor.cpp index dc58c7b52..f71ab1092 100644 --- a/testpreprocessor.cpp +++ b/testpreprocessor.cpp @@ -1,446 +1,446 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -class TestPreprocessor : public TestFixture -{ -public: - TestPreprocessor() : TestFixture("TestPreprocessor") - { } - -private: - - void run() - { - // Just read the code into a string. Perform simple cleanup of the code - TEST_CASE(readCode); - - // The bug that started the whole work with the new preprocessor - TEST_CASE( Bug2190219 ); - - TEST_CASE( test1 ); - TEST_CASE( test2 ); - TEST_CASE( test3 ); - TEST_CASE( test4 ); - TEST_CASE( test5 ); - - TEST_CASE( comments1 ); - - TEST_CASE( if0 ); - TEST_CASE( if1 ); - - TEST_CASE( elif ); - - TEST_CASE( include1 ); - - TEST_CASE( if_cond1 ); - - TEST_CASE( multiline ); - } - - - void readCode() - { - const char code[] = " \t a //\n" - " #aa\t /* remove this */\tb \r\n"; - Preprocessor p; - std::istringstream istr(code); - std::string codestr( p.read(istr,"") ); - ASSERT_EQUALS( "a \n#aa b \n", codestr ); - } - - - bool cmpmaps(const std::map &m1, const std::map &m2) - { - // Begin by checking the sizes - if ( m1.size() != m2.size() ) - return false; - - // Check each item in the maps.. - for ( std::map::const_iterator it1 = m1.begin(); it1 != m1.end(); ++it1 ) - { - std::string s1 = it1->first; - std::map::const_iterator it2 = m2.find(s1); - if ( it2 == m2.end() ) - return false; - else - { - std::string s1 = it1->second; - std::string s2 = it2->second; - if ( s1 != s2 ) - return false; - } - } - - // No diffs were found - return true; - } - - - void Bug2190219() - { - const char filedata[] = "int main()\n" - "{\n" - "#ifdef __cplusplus\n" - " int* flags = new int[10];\n" - "#else\n" - " int* flags = (int*)malloc((10)*sizeof(int));\n" - "#endif\n" - "\n" - "#ifdef __cplusplus\n" - " delete [] flags;\n" - "#else\n" - " free(flags);\n" - "#endif\n" - "}\n"; - - // Expected result.. - std::map expected; - expected[""] = "int main()\n" - "{\n" - "\n" - "\n" - "\n" - "int* flags = (int*)malloc((10)*sizeof(int));\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "free(flags);\n" - "\n" - "}\n"; - - expected["__cplusplus"] = "int main()\n" - "{\n" - "\n" - "int* flags = new int[10];\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "delete [] flags;\n" - "\n" - "\n" - "\n" - "}\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - void test1() - { - const char filedata[] = "#ifdef WIN32 \n" - " abcdef\n" - "#else \n" - " qwerty\n" - "#endif \n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\nqwerty\n\n"; - expected["WIN32"] = "\nabcdef\n\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - void test2() - { - const char filedata[] = "# ifndef WIN32\n" - " \" # ifdef WIN32\" // a comment\n" - " # else \n" - " qwerty\n" - " # endif \n"; - - // Expected result.. - std::map expected; - expected["WIN32"] = "\n\n\nqwerty\n\n"; - expected[""] = "\n\" # ifdef WIN32\"\n\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - void test3() - { - const char filedata[] = "#ifdef ABC\n" - "a\n" - "#ifdef DEF\n" - "b\n" - "#endif\n" - "c\n" - "#endif\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\n\n\n\n\n"; - expected["ABC"] = "\na\n\n\n\nc\n\n"; - expected["ABC;DEF"] = "\na\n\nb\n\nc\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - void test4() - { - const char filedata[] = "#ifdef ABC\n" - "A\n" - "#endif\t\n" - "#ifdef ABC\n" - "A\n" - "#endif\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\n\n\n\n"; - expected["ABC"] = "\nA\n\n\nA\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - void test5() - { - const char filedata[] = "#ifdef ABC\n" - "A\n" - "#else\n" - "B\n" - "#ifdef DEF\n" - "C\n" - "#endif\n" - "#endif\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\nB\n\n\n\n\n"; - expected["ABC"] = "\nA\n\n\n\n\n\n\n"; - expected["DEF"] = "\n\n\nB\n\nC\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - - void comments1() - { - const char filedata[] = "/*\n" - "#ifdef WIN32\n" - "#endif\n" - "*/\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - - void if0() - { - const char filedata[] = " # if /* comment */ 0 // comment\n" - "#ifdef WIN32\n" - "#endif\n" - "#endif\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - void if1() - { - const char filedata[] = " # if /* comment */ 1 // comment\n" - "ABC\n" - " # endif \n"; - - // Expected result.. - std::map expected; - expected[""] = "\nABC\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - void elif() - { - const char filedata[] = "#if DEF1\n" - "ABC\n" - "#elif DEF2\n" - "DEF\n" - "#endif\n"; - - // Expected result.. - std::map expected; - expected[""] = "\n\n\n\n\n"; - expected["DEF1"] = "\nABC\n\n\n\n"; - expected["DEF2"] = "\n\n\nDEF\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - - void include1() - { - const char filedata[] = " # include \"abcd.h\" // abcd\n"; - - // Expected result.. - std::map expected; - expected[""] = "#include \"abcd.h\"\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - - - void if_cond1() - { - const char filedata[] = "#if LIBVER>100\n" - " A\n" - "#else\n" - " B\n" - "#endif\n"; - - std::map expected; - expected[""] = "\n\n\nB\n\n"; - expected["LIBVER>100"] = "\nA\n\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - - - void multiline() - { - const char filedata[] = "#define str \"abc\" \\ \n" - " \"def\" \\ \n" - " \"ghi\" \n"; - - std::map expected; - expected[""] = "#define str \"abc\" \"def\" \"ghi\"\n\n\n"; - - // Preprocess => actual result.. - std::istringstream istr(filedata); - std::map actual; - Preprocessor preprocessor; - preprocessor.preprocess( istr, actual, "" ); - - // Compare results.. - ASSERT_EQUALS( true, cmpmaps(actual, expected)); - } - -}; - -REGISTER_TEST( TestPreprocessor ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +class TestPreprocessor : public TestFixture +{ +public: + TestPreprocessor() : TestFixture("TestPreprocessor") + { } + +private: + + void run() + { + // Just read the code into a string. Perform simple cleanup of the code + TEST_CASE(readCode); + + // The bug that started the whole work with the new preprocessor + TEST_CASE( Bug2190219 ); + + TEST_CASE( test1 ); + TEST_CASE( test2 ); + TEST_CASE( test3 ); + TEST_CASE( test4 ); + TEST_CASE( test5 ); + + TEST_CASE( comments1 ); + + TEST_CASE( if0 ); + TEST_CASE( if1 ); + + TEST_CASE( elif ); + + TEST_CASE( include1 ); + + TEST_CASE( if_cond1 ); + + TEST_CASE( multiline ); + } + + + void readCode() + { + const char code[] = " \t a //\n" + " #aa\t /* remove this */\tb \r\n"; + Preprocessor p; + std::istringstream istr(code); + std::string codestr( p.read(istr,"") ); + ASSERT_EQUALS( "a \n#aa b \n", codestr ); + } + + + bool cmpmaps(const std::map &m1, const std::map &m2) + { + // Begin by checking the sizes + if ( m1.size() != m2.size() ) + return false; + + // Check each item in the maps.. + for ( std::map::const_iterator it1 = m1.begin(); it1 != m1.end(); ++it1 ) + { + std::string s1 = it1->first; + std::map::const_iterator it2 = m2.find(s1); + if ( it2 == m2.end() ) + return false; + else + { + std::string s1 = it1->second; + std::string s2 = it2->second; + if ( s1 != s2 ) + return false; + } + } + + // No diffs were found + return true; + } + + + void Bug2190219() + { + const char filedata[] = "int main()\n" + "{\n" + "#ifdef __cplusplus\n" + " int* flags = new int[10];\n" + "#else\n" + " int* flags = (int*)malloc((10)*sizeof(int));\n" + "#endif\n" + "\n" + "#ifdef __cplusplus\n" + " delete [] flags;\n" + "#else\n" + " free(flags);\n" + "#endif\n" + "}\n"; + + // Expected result.. + std::map expected; + expected[""] = "int main()\n" + "{\n" + "\n" + "\n" + "\n" + "int* flags = (int*)malloc((10)*sizeof(int));\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "free(flags);\n" + "\n" + "}\n"; + + expected["__cplusplus"] = "int main()\n" + "{\n" + "\n" + "int* flags = new int[10];\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "delete [] flags;\n" + "\n" + "\n" + "\n" + "}\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + void test1() + { + const char filedata[] = "#ifdef WIN32 \n" + " abcdef\n" + "#else \n" + " qwerty\n" + "#endif \n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\nqwerty\n\n"; + expected["WIN32"] = "\nabcdef\n\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + void test2() + { + const char filedata[] = "# ifndef WIN32\n" + " \" # ifdef WIN32\" // a comment\n" + " # else \n" + " qwerty\n" + " # endif \n"; + + // Expected result.. + std::map expected; + expected["WIN32"] = "\n\n\nqwerty\n\n"; + expected[""] = "\n\" # ifdef WIN32\"\n\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + void test3() + { + const char filedata[] = "#ifdef ABC\n" + "a\n" + "#ifdef DEF\n" + "b\n" + "#endif\n" + "c\n" + "#endif\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\n\n\n\n\n"; + expected["ABC"] = "\na\n\n\n\nc\n\n"; + expected["ABC;DEF"] = "\na\n\nb\n\nc\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + void test4() + { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#endif\t\n" + "#ifdef ABC\n" + "A\n" + "#endif\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\n\n\n\n"; + expected["ABC"] = "\nA\n\n\nA\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + void test5() + { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#else\n" + "B\n" + "#ifdef DEF\n" + "C\n" + "#endif\n" + "#endif\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\nB\n\n\n\n\n"; + expected["ABC"] = "\nA\n\n\n\n\n\n\n"; + expected["DEF"] = "\n\n\nB\n\nC\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + + void comments1() + { + const char filedata[] = "/*\n" + "#ifdef WIN32\n" + "#endif\n" + "*/\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + + void if0() + { + const char filedata[] = " # if /* comment */ 0 // comment\n" + "#ifdef WIN32\n" + "#endif\n" + "#endif\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + void if1() + { + const char filedata[] = " # if /* comment */ 1 // comment\n" + "ABC\n" + " # endif \n"; + + // Expected result.. + std::map expected; + expected[""] = "\nABC\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + void elif() + { + const char filedata[] = "#if DEF1\n" + "ABC\n" + "#elif DEF2\n" + "DEF\n" + "#endif\n"; + + // Expected result.. + std::map expected; + expected[""] = "\n\n\n\n\n"; + expected["DEF1"] = "\nABC\n\n\n\n"; + expected["DEF2"] = "\n\n\nDEF\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + + void include1() + { + const char filedata[] = " # include \"abcd.h\" // abcd\n"; + + // Expected result.. + std::map expected; + expected[""] = "#include \"abcd.h\"\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + + + void if_cond1() + { + const char filedata[] = "#if LIBVER>100\n" + " A\n" + "#else\n" + " B\n" + "#endif\n"; + + std::map expected; + expected[""] = "\n\n\nB\n\n"; + expected["LIBVER>100"] = "\nA\n\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + + + void multiline() + { + const char filedata[] = "#define str \"abc\" \\ \n" + " \"def\" \\ \n" + " \"ghi\" \n"; + + std::map expected; + expected[""] = "#define str \"abc\" \"def\" \"ghi\"\n\n\n"; + + // Preprocess => actual result.. + std::istringstream istr(filedata); + std::map actual; + Preprocessor preprocessor; + preprocessor.preprocess( istr, actual, "" ); + + // Compare results.. + ASSERT_EQUALS( true, cmpmaps(actual, expected)); + } + +}; + +REGISTER_TEST( TestPreprocessor ) diff --git a/testredundantif.cpp b/testredundantif.cpp index 83b7e86c8..5e732a1ad 100644 --- a/testredundantif.cpp +++ b/testredundantif.cpp @@ -1,87 +1,87 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestRedundantIf : public TestFixture -{ -public: - TestRedundantIf() : TestFixture("TestRedundantIf") - { } - -private: - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - - // Clear the error buffer.. - errout.str(""); - - // Check for redundant condition.. - CheckOther checkOther( &tokenizer, this ); - checkOther.redundantCondition2(); - } - - void run() - { - TEST_CASE( remove1 ); - TEST_CASE( remove2 ); - } - - void remove1() - { - check( "void f()\n" - "{\n" +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestRedundantIf : public TestFixture +{ +public: + TestRedundantIf() : TestFixture("TestRedundantIf") + { } + +private: + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + + // Clear the error buffer.. + errout.str(""); + + // Check for redundant condition.. + CheckOther checkOther( &tokenizer, this ); + checkOther.redundantCondition2(); + } + + void run() + { + TEST_CASE( remove1 ); + TEST_CASE( remove2 ); + } + + void remove1() + { + check( "void f()\n" + "{\n" " if (haystack.find(needle) != haystack.end())\n" - " haystack.remove(needle);" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant condition found. The remove function in the STL will not do anything if element doesn't exist\n"), errout.str() ); - } - - void remove2() - { - check( "void f()\n" - "{\n" + " haystack.remove(needle);" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant condition found. The remove function in the STL will not do anything if element doesn't exist\n"), errout.str() ); + } + + void remove2() + { + check( "void f()\n" + "{\n" " if (haystack.find(needle) != haystack.end())\n" - " {\n" - " haystack.remove(needle);\n" - " }\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant condition found. The remove function in the STL will not do anything if element doesn't exist\n"), errout.str() ); - } - -}; - -REGISTER_TEST( TestRedundantIf ) - - + " {\n" + " haystack.remove(needle);\n" + " }\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:3]: Redundant condition found. The remove function in the STL will not do anything if element doesn't exist\n"), errout.str() ); + } + +}; + +REGISTER_TEST( TestRedundantIf ) + + diff --git a/testrunner.cpp b/testrunner.cpp index 41cf6478b..fe2a1188e 100644 --- a/testrunner.cpp +++ b/testrunner.cpp @@ -1,28 +1,28 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -int main(int argc, const char *argv[]) -{ - TestFixture::runTests( (argc==2) ? argv[1] : NULL ); - return 0; -} - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +int main(int argc, const char *argv[]) +{ + TestFixture::runTests( (argc==2) ? argv[1] : NULL ); + return 0; +} + diff --git a/testsimplifytokens.cpp b/testsimplifytokens.cpp index d4169b23e..2d1cf2ead 100644 --- a/testsimplifytokens.cpp +++ b/testsimplifytokens.cpp @@ -1,73 +1,73 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestSimplifyTokens : public TestFixture -{ -public: - TestSimplifyTokens() : TestFixture("TestSimplifyTokens") - { } - - -private: - - void run() - { - TEST_CASE( cast0 ); - TEST_CASE( sizeof1 ); - } - - std::string tok(const char code[]) - { - std::istringstream istr(code); - Tokenizer tokenizer; - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - std::string ret; - for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) - { - ret += tok->str() + " "; - } - - return ret; - } - - void cast0() - { - const char code1[] = " if ( p == (char *)0 ) "; - const char code2[] = " if ( p == 0 ) "; - ASSERT_EQUALS( tok(code1), tok(code2) ); - } - - void sizeof1() - { - const char code1[] = " struct ABC *abc = malloc(sizeof(*abc)); "; - const char code2[] = " struct ABC *abc = malloc(100); "; - ASSERT_EQUALS( tok(code1), tok(code2) ); - } -}; - -REGISTER_TEST( TestSimplifyTokens ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestSimplifyTokens : public TestFixture +{ +public: + TestSimplifyTokens() : TestFixture("TestSimplifyTokens") + { } + + +private: + + void run() + { + TEST_CASE( cast0 ); + TEST_CASE( sizeof1 ); + } + + std::string tok(const char code[]) + { + std::istringstream istr(code); + Tokenizer tokenizer; + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + std::string ret; + for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) + { + ret += tok->str() + " "; + } + + return ret; + } + + void cast0() + { + const char code1[] = " if ( p == (char *)0 ) "; + const char code2[] = " if ( p == 0 ) "; + ASSERT_EQUALS( tok(code1), tok(code2) ); + } + + void sizeof1() + { + const char code1[] = " struct ABC *abc = malloc(sizeof(*abc)); "; + const char code2[] = " struct ABC *abc = malloc(100); "; + ASSERT_EQUALS( tok(code1), tok(code2) ); + } +}; + +REGISTER_TEST( TestSimplifyTokens ) diff --git a/testsuite.cpp b/testsuite.cpp index fd83c95b6..1f28f44ec 100644 --- a/testsuite.cpp +++ b/testsuite.cpp @@ -1,178 +1,178 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -std::ostringstream errout; - -/** - * TestRegistry - **/ - -class TestRegistry -{ -private: - std::list _tests; - -public: - static TestRegistry &theInstance() - { - static TestRegistry testreg; - return testreg; - } - - void addTest( TestFixture *t ) - { - _tests.push_back( t ); - } - - const std::list &tests() const - { - return _tests; - } -}; - - - - -/** - * TestFixture - **/ - -std::ostringstream TestFixture::errmsg; -unsigned int TestFixture::countTests; - -TestFixture::TestFixture(const std::string &_name) : classname(_name) -{ - TestRegistry::theInstance().addTest(this); -} - - -bool TestFixture::runTest(const char testname[]) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +std::ostringstream errout; + +/** + * TestRegistry + **/ + +class TestRegistry +{ +private: + std::list _tests; + +public: + static TestRegistry &theInstance() + { + static TestRegistry testreg; + return testreg; + } + + void addTest( TestFixture *t ) + { + _tests.push_back( t ); + } + + const std::list &tests() const + { + return _tests; + } +}; + + + + +/** + * TestFixture + **/ + +std::ostringstream TestFixture::errmsg; +unsigned int TestFixture::countTests; + +TestFixture::TestFixture(const std::string &_name) : classname(_name) +{ + TestRegistry::theInstance().addTest(this); +} + + +bool TestFixture::runTest(const char testname[]) { if ( testToRun.empty() || testToRun == testname ) - { - countTests++; - std::cout << classname << "::" << testname << "\n"; + { + countTests++; + std::cout << classname << "::" << testname << "\n"; return true; - } + } return false; -} - -static std::string writestr( const std::string &str ) -{ - std::ostringstream ostr; - ostr << "\""; - for (unsigned int i = 0; i < str.length(); ++i) - { - char ch = str[i]; - if ( ch == '\n' ) - ostr << "\\n"; - else if ( ch == '\t' ) - ostr << "\\t"; - else if ( ch == '\"' ) - ostr << "\\\""; - else - ostr << std::string(1, ch); - } - ostr << "\""; - return ostr.str(); -} - -void TestFixture::assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual) -{ - if ( expected != actual ) - { - errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl - << "Expected:" << std::endl - << writestr(expected) << std::endl - << "Actual:" << std::endl - << writestr(actual) << std::endl; - } -} - -void TestFixture::assertEquals(const char *filename, int linenr, unsigned int expected, unsigned int actual) -{ - std::ostringstream ostr1; - ostr1 << expected; - std::ostringstream ostr2; - ostr2 << actual; - assertEquals( filename, linenr, ostr1.str(), ostr2.str() ); -} - -void TestFixture::printTests() -{ - const std::list &tests = TestRegistry::theInstance().tests(); - - for ( std::list::const_iterator it = tests.begin(); it != tests.end(); ++it ) - { - std::cout << (*it)->classname << std::endl; - } -} +} + +static std::string writestr( const std::string &str ) +{ + std::ostringstream ostr; + ostr << "\""; + for (unsigned int i = 0; i < str.length(); ++i) + { + char ch = str[i]; + if ( ch == '\n' ) + ostr << "\\n"; + else if ( ch == '\t' ) + ostr << "\\t"; + else if ( ch == '\"' ) + ostr << "\\\""; + else + ostr << std::string(1, ch); + } + ostr << "\""; + return ostr.str(); +} + +void TestFixture::assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual) +{ + if ( expected != actual ) + { + errmsg << "Assertion failed in " << filename << " at line " << linenr << std::endl + << "Expected:" << std::endl + << writestr(expected) << std::endl + << "Actual:" << std::endl + << writestr(actual) << std::endl; + } +} + +void TestFixture::assertEquals(const char *filename, int linenr, unsigned int expected, unsigned int actual) +{ + std::ostringstream ostr1; + ostr1 << expected; + std::ostringstream ostr2; + ostr2 << actual; + assertEquals( filename, linenr, ostr1.str(), ostr2.str() ); +} + +void TestFixture::printTests() +{ + const std::list &tests = TestRegistry::theInstance().tests(); + + for ( std::list::const_iterator it = tests.begin(); it != tests.end(); ++it ) + { + std::cout << (*it)->classname << std::endl; + } +} void TestFixture::run(const std::string &str) { testToRun = str; run(); } - -void TestFixture::runTests(const char cmd[]) -{ - std::string classname(cmd ? cmd : ""); - std::string testname(""); - if ( classname.find("::") != std::string::npos ) - { - testname = classname.substr( classname.find("::") + 2 ); - classname.erase( classname.find("::") ); - } - - countTests = 0; - errmsg.str(""); - - const std::list &tests = TestRegistry::theInstance().tests(); - - for ( std::list::const_iterator it = tests.begin(); it != tests.end(); ++it ) - { + +void TestFixture::runTests(const char cmd[]) +{ + std::string classname(cmd ? cmd : ""); + std::string testname(""); + if ( classname.find("::") != std::string::npos ) + { + testname = classname.substr( classname.find("::") + 2 ); + classname.erase( classname.find("::") ); + } + + countTests = 0; + errmsg.str(""); + + const std::list &tests = TestRegistry::theInstance().tests(); + + for ( std::list::const_iterator it = tests.begin(); it != tests.end(); ++it ) + { if ( classname.empty() || (*it)->classname == classname ) - { + { (*it)->run(testname); - } - } - - std::cout << "\n\nTesting Complete\nNumber of tests: " << countTests << "\n"; - - std::cerr << errmsg.str(); -} - - -void TestFixture::reportErr( const std::string &errmsg) -{ - errout << errmsg << std::endl; -} - -void TestFixture::reportOut( const std::string &outmsg) -{ - // These can probably be ignored -} + } + } + + std::cout << "\n\nTesting Complete\nNumber of tests: " << countTests << "\n"; + + std::cerr << errmsg.str(); +} + + +void TestFixture::reportErr( const std::string &errmsg) +{ + errout << errmsg << std::endl; +} + +void TestFixture::reportOut( const std::string &outmsg) +{ + // These can probably be ignored +} diff --git a/testsuite.h b/testsuite.h index 8dc7bdef0..1d95e2b77 100644 --- a/testsuite.h +++ b/testsuite.h @@ -1,61 +1,61 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 "errorlogger.h" - -class TOKEN; - -class TestFixture : public ErrorLogger -{ -private: - static std::ostringstream errmsg; - static unsigned int countTests; - -protected: - std::string classname; +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 "errorlogger.h" + +class TOKEN; + +class TestFixture : public ErrorLogger +{ +private: + static std::ostringstream errmsg; + static unsigned int countTests; + +protected: + std::string classname; std::string testToRun; - - virtual void run() = 0; - - bool runTest(const char testname[]); - void assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual); - void assertEquals(const char *filename, int linenr, unsigned int expected, unsigned int actual); - -public: - virtual void reportErr( const std::string &errmsg); - - virtual void reportOut( const std::string &outmsg); + + virtual void run() = 0; + + bool runTest(const char testname[]); + void assertEquals(const char *filename, int linenr, const std::string &expected, const std::string &actual); + void assertEquals(const char *filename, int linenr, unsigned int expected, unsigned int actual); + +public: + virtual void reportErr( const std::string &errmsg); + + virtual void reportOut( const std::string &outmsg); void run(const std::string &str); - - TestFixture(const std::string &_name); - virtual ~TestFixture() { } - - static void printTests(); - static void runTests(const char cmd[]); -}; - - -#define TEST_CASE( NAME ) if ( runTest(#NAME) ) NAME (); -#define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) -#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; } - + + TestFixture(const std::string &_name); + virtual ~TestFixture() { } + + static void printTests(); + static void runTests(const char cmd[]); +}; + + +#define TEST_CASE( NAME ) if ( runTest(#NAME) ) NAME (); +#define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) +#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; } + diff --git a/testtoken.cpp b/testtoken.cpp index ffca19318..d92b39f75 100644 --- a/testtoken.cpp +++ b/testtoken.cpp @@ -1,61 +1,61 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 "testsuite.h" -#include "token.h" - -extern std::ostringstream errout; -class TestTOKEN : public TestFixture -{ -public: - TestTOKEN() : TestFixture("TestTOKEN") - { } - -private: - - void run() - { - TEST_CASE( nextprevious ); - - } - - void nextprevious() - { - TOKEN *token = new TOKEN; - token->setstr( "1" ); - token->insertToken( "2" ); - token->next()->insertToken( "3" ); - TOKEN *last = token->next()->next(); - ASSERT_EQUALS( token->str(), "1" ); - ASSERT_EQUALS( token->next()->str(), "2" ); - ASSERT_EQUALS( token->next()->next()->str(), "3" ); - if( last->next() ) - ASSERT_EQUALS( "Null was expected", "" ); - - ASSERT_EQUALS( last->str(), "3" ); - ASSERT_EQUALS( last->previous()->str(), "2" ); - ASSERT_EQUALS( last->previous()->previous()->str(), "1" ); - if( token->previous() ) - ASSERT_EQUALS( "Null was expected", "" ); - } - - -}; - -REGISTER_TEST( TestTOKEN ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 "testsuite.h" +#include "token.h" + +extern std::ostringstream errout; +class TestTOKEN : public TestFixture +{ +public: + TestTOKEN() : TestFixture("TestTOKEN") + { } + +private: + + void run() + { + TEST_CASE( nextprevious ); + + } + + void nextprevious() + { + TOKEN *token = new TOKEN; + token->setstr( "1" ); + token->insertToken( "2" ); + token->next()->insertToken( "3" ); + TOKEN *last = token->next()->next(); + ASSERT_EQUALS( token->str(), "1" ); + ASSERT_EQUALS( token->next()->str(), "2" ); + ASSERT_EQUALS( token->next()->next()->str(), "3" ); + if( last->next() ) + ASSERT_EQUALS( "Null was expected", "" ); + + ASSERT_EQUALS( last->str(), "3" ); + ASSERT_EQUALS( last->previous()->str(), "2" ); + ASSERT_EQUALS( last->previous()->previous()->str(), "1" ); + if( token->previous() ) + ASSERT_EQUALS( "Null was expected", "" ); + } + + +}; + +REGISTER_TEST( TestTOKEN ) diff --git a/testtokenize.cpp b/testtokenize.cpp index 85bb6d146..985ced16e 100644 --- a/testtokenize.cpp +++ b/testtokenize.cpp @@ -1,506 +1,506 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 "testsuite.h" -#define UNIT_TESTING // Get access to "private" data in Tokenizer -#include "tokenize.h" - -extern std::ostringstream errout; -class TestTokenizer : public TestFixture -{ -public: - TestTokenizer() : TestFixture("TestTokenizer") - { } - -private: - - void run() - { - TEST_CASE( multiline ); - TEST_CASE( longtok ); - - TEST_CASE( inlineasm ); - - TEST_CASE( dupfuncname ); - - TEST_CASE( const_and_volatile_functions ); - - TEST_CASE( numeric_true_condition ); - - TEST_CASE( simplify_known_variables ); - - TEST_CASE( multi_compare ); - - TEST_CASE( match1 ); - - TEST_CASE( match2 ); - - TEST_CASE( varid1 ); - TEST_CASE( varid2 ); - } - - - bool cmptok(const char *expected[], const TOKEN *actual) - { - unsigned int i = 0; - for (; expected[i] && actual; ++i, actual = actual->next()) - { - if ( strcmp( expected[i], actual->aaaa() ) != 0) - return false; - } - return (expected[i] == NULL && actual == NULL); - } - - - void multiline() - { - const char filedata[] = "#define str \"abc\" \\\n" - " \"def\"\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(filedata); - tokenizer.tokenize(istr, "test.cpp"); - - // Expected result.. - const char *expected[] = - { - "def", - "str", - ";", - 0 - }; - - // Compare.. - ASSERT_EQUALS( true, cmptok(expected, tokenizer.tokens()) ); - } - - - void longtok() - { - std::string filedata(10000,'a'); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(filedata); - tokenizer.tokenize(istr, "test.cpp"); - - // Expected result.. - ASSERT_EQUALS( std::string(10000,'a'), std::string(tokenizer.tokens()->aaaa()) ); - } - - - void inlineasm() - { - const char filedata[] = "void foo()\n" - "{\n" - " __asm\n" - " {\n" - " jmp $jump1\n" - " $jump1:\n" - " }\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(filedata); - tokenizer.tokenize(istr, "test.cpp"); - - // Expected result.. - const char *expected[] = - { - "void", - "foo", - "(", - ")", - "{", - "}", - 0 - }; - - // Compare.. - ASSERT_EQUALS( true, cmptok(expected, tokenizer.tokens()) ); - } - - - void dupfuncname() - { - const char code[] = "void a()\n" - "{ }\n" - "void a(int i)\n" - "{ }\n" - "void b()\n" - "{ }\n"; - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.fillFunctionList(); - - ASSERT_EQUALS( 1, static_cast(tokenizer._functionList.size()) ); - ASSERT_EQUALS( std::string("b"), tokenizer._functionList[0]->aaaa() ); - } - - void const_and_volatile_functions() - { - const char code[] = "class B\n\ - {\n\ - public:\n\ - void a();\n\ - void b() const;\n\ - void c() volatile;\n\ - };\n\ - \n\ - void B::a()\n\ - {}\n\ - \n\ - void B::b() const\n\ - {}\n\ - \n\ - void B::c() volatile\n\ - {}\n"; - - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.fillFunctionList(); - - ASSERT_EQUALS( 3, static_cast(tokenizer._functionList.size()) ); - if( tokenizer._functionList.size() == 3 ) - { - ASSERT_EQUALS( std::string("a"), tokenizer._functionList[0]->str() ); - ASSERT_EQUALS( std::string("b"), tokenizer._functionList[1]->str() ); - ASSERT_EQUALS( std::string("c"), tokenizer._functionList[2]->str() ); - } - } - - - void numeric_true_condition() - { - const char code[] = "void f()\n" - "{\n" - " if (5==5);\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.simplifyTokenList(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { if ( true ) ; }"), ostr.str() ); - } - - void simplify_known_variables() - { - { - const char code[] = "void f()\n" - "{\n" - " int a = 10;\n" - " if (a);\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.setVarId(); - tokenizer.simplifyKnownVariables(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { int a = 10 ; if ( 10 ) ; }"), ostr.str() ); - } - - { - const char code[] = "void f()\n" - "{\n" - " int a = 10;\n" - " a = g();\n" - " if (a);\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.setVarId(); - tokenizer.simplifyKnownVariables(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { int a = 10 ; a = g ( ) ; if ( a ) ; }"), ostr.str() ); - } - - { - const char code[] = "void f()\n" - "{\n" - " int a = 4;\n" - " while(true){\n" - " break;\n" - " a = 10;\n" - " }\n" - " if (a);\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.setVarId(); - tokenizer.simplifyKnownVariables(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; while ( true ) { break ; a = 10 ; } if ( a ) ; }"), ostr.str() ); - } - - { - const char code[] = "void f()\n" - "{\n" - " int a = 4;\n" - " if ( g(a));\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.setVarId(); - tokenizer.simplifyKnownVariables(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; if ( g ( a ) ) ; }"), ostr.str() ); - } - - { - const char code[] = "void f()\n" - "{\n" - " int a = 4;\n" - " if ( a = 5 );\n" - "}\n"; - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - tokenizer.setVarId(); - tokenizer.simplifyKnownVariables(); - - std::ostringstream ostr; - for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) - ostr << " " << tok->str(); - ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; if ( a = 5 ) ; }"), ostr.str() ); - } - } - - void multi_compare() - { - // Test for found - ASSERT_EQUALS( 1, TOKEN::multiCompare( "one|two", "one" ) ); - ASSERT_EQUALS( 1, TOKEN::multiCompare( "one|two", "two" ) ); - ASSERT_EQUALS( 1, TOKEN::multiCompare( "verybig|two|", "two" ) ); - - // Test for empty string found - ASSERT_EQUALS( 0, TOKEN::multiCompare( "|one|two", "notfound" ) ); - ASSERT_EQUALS( 0, TOKEN::multiCompare( "one||two", "notfound" ) ); - ASSERT_EQUALS( 0, TOKEN::multiCompare( "one|two|", "notfound" ) ); - - // Test for not found - ASSERT_EQUALS( -1, TOKEN::multiCompare( "one|two", "notfound" ) ); - ASSERT_EQUALS( -1, TOKEN::multiCompare( "verybig|two", "s" ) ); - ASSERT_EQUALS( -1, TOKEN::multiCompare( "one|two", "ne" ) ); - ASSERT_EQUALS( -1, TOKEN::multiCompare( "abc|def", "a" ) ); - } - - void match1() - { - // Match "%var% | %var%" - { - const std::string code("abc|def"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "%var% | %var%") ); - } - - // Match "%var% || %var%" - { - const std::string code("abc||def"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "%var% || %var%") ); - } - } - - void match2() - { - { - const std::string code(""); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "!!else") ); - } - - { - const std::string code("if ;"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); - } - - { - const std::string code("if ; something"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); - } - - { - const std::string code("else"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( false, TOKEN::Match(tokenizer.tokens(), "!!else") ); - } - - { - const std::string code("if ; else"); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - - // Match.. - ASSERT_EQUALS( false, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); - } - } - - void varid1() - { - const std::string code(";static int i = 1;\n" - "void f()\n" - "{\n" - " int i = 2;\n" - " for (int i = 0; i < 10; ++i)\n" - " i = 3;\n" - " i = 4;\n" - "}\n" ); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - tokenizer.setVarId(); - - for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) - { - if ( tok->str() != "i" ) - ASSERT_EQUALS( 0, tok->varId() ); - else if ( TOKEN::Match(tok, "i = 1") ) - ASSERT_EQUALS( 1, tok->varId() ); - else if ( TOKEN::Match(tok, "i = 2") ) - ASSERT_EQUALS( 2, tok->varId() ); - else if ( TOKEN::Match(tok, "i = 3") ) - ASSERT_EQUALS( 3, tok->varId() ); - else if ( TOKEN::Match(tok, "i = 4") ) - ASSERT_EQUALS( 2, tok->varId() ); - } - } - - void varid2() - { - const std::string code("void f()\n" - "{\n" - " struct ABC abc;\n" - " abc.a = 3;\n" - " i = abc.a;\n" - "}\n" ); - - // tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize(istr, "test.cpp"); - tokenizer.setVarId(); - - for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) - { - if ( tok->str() == "abc" ) - ASSERT_EQUALS( 1, tok->varId() ); - else if ( tok->str() == "a" ) - ASSERT_EQUALS( 2, tok->varId() ); - else - ASSERT_EQUALS( 0, tok->varId() ); - } - } -}; - -REGISTER_TEST( TestTokenizer ) +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 "testsuite.h" +#define UNIT_TESTING // Get access to "private" data in Tokenizer +#include "tokenize.h" + +extern std::ostringstream errout; +class TestTokenizer : public TestFixture +{ +public: + TestTokenizer() : TestFixture("TestTokenizer") + { } + +private: + + void run() + { + TEST_CASE( multiline ); + TEST_CASE( longtok ); + + TEST_CASE( inlineasm ); + + TEST_CASE( dupfuncname ); + + TEST_CASE( const_and_volatile_functions ); + + TEST_CASE( numeric_true_condition ); + + TEST_CASE( simplify_known_variables ); + + TEST_CASE( multi_compare ); + + TEST_CASE( match1 ); + + TEST_CASE( match2 ); + + TEST_CASE( varid1 ); + TEST_CASE( varid2 ); + } + + + bool cmptok(const char *expected[], const TOKEN *actual) + { + unsigned int i = 0; + for (; expected[i] && actual; ++i, actual = actual->next()) + { + if ( strcmp( expected[i], actual->aaaa() ) != 0) + return false; + } + return (expected[i] == NULL && actual == NULL); + } + + + void multiline() + { + const char filedata[] = "#define str \"abc\" \\\n" + " \"def\"\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(filedata); + tokenizer.tokenize(istr, "test.cpp"); + + // Expected result.. + const char *expected[] = + { + "def", + "str", + ";", + 0 + }; + + // Compare.. + ASSERT_EQUALS( true, cmptok(expected, tokenizer.tokens()) ); + } + + + void longtok() + { + std::string filedata(10000,'a'); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(filedata); + tokenizer.tokenize(istr, "test.cpp"); + + // Expected result.. + ASSERT_EQUALS( std::string(10000,'a'), std::string(tokenizer.tokens()->aaaa()) ); + } + + + void inlineasm() + { + const char filedata[] = "void foo()\n" + "{\n" + " __asm\n" + " {\n" + " jmp $jump1\n" + " $jump1:\n" + " }\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(filedata); + tokenizer.tokenize(istr, "test.cpp"); + + // Expected result.. + const char *expected[] = + { + "void", + "foo", + "(", + ")", + "{", + "}", + 0 + }; + + // Compare.. + ASSERT_EQUALS( true, cmptok(expected, tokenizer.tokens()) ); + } + + + void dupfuncname() + { + const char code[] = "void a()\n" + "{ }\n" + "void a(int i)\n" + "{ }\n" + "void b()\n" + "{ }\n"; + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.fillFunctionList(); + + ASSERT_EQUALS( 1, static_cast(tokenizer._functionList.size()) ); + ASSERT_EQUALS( std::string("b"), tokenizer._functionList[0]->aaaa() ); + } + + void const_and_volatile_functions() + { + const char code[] = "class B\n\ + {\n\ + public:\n\ + void a();\n\ + void b() const;\n\ + void c() volatile;\n\ + };\n\ + \n\ + void B::a()\n\ + {}\n\ + \n\ + void B::b() const\n\ + {}\n\ + \n\ + void B::c() volatile\n\ + {}\n"; + + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.fillFunctionList(); + + ASSERT_EQUALS( 3, static_cast(tokenizer._functionList.size()) ); + if( tokenizer._functionList.size() == 3 ) + { + ASSERT_EQUALS( std::string("a"), tokenizer._functionList[0]->str() ); + ASSERT_EQUALS( std::string("b"), tokenizer._functionList[1]->str() ); + ASSERT_EQUALS( std::string("c"), tokenizer._functionList[2]->str() ); + } + } + + + void numeric_true_condition() + { + const char code[] = "void f()\n" + "{\n" + " if (5==5);\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.simplifyTokenList(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { if ( true ) ; }"), ostr.str() ); + } + + void simplify_known_variables() + { + { + const char code[] = "void f()\n" + "{\n" + " int a = 10;\n" + " if (a);\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.setVarId(); + tokenizer.simplifyKnownVariables(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { int a = 10 ; if ( 10 ) ; }"), ostr.str() ); + } + + { + const char code[] = "void f()\n" + "{\n" + " int a = 10;\n" + " a = g();\n" + " if (a);\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.setVarId(); + tokenizer.simplifyKnownVariables(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { int a = 10 ; a = g ( ) ; if ( a ) ; }"), ostr.str() ); + } + + { + const char code[] = "void f()\n" + "{\n" + " int a = 4;\n" + " while(true){\n" + " break;\n" + " a = 10;\n" + " }\n" + " if (a);\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.setVarId(); + tokenizer.simplifyKnownVariables(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; while ( true ) { break ; a = 10 ; } if ( a ) ; }"), ostr.str() ); + } + + { + const char code[] = "void f()\n" + "{\n" + " int a = 4;\n" + " if ( g(a));\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.setVarId(); + tokenizer.simplifyKnownVariables(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; if ( g ( a ) ) ; }"), ostr.str() ); + } + + { + const char code[] = "void f()\n" + "{\n" + " int a = 4;\n" + " if ( a = 5 );\n" + "}\n"; + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + tokenizer.setVarId(); + tokenizer.simplifyKnownVariables(); + + std::ostringstream ostr; + for (const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next()) + ostr << " " << tok->str(); + ASSERT_EQUALS( std::string(" void f ( ) { int a = 4 ; if ( a = 5 ) ; }"), ostr.str() ); + } + } + + void multi_compare() + { + // Test for found + ASSERT_EQUALS( 1, TOKEN::multiCompare( "one|two", "one" ) ); + ASSERT_EQUALS( 1, TOKEN::multiCompare( "one|two", "two" ) ); + ASSERT_EQUALS( 1, TOKEN::multiCompare( "verybig|two|", "two" ) ); + + // Test for empty string found + ASSERT_EQUALS( 0, TOKEN::multiCompare( "|one|two", "notfound" ) ); + ASSERT_EQUALS( 0, TOKEN::multiCompare( "one||two", "notfound" ) ); + ASSERT_EQUALS( 0, TOKEN::multiCompare( "one|two|", "notfound" ) ); + + // Test for not found + ASSERT_EQUALS( -1, TOKEN::multiCompare( "one|two", "notfound" ) ); + ASSERT_EQUALS( -1, TOKEN::multiCompare( "verybig|two", "s" ) ); + ASSERT_EQUALS( -1, TOKEN::multiCompare( "one|two", "ne" ) ); + ASSERT_EQUALS( -1, TOKEN::multiCompare( "abc|def", "a" ) ); + } + + void match1() + { + // Match "%var% | %var%" + { + const std::string code("abc|def"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "%var% | %var%") ); + } + + // Match "%var% || %var%" + { + const std::string code("abc||def"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "%var% || %var%") ); + } + } + + void match2() + { + { + const std::string code(""); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "!!else") ); + } + + { + const std::string code("if ;"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); + } + + { + const std::string code("if ; something"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( true, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); + } + + { + const std::string code("else"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( false, TOKEN::Match(tokenizer.tokens(), "!!else") ); + } + + { + const std::string code("if ; else"); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + + // Match.. + ASSERT_EQUALS( false, TOKEN::Match(tokenizer.tokens(), "if ; !!else") ); + } + } + + void varid1() + { + const std::string code(";static int i = 1;\n" + "void f()\n" + "{\n" + " int i = 2;\n" + " for (int i = 0; i < 10; ++i)\n" + " i = 3;\n" + " i = 4;\n" + "}\n" ); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.setVarId(); + + for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) + { + if ( tok->str() != "i" ) + ASSERT_EQUALS( 0, tok->varId() ); + else if ( TOKEN::Match(tok, "i = 1") ) + ASSERT_EQUALS( 1, tok->varId() ); + else if ( TOKEN::Match(tok, "i = 2") ) + ASSERT_EQUALS( 2, tok->varId() ); + else if ( TOKEN::Match(tok, "i = 3") ) + ASSERT_EQUALS( 3, tok->varId() ); + else if ( TOKEN::Match(tok, "i = 4") ) + ASSERT_EQUALS( 2, tok->varId() ); + } + } + + void varid2() + { + const std::string code("void f()\n" + "{\n" + " struct ABC abc;\n" + " abc.a = 3;\n" + " i = abc.a;\n" + "}\n" ); + + // tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize(istr, "test.cpp"); + tokenizer.setVarId(); + + for ( const TOKEN *tok = tokenizer.tokens(); tok; tok = tok->next() ) + { + if ( tok->str() == "abc" ) + ASSERT_EQUALS( 1, tok->varId() ); + else if ( tok->str() == "a" ) + ASSERT_EQUALS( 2, tok->varId() ); + else + ASSERT_EQUALS( 0, tok->varId() ); + } + } +}; + +REGISTER_TEST( TestTokenizer ) diff --git a/testunusedprivfunc.cpp b/testunusedprivfunc.cpp index 05212b6ff..1a2e29ebe 100644 --- a/testunusedprivfunc.cpp +++ b/testunusedprivfunc.cpp @@ -1,120 +1,120 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 - -extern std::ostringstream errout; - -class TestUnusedPrivateFunction : public TestFixture -{ -public: - TestUnusedPrivateFunction() : TestFixture("TestUnusedPrivateFunction") - { } - -private: - void run() - { - TEST_CASE( test1 ); - - // [ 2236547 ] False positive --style unused function, called via pointer - TEST_CASE( func_pointer ); - } - - - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - - // Clear the error buffer.. - errout.str(""); - - // Check for unused private functions.. - Settings settings; - settings._checkCodingStyle = true; - CheckClass checkClass( &tokenizer, settings, this ); - checkClass.CheckUnusedPrivateFunctions(); - } - - - - void test1() - { - check( "class Fred\n" - "{\n" - "private:\n" - " unsigned int f();\n" - "public:\n" - " Fred();\n" - "};\n" - "\n" - "Fred::Fred()\n" - "{ }\n" - "\n" - "unsigned int Fred::f()\n" - "{ }\n" ); - - ASSERT_EQUALS( std::string("Class 'Fred', unused private function: 'f'\n"), errout.str() ); - } - - - - - - - void func_pointer() - { - check( "class Fred\n" - "{\n" - "private:\n" - " typedef void (*testfp)();\n" - "\n" - " testfp get()\n" - " {\n" - " return test;\n" - " }\n" - "\n" - " static void test()\n" - " { }\n" - "\n" - "public:\n" - " Fred();\n" - "};\n" - "\n" - "Fred::Fred()\n" - "{}\n" ); - - std::string str( errout.str() ); - - ASSERT_EQUALS( std::string("Class 'Fred', unused private function: 'get'\n"), str ); - } - - - -}; - -REGISTER_TEST( TestUnusedPrivateFunction ) - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 + +extern std::ostringstream errout; + +class TestUnusedPrivateFunction : public TestFixture +{ +public: + TestUnusedPrivateFunction() : TestFixture("TestUnusedPrivateFunction") + { } + +private: + void run() + { + TEST_CASE( test1 ); + + // [ 2236547 ] False positive --style unused function, called via pointer + TEST_CASE( func_pointer ); + } + + + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + + // Clear the error buffer.. + errout.str(""); + + // Check for unused private functions.. + Settings settings; + settings._checkCodingStyle = true; + CheckClass checkClass( &tokenizer, settings, this ); + checkClass.CheckUnusedPrivateFunctions(); + } + + + + void test1() + { + check( "class Fred\n" + "{\n" + "private:\n" + " unsigned int f();\n" + "public:\n" + " Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{ }\n" + "\n" + "unsigned int Fred::f()\n" + "{ }\n" ); + + ASSERT_EQUALS( std::string("Class 'Fred', unused private function: 'f'\n"), errout.str() ); + } + + + + + + + void func_pointer() + { + check( "class Fred\n" + "{\n" + "private:\n" + " typedef void (*testfp)();\n" + "\n" + " testfp get()\n" + " {\n" + " return test;\n" + " }\n" + "\n" + " static void test()\n" + " { }\n" + "\n" + "public:\n" + " Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{}\n" ); + + std::string str( errout.str() ); + + ASSERT_EQUALS( std::string("Class 'Fred', unused private function: 'get'\n"), str ); + } + + + +}; + +REGISTER_TEST( TestUnusedPrivateFunction ) + diff --git a/testunusedvar.cpp b/testunusedvar.cpp index 8da319bab..e54e12a47 100644 --- a/testunusedvar.cpp +++ b/testunusedvar.cpp @@ -1,184 +1,184 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 -extern std::ostringstream errout; - -class TestUnusedVar : public TestFixture -{ -public: - TestUnusedVar() : TestFixture("TestUnusedVar") - { } - -private: - void check( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for unused variables.. - CheckOther checkOther( &tokenizer, this ); - checkOther.CheckStructMemberUsage(); - } - - void run() - { - TEST_CASE( structmember1 ); - TEST_CASE( structmember2 ); - TEST_CASE( structmember3 ); - - TEST_CASE( localvar1 ); - TEST_CASE( localvar2 ); - TEST_CASE( localvar3 ); - TEST_CASE( localvar4 ); - } - - void structmember1() - { - check( "struct abc\n" - "{\n" - " int a;\n" - " int b;\n" - " int c;\n" - "};\n" ); - ASSERT_EQUALS( std::string("[test.cpp:2]: struct member 'abc::a' is never read\n" - "[test.cpp:3]: struct member 'abc::b' is never read\n" - "[test.cpp:4]: struct member 'abc::c' is never read\n"), errout.str() ); - } - - void structmember2() - { - check( "struct ABC\n" - "{\n" - " int a;\n" - " int b;\n" - " int c;\n" - "};\n" - "\n" - "void foo()\n" - "{\n" - " struct ABC abc;\n" - " int a = abc.a;\n" - " int b = abc.b;\n" - " int c = abc.c;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - void structmember3() - { - check( "struct ABC\n" - "{\n" - " int a;\n" - " int b;\n" - " int c;\n" - "};\n" - "\n" - "static struct ABC abc[] = { {1, 2, 3} };\n" - "\n" - "void foo()\n" - "{\n" - " int a = abc[0].a;\n" - " int b = abc[0].b;\n" - " int c = abc[0].c;\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - - - - - void functionVariableUsage( const char code[] ) - { - // Tokenize.. - Tokenizer tokenizer; - std::istringstream istr(code); - tokenizer.tokenize( istr, "test.cpp" ); - tokenizer.simplifyTokenList(); - - // Clear the error buffer.. - errout.str(""); - - // Check for unused variables.. - CheckOther checkOther( &tokenizer, this ); - checkOther.functionVariableUsage(); - } - - void localvar1() - { - functionVariableUsage( "void foo()\n" - "{\n" - " int i = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is assigned a value that is never used\n"), errout.str() ); - } - - void localvar2() - { - functionVariableUsage( "void foo()\n" - "{\n" - " int i;\n" - " return i;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is not assigned a value\n"), errout.str() ); - } - - void localvar3() - { - functionVariableUsage( "void foo()\n" - "{\n" - " int i;\n" - " if ( abc )\n" - " ;\n" - " else i = 0;\n" - "}\n" ); - ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is assigned a value that is never used\n"), errout.str() ); - } - - void localvar4() - { - functionVariableUsage( "void foo()\n" - "{\n" - " int i = 0;\n" - " f(i);\n" - "}\n" ); - ASSERT_EQUALS( std::string(""), errout.str() ); - } - - -}; - -REGISTER_TEST( TestUnusedVar ) - - +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 +extern std::ostringstream errout; + +class TestUnusedVar : public TestFixture +{ +public: + TestUnusedVar() : TestFixture("TestUnusedVar") + { } + +private: + void check( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for unused variables.. + CheckOther checkOther( &tokenizer, this ); + checkOther.CheckStructMemberUsage(); + } + + void run() + { + TEST_CASE( structmember1 ); + TEST_CASE( structmember2 ); + TEST_CASE( structmember3 ); + + TEST_CASE( localvar1 ); + TEST_CASE( localvar2 ); + TEST_CASE( localvar3 ); + TEST_CASE( localvar4 ); + } + + void structmember1() + { + check( "struct abc\n" + "{\n" + " int a;\n" + " int b;\n" + " int c;\n" + "};\n" ); + ASSERT_EQUALS( std::string("[test.cpp:2]: struct member 'abc::a' is never read\n" + "[test.cpp:3]: struct member 'abc::b' is never read\n" + "[test.cpp:4]: struct member 'abc::c' is never read\n"), errout.str() ); + } + + void structmember2() + { + check( "struct ABC\n" + "{\n" + " int a;\n" + " int b;\n" + " int c;\n" + "};\n" + "\n" + "void foo()\n" + "{\n" + " struct ABC abc;\n" + " int a = abc.a;\n" + " int b = abc.b;\n" + " int c = abc.c;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + void structmember3() + { + check( "struct ABC\n" + "{\n" + " int a;\n" + " int b;\n" + " int c;\n" + "};\n" + "\n" + "static struct ABC abc[] = { {1, 2, 3} };\n" + "\n" + "void foo()\n" + "{\n" + " int a = abc[0].a;\n" + " int b = abc[0].b;\n" + " int c = abc[0].c;\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + + + + + void functionVariableUsage( const char code[] ) + { + // Tokenize.. + Tokenizer tokenizer; + std::istringstream istr(code); + tokenizer.tokenize( istr, "test.cpp" ); + tokenizer.simplifyTokenList(); + + // Clear the error buffer.. + errout.str(""); + + // Check for unused variables.. + CheckOther checkOther( &tokenizer, this ); + checkOther.functionVariableUsage(); + } + + void localvar1() + { + functionVariableUsage( "void foo()\n" + "{\n" + " int i = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is assigned a value that is never used\n"), errout.str() ); + } + + void localvar2() + { + functionVariableUsage( "void foo()\n" + "{\n" + " int i;\n" + " return i;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is not assigned a value\n"), errout.str() ); + } + + void localvar3() + { + functionVariableUsage( "void foo()\n" + "{\n" + " int i;\n" + " if ( abc )\n" + " ;\n" + " else i = 0;\n" + "}\n" ); + ASSERT_EQUALS( std::string("[test.cpp:2]: Variable 'i' is assigned a value that is never used\n"), errout.str() ); + } + + void localvar4() + { + functionVariableUsage( "void foo()\n" + "{\n" + " int i = 0;\n" + " f(i);\n" + "}\n" ); + ASSERT_EQUALS( std::string(""), errout.str() ); + } + + +}; + +REGISTER_TEST( TestUnusedVar ) + + diff --git a/token.cpp b/token.cpp index 11340b894..3246457a4 100644 --- a/token.cpp +++ b/token.cpp @@ -1,436 +1,436 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -#ifdef __BORLANDC__ -#include // isalpha, isdigit -#endif - -TOKEN::TOKEN() -{ - _fileIndex = 0; - _cstr = 0; - _str = ""; - _linenr = 0; - _next = 0; - _previous = 0; - _varId = 0; - _isName = false; - _isNumber = false; -} - -TOKEN::~TOKEN() -{ - std::free(_cstr); -} - -void TOKEN::setstr( const char s[] ) -{ - _str = s; - std::free(_cstr); -#ifndef _MSC_VER - _cstr = strdup(s); -#else - _cstr = _strdup(s); -#endif - _isName = bool(_str[0]=='_' || isalpha(_str[0])); +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +#ifdef __BORLANDC__ +#include // isalpha, isdigit +#endif + +TOKEN::TOKEN() +{ + _fileIndex = 0; + _cstr = 0; + _str = ""; + _linenr = 0; + _next = 0; + _previous = 0; + _varId = 0; + _isName = false; + _isNumber = false; +} + +TOKEN::~TOKEN() +{ + std::free(_cstr); +} + +void TOKEN::setstr( const char s[] ) +{ + _str = s; + std::free(_cstr); +#ifndef _MSC_VER + _cstr = strdup(s); +#else + _cstr = _strdup(s); +#endif + _isName = bool(_str[0]=='_' || isalpha(_str[0])); _isNumber = bool(isdigit(_str[0]) != 0); - _varId = 0; -} - -void TOKEN::combineWithNext(const char str1[], const char str2[]) -{ - if (!(_next)) - return; - if (_str!=str1 || _next->_str!=str2) - return; - - std::string newstr(std::string(str1) + std::string(str2)); - setstr( newstr.c_str() ); - deleteNext(); -} - -void TOKEN::deleteNext() -{ - TOKEN *n = _next; - _next = n->next(); + _varId = 0; +} + +void TOKEN::combineWithNext(const char str1[], const char str2[]) +{ + if (!(_next)) + return; + if (_str!=str1 || _next->_str!=str2) + return; + + std::string newstr(std::string(str1) + std::string(str2)); + setstr( newstr.c_str() ); + deleteNext(); +} + +void TOKEN::deleteNext() +{ + TOKEN *n = _next; + _next = n->next(); delete n; if (_next) - _next->previous(this); -} - -const TOKEN *TOKEN::tokAt(int index) const -{ - const TOKEN *tok = this; - while (index>0 && tok) - { - tok = tok->next(); - index--; - } - return tok; -} - -const char *TOKEN::strAt(int index) const -{ - const TOKEN *tok = this->tokAt(index); - return tok ? tok->_cstr : ""; -} - -int TOKEN::multiCompare( const char *needle, const char *haystack ) -{ - bool emptyStringFound = false; - bool findNextOr = false; - const char *haystackPointer = haystack; - for( ; *needle; ++needle ) - { - if( *needle == '|' ) - { - // If needle and haystack are both at the end, we have a match. - if( *haystackPointer == 0 ) - return 1; - - haystackPointer = haystack; - if( findNextOr ) - findNextOr = false; - else - emptyStringFound = true; - - continue; - } - - if( findNextOr ) - continue; - - // If haystack and needle don't share the same character, reset - // haystackpointer and find next '|' character. - if( *haystackPointer != *needle ) - { - haystackPointer = haystack; - findNextOr = true; - continue; - } - - // All characters in haystack and needle have matched this far - haystackPointer++; - } - - // If both needle and haystack are at the end, then we have a match. - if( *haystackPointer == 0 ) - return 1; - - // If empty string was found or if last character in needle was '|' - if( emptyStringFound || findNextOr == false ) - return 0; - - return -1; -} - -bool TOKEN::Match(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[], unsigned int varid) -{ - const char *p = pattern; - while ( *p ) - { - // Skip spaces in pattern.. - while ( *p == ' ' ) - p++; - - if (!tok) - { - // If we have no tokens, pattern "!!else" should return true - if( isNotPattern( p ) ) - return true; - else - return false; - } - - // Extract token from pattern.. - // TODO: Refactor this so there can't be buffer overflows - char str[500]; - char *s = str; - while (*p && *p!=' ') - { - *s = *p; - s++; - p++; - } - *s = 0; - - // No token => Success! - if (str[0] == 0) - return true; - - bool useVar1; - // Any symbolname.. - if (strcmp(str,"%var%")==0 || strcmp(str,"%type%")==0) - { - if (!tok->isName()) - return false; - } - - // Accept any token - else if (strcmp(str,"%any%")==0 ) - { - - } - - // Variable name.. - else if ((useVar1 = (strcmp(str,"%var1%")==0)) || strcmp(str,"%var2%")==0) - { - const char **varname = useVar1 ? varname1 : varname2; - - if ( ! varname ) - return false; - - if (tok->_str != varname[0]) - return false; - - for ( int i = 1; varname[i]; i++ ) - { - if ( !(tok->tokAt(2)) ) - return false; - - if ( strcmp(tok->strAt( 1), ".") ) - return false; - - if ( strcmp(tok->strAt( 2), varname[i]) ) - return false; - - tok = tok->tokAt(2); - } - } - - else if (strcmp(str,"%varid%")==0) - { - if ( tok->varId() != varid ) - return false; - } - - else if (strcmp(str,"%num%")==0) - { - if ( ! tok->isNumber() ) - return false; - } - - - else if (strcmp(str,"%str%")==0) - { - if ( tok->_str[0] != '\"' ) - return false; - } - - // [.. => search for a one-character token.. - else if (str[0]=='[' && strchr(str, ']') && tok->_str[1] == 0) - { - *strrchr(str, ']') = 0; - if ( strchr( str + 1, tok->_str[0] ) == 0 ) - return false; - } - - // Parse multi options, such as void|int|char (accept token which is one of these 3) - else if ( strchr(str, '|') && strlen( str ) > 2 ) - { - int res = multiCompare( str, tok->_cstr ); - if( res == 0 ) - { - // Empty alternative matches, use the same token on next round - continue; - } - else if( res == -1 ) - { - // No match - return false; - } - } - - // Parse "not" options. Token can be anything except the given one - else if( isNotPattern( str ) ) - { - if( strcmp( tok->aaaa(), &(str[2]) ) == 0 ) - return false; - } - - else if (str != tok->_str) - return false; - - tok = tok->next(); - } - - // The end of the pattern has been reached and nothing wrong has been found - return true; -} - -bool TOKEN::isNotPattern( const char *pattern ) -{ - if( pattern && strlen(pattern) > 2 && pattern[0] == '!' && pattern[1] == '!' ) - return true; - else - return false; -} - -bool TOKEN::isName() const -{ - return _isName; -} - -bool TOKEN::isNumber() const -{ - return _isNumber; -} - -bool TOKEN::isStandardType() const -{ - bool ret = false; - const char *type[] = {"bool","char","short","int","long","float","double",0}; - for (int i = 0; type[i]; i++) - ret |= (_str == type[i]); - return ret; -} - -//--------------------------------------------------------------------------- - -const TOKEN *TOKEN::findmatch(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[]) -{ - for ( ; tok; tok = tok->next()) - { - if ( TOKEN::Match(tok, pattern, varname1, varname2) ) - return tok; - } - return 0; -} - -const TOKEN *TOKEN::findtoken(const TOKEN *tok1, const char *tokenstr[]) -{ - for (const TOKEN *ret = tok1; ret; ret = ret->next()) - { - unsigned int i = 0; - const TOKEN *tok = ret; - while (tokenstr[i]) - { - if (!tok) - return NULL; - if (*(tokenstr[i]) && (tokenstr[i] != tok->_str)) - break; - tok = tok->next(); - i++; - } - if (!tokenstr[i]) - return ret; - } - return NULL; -} - -unsigned int TOKEN::varId() const -{ - return _varId; -} - -void TOKEN::varId( unsigned int id ) -{ - _varId = id; -} - -TOKEN *TOKEN::next() const -{ - return _next; -} - -void TOKEN::next( TOKEN *next ) -{ - _next = next; -} - -TOKEN *TOKEN::previous() const -{ - return _previous; -} - -void TOKEN::previous( TOKEN *previous ) -{ - _previous = previous; -} - -void TOKEN::insertToken( const char str[] ) -{ - TOKEN *newToken = new TOKEN; + _next->previous(this); +} + +const TOKEN *TOKEN::tokAt(int index) const +{ + const TOKEN *tok = this; + while (index>0 && tok) + { + tok = tok->next(); + index--; + } + return tok; +} + +const char *TOKEN::strAt(int index) const +{ + const TOKEN *tok = this->tokAt(index); + return tok ? tok->_cstr : ""; +} + +int TOKEN::multiCompare( const char *needle, const char *haystack ) +{ + bool emptyStringFound = false; + bool findNextOr = false; + const char *haystackPointer = haystack; + for( ; *needle; ++needle ) + { + if( *needle == '|' ) + { + // If needle and haystack are both at the end, we have a match. + if( *haystackPointer == 0 ) + return 1; + + haystackPointer = haystack; + if( findNextOr ) + findNextOr = false; + else + emptyStringFound = true; + + continue; + } + + if( findNextOr ) + continue; + + // If haystack and needle don't share the same character, reset + // haystackpointer and find next '|' character. + if( *haystackPointer != *needle ) + { + haystackPointer = haystack; + findNextOr = true; + continue; + } + + // All characters in haystack and needle have matched this far + haystackPointer++; + } + + // If both needle and haystack are at the end, then we have a match. + if( *haystackPointer == 0 ) + return 1; + + // If empty string was found or if last character in needle was '|' + if( emptyStringFound || findNextOr == false ) + return 0; + + return -1; +} + +bool TOKEN::Match(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[], unsigned int varid) +{ + const char *p = pattern; + while ( *p ) + { + // Skip spaces in pattern.. + while ( *p == ' ' ) + p++; + + if (!tok) + { + // If we have no tokens, pattern "!!else" should return true + if( isNotPattern( p ) ) + return true; + else + return false; + } + + // Extract token from pattern.. + // TODO: Refactor this so there can't be buffer overflows + char str[500]; + char *s = str; + while (*p && *p!=' ') + { + *s = *p; + s++; + p++; + } + *s = 0; + + // No token => Success! + if (str[0] == 0) + return true; + + bool useVar1; + // Any symbolname.. + if (strcmp(str,"%var%")==0 || strcmp(str,"%type%")==0) + { + if (!tok->isName()) + return false; + } + + // Accept any token + else if (strcmp(str,"%any%")==0 ) + { + + } + + // Variable name.. + else if ((useVar1 = (strcmp(str,"%var1%")==0)) || strcmp(str,"%var2%")==0) + { + const char **varname = useVar1 ? varname1 : varname2; + + if ( ! varname ) + return false; + + if (tok->_str != varname[0]) + return false; + + for ( int i = 1; varname[i]; i++ ) + { + if ( !(tok->tokAt(2)) ) + return false; + + if ( strcmp(tok->strAt( 1), ".") ) + return false; + + if ( strcmp(tok->strAt( 2), varname[i]) ) + return false; + + tok = tok->tokAt(2); + } + } + + else if (strcmp(str,"%varid%")==0) + { + if ( tok->varId() != varid ) + return false; + } + + else if (strcmp(str,"%num%")==0) + { + if ( ! tok->isNumber() ) + return false; + } + + + else if (strcmp(str,"%str%")==0) + { + if ( tok->_str[0] != '\"' ) + return false; + } + + // [.. => search for a one-character token.. + else if (str[0]=='[' && strchr(str, ']') && tok->_str[1] == 0) + { + *strrchr(str, ']') = 0; + if ( strchr( str + 1, tok->_str[0] ) == 0 ) + return false; + } + + // Parse multi options, such as void|int|char (accept token which is one of these 3) + else if ( strchr(str, '|') && strlen( str ) > 2 ) + { + int res = multiCompare( str, tok->_cstr ); + if( res == 0 ) + { + // Empty alternative matches, use the same token on next round + continue; + } + else if( res == -1 ) + { + // No match + return false; + } + } + + // Parse "not" options. Token can be anything except the given one + else if( isNotPattern( str ) ) + { + if( strcmp( tok->aaaa(), &(str[2]) ) == 0 ) + return false; + } + + else if (str != tok->_str) + return false; + + tok = tok->next(); + } + + // The end of the pattern has been reached and nothing wrong has been found + return true; +} + +bool TOKEN::isNotPattern( const char *pattern ) +{ + if( pattern && strlen(pattern) > 2 && pattern[0] == '!' && pattern[1] == '!' ) + return true; + else + return false; +} + +bool TOKEN::isName() const +{ + return _isName; +} + +bool TOKEN::isNumber() const +{ + return _isNumber; +} + +bool TOKEN::isStandardType() const +{ + bool ret = false; + const char *type[] = {"bool","char","short","int","long","float","double",0}; + for (int i = 0; type[i]; i++) + ret |= (_str == type[i]); + return ret; +} + +//--------------------------------------------------------------------------- + +const TOKEN *TOKEN::findmatch(const TOKEN *tok, const char pattern[], const char *varname1[], const char *varname2[]) +{ + for ( ; tok; tok = tok->next()) + { + if ( TOKEN::Match(tok, pattern, varname1, varname2) ) + return tok; + } + return 0; +} + +const TOKEN *TOKEN::findtoken(const TOKEN *tok1, const char *tokenstr[]) +{ + for (const TOKEN *ret = tok1; ret; ret = ret->next()) + { + unsigned int i = 0; + const TOKEN *tok = ret; + while (tokenstr[i]) + { + if (!tok) + return NULL; + if (*(tokenstr[i]) && (tokenstr[i] != tok->_str)) + break; + tok = tok->next(); + i++; + } + if (!tokenstr[i]) + return ret; + } + return NULL; +} + +unsigned int TOKEN::varId() const +{ + return _varId; +} + +void TOKEN::varId( unsigned int id ) +{ + _varId = id; +} + +TOKEN *TOKEN::next() const +{ + return _next; +} + +void TOKEN::next( TOKEN *next ) +{ + _next = next; +} + +TOKEN *TOKEN::previous() const +{ + return _previous; +} + +void TOKEN::previous( TOKEN *previous ) +{ + _previous = previous; +} + +void TOKEN::insertToken( const char str[] ) +{ + TOKEN *newToken = new TOKEN; newToken->setstr( str ); newToken->_linenr = _linenr; - newToken->_fileIndex = _fileIndex; - if( this->next() ) - { - newToken->next( this->next() ); - newToken->next()->previous( newToken ); - } + newToken->_fileIndex = _fileIndex; + if( this->next() ) + { + newToken->next( this->next() ); + newToken->next()->previous( newToken ); + } - this->next( newToken ); - newToken->previous( this ); -} - -void TOKEN::eraseTokens( TOKEN *begin, const TOKEN *end ) -{ - if ( ! begin ) - return; - - while ( begin->next() && begin->next() != end ) - { - begin->deleteNext(); - } -} - -unsigned int TOKEN::fileIndex() const -{ - return _fileIndex; -} - -void TOKEN::fileIndex( unsigned int fileIndex ) -{ - _fileIndex = fileIndex; -} - -unsigned int TOKEN::linenr() const -{ - return _linenr; -} - -void TOKEN::linenr( unsigned int linenr ) -{ - _linenr = linenr; -} - -void TOKEN::printOut( const char *title ) const -{ - std::cout << std::endl << "###"; - if ( title ) - std::cout << " " << title << " "; - else - std::cout << "########"; - - std::cout << "###" << std::endl; - for( const TOKEN *t = this; t; t = t->next() ) - { - std::cout << t->linenr() << ": " << t->str(); - if ( t->varId() ) - std::cout << " ("<< t->varId() <<")"; - - std::cout << std::endl; - } -} + this->next( newToken ); + newToken->previous( this ); +} + +void TOKEN::eraseTokens( TOKEN *begin, const TOKEN *end ) +{ + if ( ! begin ) + return; + + while ( begin->next() && begin->next() != end ) + { + begin->deleteNext(); + } +} + +unsigned int TOKEN::fileIndex() const +{ + return _fileIndex; +} + +void TOKEN::fileIndex( unsigned int fileIndex ) +{ + _fileIndex = fileIndex; +} + +unsigned int TOKEN::linenr() const +{ + return _linenr; +} + +void TOKEN::linenr( unsigned int linenr ) +{ + _linenr = linenr; +} + +void TOKEN::printOut( const char *title ) const +{ + std::cout << std::endl << "###"; + if ( title ) + std::cout << " " << title << " "; + else + std::cout << "########"; + + std::cout << "###" << std::endl; + for( const TOKEN *t = this; t; t = t->next() ) + { + std::cout << t->linenr() << ": " << t->str(); + if ( t->varId() ) + std::cout << " ("<< t->varId() <<")"; + + std::cout << std::endl; + } +} diff --git a/token.h b/token.h index bc83602a8..c93b77da0 100644 --- a/token.h +++ b/token.h @@ -1,183 +1,183 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi - * - * 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 - -class TOKEN -{ -public: - TOKEN(); - ~TOKEN(); - void setstr( const char s[] ); - - const std::string &str() const - { return _str; } - - const char *aaaa() const - { return _cstr; } - - char aaaa0() const - { return _cstr[0]; } - - char aaaa1() const - { return _cstr[1]; } - - - /** - * Combine two tokens that belong to each other. - * Ex: "<" and "=" may become "<=" - */ - void combineWithNext(const char str1[], const char str2[]); - - /** - * Unlink and delete next token. - */ - void deleteNext(); - - /** - * Returns token in given index, related to this token. - * For example index 1 would return next token, and 2 - * would return next from that one. - */ - const TOKEN *tokAt(int index) const; - - const char *strAt(int index) const; - - /** - * Match given token (or list of tokens) to a pattern list. - * - * Possible patterns - * "%any%" any token - * "%var%" any token which is a name or type e.g. "hello" or "int" - * "%num%" Any numeric token, e.g. "23" - * "%str%" Any token starting with "-character (C-string). - * "%var1%" Match with parameter varname1 - * "%var2%" Match with parameter varname2 - * "%varid%" Match with parameter varid - * "[abc]" Any of the characters 'a' or 'b' or 'c' - * "int|void|char" Any of the strings, int, void or char - * "int|void|char|" Any of the strings, int, void or char or empty string - * "!!else" No tokens or any token that is not "else". - * "someRandomText" If token contains "someRandomText". - * - * The patterns can be also combined to compare to multiple tokens at once - * by separating tokens with a space, e.g. - * ") const|void {" will return true if first token is ')' next token is either - * "const" or "void" and token after that is '{'. If even one of the tokens does not - * match its pattern, false is returned. - * - * @param tok List of tokens to be compared to the pattern - * @param pattern The pattern where tokens are compared, e.g. "const" - * or ") const|volatile| {". - * @param varname1 Used with pattern "%var1%" and "%var2%" - * @param varname2 Used with pattern "%var1%" and "%var2%" - * @return true if given token matches with given pattern - * false if given token does not match with given pattern - */ - static bool Match(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0, unsigned int varid=0); - - bool isName() const; - bool isNumber() const; - bool isStandardType() const; - static const TOKEN *findmatch(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0); - static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]); - - /** - * Needle is build from multiple alternatives. If one of - * them is equal to haystack, return value is 1. If there - * are no matches, but one alternative to needle is empty - * string, return value is 0. If needle was not found, return - * value is -1. - * - * @param needle e.g. "one|two" or "|one|two" - * @param haystack e.g. "one", "two" or "invalid" - * @return 1 if needle is found from the haystack - * 0 if needle was empty string - * -1 if needle was not found - */ - static int multiCompare( const char *needle, const char *haystack ); - - - unsigned int linenr() const; - void linenr( unsigned int linenr ); - - unsigned int fileIndex() const; - void fileIndex( unsigned int fileIndex ); - - TOKEN *next() const; - - - /** - * Delete tokens between begin and end. E.g. if begin = 1 - * and end = 5, tokens 2,3 and 4 would be erased. - * - * @param begin Tokens after this will be erased. - * @param end Tokens before this will be erased. - */ - static void eraseTokens( TOKEN *begin, const TOKEN *end ); - - /** - * Insert new token after this token. This function will handle - * relations between next and previous token also. - * @param str String for the new token. - */ - void insertToken( const char str[] ); - - TOKEN *previous() const; - - - unsigned int varId() const; - void varId( unsigned int id ); - - /** - * For debugging purposes, prints token and all tokens - * followed by it. - * @param title Title for the printout or use default parameter or 0 - * for no title. - */ - void printOut( const char *title = 0 ) const; - -private: - void next( TOKEN *next ); - void previous( TOKEN *previous ); - - /** - * Return true if pattern is e.g. "!!else". - * See Match() for more info. - * - * @param pattern Pattern to match, e.g. "if ; !!else" - * @return true if pattern starts with "!!" and contains 3 - * or more characters. - */ - static bool isNotPattern( const char *pattern ); - - std::string _str; - char * _cstr; - bool _isName; - bool _isNumber; - unsigned int _varId; - TOKEN *_next; - TOKEN *_previous; - unsigned int _fileIndex; - unsigned int _linenr; -}; - -#endif // TOKEN_H +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007-2008 Daniel Marjamäki and Reijo Tomperi + * + * 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 + +class TOKEN +{ +public: + TOKEN(); + ~TOKEN(); + void setstr( const char s[] ); + + const std::string &str() const + { return _str; } + + const char *aaaa() const + { return _cstr; } + + char aaaa0() const + { return _cstr[0]; } + + char aaaa1() const + { return _cstr[1]; } + + + /** + * Combine two tokens that belong to each other. + * Ex: "<" and "=" may become "<=" + */ + void combineWithNext(const char str1[], const char str2[]); + + /** + * Unlink and delete next token. + */ + void deleteNext(); + + /** + * Returns token in given index, related to this token. + * For example index 1 would return next token, and 2 + * would return next from that one. + */ + const TOKEN *tokAt(int index) const; + + const char *strAt(int index) const; + + /** + * Match given token (or list of tokens) to a pattern list. + * + * Possible patterns + * "%any%" any token + * "%var%" any token which is a name or type e.g. "hello" or "int" + * "%num%" Any numeric token, e.g. "23" + * "%str%" Any token starting with "-character (C-string). + * "%var1%" Match with parameter varname1 + * "%var2%" Match with parameter varname2 + * "%varid%" Match with parameter varid + * "[abc]" Any of the characters 'a' or 'b' or 'c' + * "int|void|char" Any of the strings, int, void or char + * "int|void|char|" Any of the strings, int, void or char or empty string + * "!!else" No tokens or any token that is not "else". + * "someRandomText" If token contains "someRandomText". + * + * The patterns can be also combined to compare to multiple tokens at once + * by separating tokens with a space, e.g. + * ") const|void {" will return true if first token is ')' next token is either + * "const" or "void" and token after that is '{'. If even one of the tokens does not + * match its pattern, false is returned. + * + * @param tok List of tokens to be compared to the pattern + * @param pattern The pattern where tokens are compared, e.g. "const" + * or ") const|volatile| {". + * @param varname1 Used with pattern "%var1%" and "%var2%" + * @param varname2 Used with pattern "%var1%" and "%var2%" + * @return true if given token matches with given pattern + * false if given token does not match with given pattern + */ + static bool Match(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0, unsigned int varid=0); + + bool isName() const; + bool isNumber() const; + bool isStandardType() const; + static const TOKEN *findmatch(const TOKEN *tok, const char pattern[], const char *varname1[]=0, const char *varname2[]=0); + static const TOKEN *findtoken(const TOKEN *tok1, const char *tokenstr[]); + + /** + * Needle is build from multiple alternatives. If one of + * them is equal to haystack, return value is 1. If there + * are no matches, but one alternative to needle is empty + * string, return value is 0. If needle was not found, return + * value is -1. + * + * @param needle e.g. "one|two" or "|one|two" + * @param haystack e.g. "one", "two" or "invalid" + * @return 1 if needle is found from the haystack + * 0 if needle was empty string + * -1 if needle was not found + */ + static int multiCompare( const char *needle, const char *haystack ); + + + unsigned int linenr() const; + void linenr( unsigned int linenr ); + + unsigned int fileIndex() const; + void fileIndex( unsigned int fileIndex ); + + TOKEN *next() const; + + + /** + * Delete tokens between begin and end. E.g. if begin = 1 + * and end = 5, tokens 2,3 and 4 would be erased. + * + * @param begin Tokens after this will be erased. + * @param end Tokens before this will be erased. + */ + static void eraseTokens( TOKEN *begin, const TOKEN *end ); + + /** + * Insert new token after this token. This function will handle + * relations between next and previous token also. + * @param str String for the new token. + */ + void insertToken( const char str[] ); + + TOKEN *previous() const; + + + unsigned int varId() const; + void varId( unsigned int id ); + + /** + * For debugging purposes, prints token and all tokens + * followed by it. + * @param title Title for the printout or use default parameter or 0 + * for no title. + */ + void printOut( const char *title = 0 ) const; + +private: + void next( TOKEN *next ); + void previous( TOKEN *previous ); + + /** + * Return true if pattern is e.g. "!!else". + * See Match() for more info. + * + * @param pattern Pattern to match, e.g. "if ; !!else" + * @return true if pattern starts with "!!" and contains 3 + * or more characters. + */ + static bool isNotPattern( const char *pattern ); + + std::string _str; + char * _cstr; + bool _isName; + bool _isNumber; + unsigned int _varId; + TOKEN *_next; + TOKEN *_previous; + unsigned int _fileIndex; + unsigned int _linenr; +}; + +#endif // TOKEN_H diff --git a/tokenize.cpp b/tokenize.cpp index 4c2fe8d56..42b934275 100644 --- a/tokenize.cpp +++ b/tokenize.cpp @@ -1,669 +1,669 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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 // <- strtoul -#include - -#ifdef __BORLANDC__ -#include -#include -#endif - -#ifndef _MSC_VER -#define _strdup(str) strdup(str) -#endif - - - -//--------------------------------------------------------------------------- - -Tokenizer::Tokenizer() -{ - _tokens = 0; - _tokensBack = 0; - _dsymlist = 0; -} - -Tokenizer::~Tokenizer() -{ - DeallocateTokens(); -} - -//--------------------------------------------------------------------------- - -// Helper functions.. - -TOKEN *Tokenizer::_gettok(TOKEN *tok, int index) -{ - while (tok && index>0) - { - tok = tok->next(); - index--; - } - return tok; -} - -//--------------------------------------------------------------------------- - -const TOKEN *Tokenizer::tokens() const -{ - return _tokens; -} - -//--------------------------------------------------------------------------- -// Defined symbols. -// "#define abc 123" will create a defined symbol "abc" with the value 123 -//--------------------------------------------------------------------------- - - - -const std::vector *Tokenizer::getFiles() const -{ - return &_files; -} - -void Tokenizer::Define(const char Name[], const char Value[]) -{ - if (!(Name && Name[0])) - return; - - if (!(Value && Value[0])) - return; - - // Is 'Value' a decimal value.. - bool dec = true, hex = true; - for (int i = 0; Value[i]; i++) - { - if ( ! isdigit(Value[i]) ) - dec = false; - - if ( ! isxdigit(Value[i]) && (!(i==1 && Value[i]=='x'))) - hex = false; - } - - if (!dec && !hex) - return; - - char *strValue = _strdup(Value); - - if (!dec && hex) - { - // Convert Value from hexadecimal to decimal - unsigned long value; - std::istringstream istr(Value+2); - istr >> std::hex >> value; - std::ostringstream ostr; - ostr << value; - free(strValue); - strValue = _strdup(ostr.str().c_str()); - } - - DefineSymbol *NewSym = new DefineSymbol; - memset(NewSym, 0, sizeof(DefineSymbol)); - NewSym->name = _strdup(Name); - NewSym->value = strValue; - NewSym->next = _dsymlist; - _dsymlist = NewSym; -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// 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->setstr( str2.str().c_str() ); - } - - _tokensBack->linenr( lineno ); - _tokensBack->fileIndex( fileno ); - - // Check if str is defined.. - for (DefineSymbol *sym = _dsymlist; sym; sym = sym->next) - { - if (strcmp(str,sym->name)==0) - { - _tokensBack->setstr(sym->value); - break; - } - } -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// 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->next()->fileIndex( src->fileIndex() ); - dest->next()->linenr( src->linenr() ); - dest = dest->next(); - src = src->next(); - n--; - } -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// Tokenize - tokenizes a given file. -//--------------------------------------------------------------------------- - -void Tokenizer::tokenize(std::istream &code, const char FileName[]) -{ - // Has this file been tokenized already? - for (unsigned int i = 0; i < _files.size(); i++) - { - if ( SameFileName( _files[i].c_str(), FileName ) ) - return; - } - - // The "_files" vector remembers what files have been tokenized.. - _files.push_back( FileLister::simplifyPath( FileName ) ); - - // Tokenize the file.. - tokenizeCode( code, (unsigned int)(_files.size() - 1) ); -} -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// Tokenize - tokenizes input stream -//--------------------------------------------------------------------------- - -void Tokenizer::tokenizeCode(std::istream &code, const unsigned int FileIndex) -{ - // Tokenize the file. - unsigned int lineno = 1; - std::string CurrentToken; - 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; - - // Preprocessor stuff? - if (ch == '#' && CurrentToken.empty()) - { - 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(),"#include",8)==0 && - line.find("\"") != std::string::npos) - { - // Extract the filename - line.erase(0, line.find("\"")+1); - 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; - } - - addtoken("#include", lineno, FileIndex); - addtoken(line.c_str(), lineno, FileIndex); - - std::ifstream fin( line.c_str() ); - tokenize(fin, line.c_str()); - } - - else if (strncmp(line.c_str(), "#define", 7) == 0) - { - std::string strId; - enum {Space1, Id, Space2, Value} State; - State = Space1; - for (unsigned int i = 8; i < line.length(); i++) - { - if (State==Space1 || State==Space2) - { - if (isspace(line[i])) - continue; - State = (State==Space1) ? Id : Value; - } - - else if (State==Id) - { - if ( isspace( line[i] ) ) - { - strId = CurrentToken; - CurrentToken.clear(); - State = Space2; - continue; - } - else if ( ! isalnum(line[i]) ) - { - break; - } - } - - CurrentToken += line[i]; - } - - if (State==Value) - { - addtoken("def", lineno, FileIndex); - addtoken(strId.c_str(), lineno, FileIndex); - addtoken(";", lineno, FileIndex); - Define(strId.c_str(), CurrentToken.c_str()); - } - - CurrentToken.clear(); - } - - else - { - addtoken("#", lineno, FileIndex); - addtoken(";", lineno, FileIndex); - } - - lineno++; - continue; - } - - if (ch == '\n') - { - // Add current token.. - addtoken(CurrentToken.c_str(), lineno++, FileIndex); - CurrentToken.clear(); - continue; - } - - // Comments.. - if (ch == '/' && code.good()) - { - bool newstatement = bool( strchr(";{}", CurrentToken.empty() ? '\0' : CurrentToken[0]) != NULL ); - - // Add current token.. - addtoken(CurrentToken.c_str(), lineno, FileIndex); - CurrentToken.clear(); - - // Read next character.. - ch = (char)code.get(); - - // If '//'.. - if (ch == '/') - { - std::string comment; - getline( code, comment ); // Parse in the whole comment - - // If the comment says something like "fred is deleted" then generate appropriate tokens for that - comment = comment + " "; - if ( newstatement && comment.find(" deleted ")!=std::string::npos ) - { - // delete - addtoken( "delete", lineno, FileIndex ); - - // fred - std::string::size_type pos1 = comment.find_first_not_of(" \t"); - std::string::size_type pos2 = comment.find(" ", pos1); - std::string firstWord = comment.substr( pos1, pos2-pos1 ); - addtoken( firstWord.c_str(), lineno, FileIndex ); - - // ; - addtoken( ";", lineno, FileIndex ); - } - - lineno++; - continue; - } - - // If '/*'.. - if (ch == '*') - { - char chPrev; - ch = chPrev = 'A'; - while (code.good() && (chPrev!='*' || ch!='/')) - { - chPrev = ch; - ch = (char)code.get(); - if (ch == '\n') - lineno++; - } - continue; - } - - // Not a comment.. add token.. - addtoken("/", lineno, FileIndex); - } - - // 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 (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()) - { - tok->combineWithNext("<", "<"); - tok->combineWithNext(">", ">"); - - tok->combineWithNext("&", "&"); - tok->combineWithNext("|", "|"); - - tok->combineWithNext("+", "="); - tok->combineWithNext("-", "="); - tok->combineWithNext("*", "="); - tok->combineWithNext("/", "="); - tok->combineWithNext("&", "="); - tok->combineWithNext("|", "="); - - tok->combineWithNext("=", "="); - tok->combineWithNext("!", "="); - tok->combineWithNext("<", "="); - tok->combineWithNext(">", "="); - - tok->combineWithNext(":", ":"); - tok->combineWithNext("-", ">"); - - tok->combineWithNext("private", ":"); - tok->combineWithNext("protected", ":"); - tok->combineWithNext("public", ":"); - } - - // Replace "->" with "." - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if ( tok->str() == "->" ) - { - tok->setstr("."); - } - } - - // typedef.. - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if (TOKEN::Match(tok, "typedef %type% %type% ;")) - { - const char *type1 = tok->strAt( 1); - const char *type2 = tok->strAt( 2); - for ( TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) - { - if (tok2->aaaa()!=type1 && tok2->aaaa()!=type2 && (tok2->str() == type2)) - { - tok2->setstr(type1); - } - } - } - - 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); - - TOKEN *tok2 = tok; - while ( ! TOKEN::Match(tok2, ";") ) - tok2 = tok2->next(); - - for ( ; tok2; tok2 = tok2->next() ) - { - if (tok2->aaaa()!=type3 && (tok2->str() == type3)) - { - tok2->setstr(type1); - tok2->insertToken( type2 ); - tok2->next()->fileIndex( tok2->fileIndex() ); - tok2->next()->linenr( tok2->linenr() ); - tok2 = tok2->next(); - } - } - } - } - - - // Remove __asm.. - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if ( TOKEN::Match(tok->next(), "__asm {") ) - { - while ( tok->next() ) - { - bool last = TOKEN::Match( 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::Match(_tokens, "volatile") ) - { - TOKEN *tok = _tokens; - _tokens = _tokens->next(); - delete tok; - } - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - while ( TOKEN::Match(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.. - unsigned int _varId = 0; - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if ( ! TOKEN::Match(tok, "[;{}(] %type% %var%") ) - continue; - - // Determine name of declared variable.. - const char *varname = 0; - TOKEN *tok2 = tok->next(); - 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; - for ( tok2 = tok->next(); tok2 && indentlevel >= 0; tok2 = tok2->next() ) - { - if ( tok2->str() == varname ) - tok2->varId( _varId ); - else if ( tok2->str() == "{" ) - ++indentlevel; - else if ( tok2->str() == "}" ) - --indentlevel; - else if ( tok2->str() == "(" ) - ++parlevel; - else if ( tok2->str() == ")" ) - --parlevel; - else if ( parlevel < 0 && tok2->str() == ";" ) - break; - } - } +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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 // <- strtoul +#include + +#ifdef __BORLANDC__ +#include +#include +#endif + +#ifndef _MSC_VER +#define _strdup(str) strdup(str) +#endif + + + +//--------------------------------------------------------------------------- + +Tokenizer::Tokenizer() +{ + _tokens = 0; + _tokensBack = 0; + _dsymlist = 0; +} + +Tokenizer::~Tokenizer() +{ + DeallocateTokens(); +} + +//--------------------------------------------------------------------------- + +// Helper functions.. + +TOKEN *Tokenizer::_gettok(TOKEN *tok, int index) +{ + while (tok && index>0) + { + tok = tok->next(); + index--; + } + return tok; +} + +//--------------------------------------------------------------------------- + +const TOKEN *Tokenizer::tokens() const +{ + return _tokens; +} + +//--------------------------------------------------------------------------- +// Defined symbols. +// "#define abc 123" will create a defined symbol "abc" with the value 123 +//--------------------------------------------------------------------------- + + + +const std::vector *Tokenizer::getFiles() const +{ + return &_files; +} + +void Tokenizer::Define(const char Name[], const char Value[]) +{ + if (!(Name && Name[0])) + return; + + if (!(Value && Value[0])) + return; + + // Is 'Value' a decimal value.. + bool dec = true, hex = true; + for (int i = 0; Value[i]; i++) + { + if ( ! isdigit(Value[i]) ) + dec = false; + + if ( ! isxdigit(Value[i]) && (!(i==1 && Value[i]=='x'))) + hex = false; + } + + if (!dec && !hex) + return; + + char *strValue = _strdup(Value); + + if (!dec && hex) + { + // Convert Value from hexadecimal to decimal + unsigned long value; + std::istringstream istr(Value+2); + istr >> std::hex >> value; + std::ostringstream ostr; + ostr << value; + free(strValue); + strValue = _strdup(ostr.str().c_str()); + } + + DefineSymbol *NewSym = new DefineSymbol; + memset(NewSym, 0, sizeof(DefineSymbol)); + NewSym->name = _strdup(Name); + NewSym->value = strValue; + NewSym->next = _dsymlist; + _dsymlist = NewSym; +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// 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->setstr( str2.str().c_str() ); + } + + _tokensBack->linenr( lineno ); + _tokensBack->fileIndex( fileno ); + + // Check if str is defined.. + for (DefineSymbol *sym = _dsymlist; sym; sym = sym->next) + { + if (strcmp(str,sym->name)==0) + { + _tokensBack->setstr(sym->value); + break; + } + } +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// 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->next()->fileIndex( src->fileIndex() ); + dest->next()->linenr( src->linenr() ); + dest = dest->next(); + src = src->next(); + n--; + } +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Tokenize - tokenizes a given file. +//--------------------------------------------------------------------------- + +void Tokenizer::tokenize(std::istream &code, const char FileName[]) +{ + // Has this file been tokenized already? + for (unsigned int i = 0; i < _files.size(); i++) + { + if ( SameFileName( _files[i].c_str(), FileName ) ) + return; + } + + // The "_files" vector remembers what files have been tokenized.. + _files.push_back( FileLister::simplifyPath( FileName ) ); + + // Tokenize the file.. + tokenizeCode( code, (unsigned int)(_files.size() - 1) ); +} +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// Tokenize - tokenizes input stream +//--------------------------------------------------------------------------- + +void Tokenizer::tokenizeCode(std::istream &code, const unsigned int FileIndex) +{ + // Tokenize the file. + unsigned int lineno = 1; + std::string CurrentToken; + 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; + + // Preprocessor stuff? + if (ch == '#' && CurrentToken.empty()) + { + 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(),"#include",8)==0 && + line.find("\"") != std::string::npos) + { + // Extract the filename + line.erase(0, line.find("\"")+1); + 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; + } + + addtoken("#include", lineno, FileIndex); + addtoken(line.c_str(), lineno, FileIndex); + + std::ifstream fin( line.c_str() ); + tokenize(fin, line.c_str()); + } + + else if (strncmp(line.c_str(), "#define", 7) == 0) + { + std::string strId; + enum {Space1, Id, Space2, Value} State; + State = Space1; + for (unsigned int i = 8; i < line.length(); i++) + { + if (State==Space1 || State==Space2) + { + if (isspace(line[i])) + continue; + State = (State==Space1) ? Id : Value; + } + + else if (State==Id) + { + if ( isspace( line[i] ) ) + { + strId = CurrentToken; + CurrentToken.clear(); + State = Space2; + continue; + } + else if ( ! isalnum(line[i]) ) + { + break; + } + } + + CurrentToken += line[i]; + } + + if (State==Value) + { + addtoken("def", lineno, FileIndex); + addtoken(strId.c_str(), lineno, FileIndex); + addtoken(";", lineno, FileIndex); + Define(strId.c_str(), CurrentToken.c_str()); + } + + CurrentToken.clear(); + } + + else + { + addtoken("#", lineno, FileIndex); + addtoken(";", lineno, FileIndex); + } + + lineno++; + continue; + } + + if (ch == '\n') + { + // Add current token.. + addtoken(CurrentToken.c_str(), lineno++, FileIndex); + CurrentToken.clear(); + continue; + } + + // Comments.. + if (ch == '/' && code.good()) + { + bool newstatement = bool( strchr(";{}", CurrentToken.empty() ? '\0' : CurrentToken[0]) != NULL ); + + // Add current token.. + addtoken(CurrentToken.c_str(), lineno, FileIndex); + CurrentToken.clear(); + + // Read next character.. + ch = (char)code.get(); + + // If '//'.. + if (ch == '/') + { + std::string comment; + getline( code, comment ); // Parse in the whole comment + + // If the comment says something like "fred is deleted" then generate appropriate tokens for that + comment = comment + " "; + if ( newstatement && comment.find(" deleted ")!=std::string::npos ) + { + // delete + addtoken( "delete", lineno, FileIndex ); + + // fred + std::string::size_type pos1 = comment.find_first_not_of(" \t"); + std::string::size_type pos2 = comment.find(" ", pos1); + std::string firstWord = comment.substr( pos1, pos2-pos1 ); + addtoken( firstWord.c_str(), lineno, FileIndex ); + + // ; + addtoken( ";", lineno, FileIndex ); + } + + lineno++; + continue; + } + + // If '/*'.. + if (ch == '*') + { + char chPrev; + ch = chPrev = 'A'; + while (code.good() && (chPrev!='*' || ch!='/')) + { + chPrev = ch; + ch = (char)code.get(); + if (ch == '\n') + lineno++; + } + continue; + } + + // Not a comment.. add token.. + addtoken("/", lineno, FileIndex); + } + + // 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 (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()) + { + tok->combineWithNext("<", "<"); + tok->combineWithNext(">", ">"); + + tok->combineWithNext("&", "&"); + tok->combineWithNext("|", "|"); + + tok->combineWithNext("+", "="); + tok->combineWithNext("-", "="); + tok->combineWithNext("*", "="); + tok->combineWithNext("/", "="); + tok->combineWithNext("&", "="); + tok->combineWithNext("|", "="); + + tok->combineWithNext("=", "="); + tok->combineWithNext("!", "="); + tok->combineWithNext("<", "="); + tok->combineWithNext(">", "="); + + tok->combineWithNext(":", ":"); + tok->combineWithNext("-", ">"); + + tok->combineWithNext("private", ":"); + tok->combineWithNext("protected", ":"); + tok->combineWithNext("public", ":"); + } + + // Replace "->" with "." + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if ( tok->str() == "->" ) + { + tok->setstr("."); + } + } + + // typedef.. + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if (TOKEN::Match(tok, "typedef %type% %type% ;")) + { + const char *type1 = tok->strAt( 1); + const char *type2 = tok->strAt( 2); + for ( TOKEN *tok2 = tok; tok2; tok2 = tok2->next() ) + { + if (tok2->aaaa()!=type1 && tok2->aaaa()!=type2 && (tok2->str() == type2)) + { + tok2->setstr(type1); + } + } + } + + 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); + + TOKEN *tok2 = tok; + while ( ! TOKEN::Match(tok2, ";") ) + tok2 = tok2->next(); + + for ( ; tok2; tok2 = tok2->next() ) + { + if (tok2->aaaa()!=type3 && (tok2->str() == type3)) + { + tok2->setstr(type1); + tok2->insertToken( type2 ); + tok2->next()->fileIndex( tok2->fileIndex() ); + tok2->next()->linenr( tok2->linenr() ); + tok2 = tok2->next(); + } + } + } + } + + + // Remove __asm.. + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if ( TOKEN::Match(tok->next(), "__asm {") ) + { + while ( tok->next() ) + { + bool last = TOKEN::Match( 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::Match(_tokens, "volatile") ) + { + TOKEN *tok = _tokens; + _tokens = _tokens->next(); + delete tok; + } + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + while ( TOKEN::Match(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.. + unsigned int _varId = 0; + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if ( ! TOKEN::Match(tok, "[;{}(] %type% %var%") ) + continue; + + // Determine name of declared variable.. + const char *varname = 0; + TOKEN *tok2 = tok->next(); + 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; + for ( tok2 = tok->next(); tok2 && indentlevel >= 0; tok2 = tok2->next() ) + { + if ( tok2->str() == varname ) + tok2->varId( _varId ); + else if ( tok2->str() == "{" ) + ++indentlevel; + else if ( tok2->str() == "}" ) + --indentlevel; + else if ( tok2->str() == "(" ) + ++parlevel; + else if ( tok2->str() == ")" ) + --parlevel; + else if ( parlevel < 0 && tok2->str() == ";" ) + break; + } + } } // Struct/Class members - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { if ( TOKEN::Match(tok, "%var% . %var%") && tok->varId() != 0 && tok->tokAt(2)->varId() == 0 ) @@ -675,760 +675,760 @@ void Tokenizer::setVarId() if ( TOKEN::Match(tok2, pattern.c_str(), 0, 0, tok->varId()) ) tok2->next()->next()->varId( _varId ); } - } - } -} - - -//--------------------------------------------------------------------------- -// Simplify token list -//--------------------------------------------------------------------------- - -void Tokenizer::simplifyTokenList() -{ - - // Remove the keyword 'unsigned' - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if (tok->next() && (tok->next()->str() == "unsigned")) - { - tok->deleteNext(); - } - } - - // 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 = _gettok(tok,6); tok2; tok2 = tok2->next()) - { - if (tok2->str() == sym) - { - tok2->setstr(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->setstr( 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->setstr( str.str().c_str() ); - for (int i = 0; i < 3; i++) - { - tok->deleteNext(); - } - } - } - - else if (TOKEN::Match(tok, "sizeof ( * %var% )")) - { - tok->setstr("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 = _gettok(tok,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->setstr(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::Match(tok->next(), "* 1") || TOKEN::Match(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->setstr(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->setstr(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") || TOKEN::Match(type0, "return")) - continue; - - TOKEN *tok2 = NULL; - unsigned int typelen = 0; - - if ( TOKEN::Match(type0, "%type% %var% ,|=") ) - { - if ( type0->next()->str() != "operator" ) - { - tok2 = _gettok(type0, 2); // The ',' or '=' token - typelen = 1; - } - } - - else if ( TOKEN::Match(type0, "%type% * %var% ,|=") ) - { - if ( type0->next()->next()->str() != "operator" ) - { - tok2 = _gettok(type0, 3); // The ',' token - typelen = 1; - } - } - - else if ( TOKEN::Match(type0, "%type% %var% [ %num% ] ,|=") ) - { - tok2 = _gettok(type0, 5); // The ',' token - typelen = 1; - } - - else if ( TOKEN::Match(type0, "%type% * %var% [ %num% ] ,|=") ) - { - tok2 = _gettok(type0, 6); // The ',' token - typelen = 1; - } - - else if ( TOKEN::Match(type0, "struct %type% %var% ,|=") ) - { - tok2 = _gettok(type0, 3); - typelen = 2; - } - - else if ( TOKEN::Match(type0, "struct %type% * %var% ,|=") ) - { - tok2 = _gettok(type0, 4); - typelen = 2; - } - - - if (tok2) - { - if (tok2->str() == ",") - { - tok2->setstr(";"); - 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 = _gettok(type0,typelen); - if (VarTok->aaaa0()=='*') - VarTok = VarTok->next(); - InsertTokens(eq, VarTok, 2); - eq->setstr(";"); - - // "= x, " => "= x; type " - if (tok2->str() == ",") - { - tok2->setstr(";"); - InsertTokens( tok2, type0, typelen ); - } - break; - } - - tok2 = tok2->next(); - } - } - } - } - - // Replace NULL with 0.. - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if ( TOKEN::Match(tok, "NULL") ) - tok->setstr("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::Match(tok->next(),"0")) - tok->deleteNext(); - } - } - - bool modified = true; - while ( modified ) - { - modified = false; - modified |= simplifyConditions(); - modified |= simplifyCasts(); - modified |= simplifyFunctionReturn(); - modified |= simplifyKnownVariables(); + } } -} -//--------------------------------------------------------------------------- - - -bool Tokenizer::simplifyConditions() -{ - bool ret = false; - - for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) - { - if (TOKEN::Match(tok, "( true &&") || TOKEN::Match(tok, "&& true &&") || TOKEN::Match(tok->next(), "&& true )")) - { - tok->deleteNext(); - tok->deleteNext(); - ret = true; - } - - else if (TOKEN::Match(tok, "( false ||") || TOKEN::Match(tok, "|| false ||") || TOKEN::Match(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 ((TOKEN::Match(tok, "(") || TOKEN::Match(tok, "&&") || TOKEN::Match(tok, "||")) && - TOKEN::Match(tok->next(), "%num%") && - (TOKEN::Match(tok2, ")") || TOKEN::Match(tok2, "&&") || TOKEN::Match(tok2, "||")) ) - { - tok->next()->setstr((tok->next()->str() != "0") ? "true" : "false"); - ret = true; - } - - // Reduce "(%num% == %num%)" => "(true)"/"(false)" - if ( (TOKEN::Match(tok, "&&") || TOKEN::Match(tok, "||") || TOKEN::Match(tok, "(")) && - TOKEN::Match(tok->tokAt(1), "%num% %any% %num%") && - (TOKEN::Match(tok->tokAt(4), "&&") || TOKEN::Match(tok->tokAt(4), "||") || TOKEN::Match(tok->tokAt(4), ")")) ) - { - 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->setstr( 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::Match(tok2->next(), "(") ) - ++parlevel; - else if ( TOKEN::Match(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->setstr( 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 ) - continue; - } - - else if ( TOKEN::Match(tok2, "%var% = %num% ;") ) - { - unsigned int varid = tok2->varId(); - TOKEN *tok3 = tok2; - while ( 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% )", 0, 0, varid) ) - { - tok3 = tok3->next()->next(); - tok3->setstr( 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(); - - bool staticfunc = false; - bool classfunc = false; - - 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 (strchr("};", tok->aaaa0())) - staticfunc = classfunc = false; - - else if ( tok->str() == "static" ) - staticfunc = true; - - else if ( tok->str() == "::" ) - classfunc = true; - - else if (TOKEN::Match(tok, "%var% (")) - { - // Check if this is the first token of a function implementation.. - for ( const TOKEN *tok2 = tok; 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; - - while (_dsymlist) - { - struct DefineSymbol *next = _dsymlist->next; - free(_dsymlist->name); - free(_dsymlist->value); - delete _dsymlist; - _dsymlist = next; - } - - _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 ( TOKEN::Match(ftok, ",") ) - ++_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(); -} - -//--------------------------------------------------------------------------- - -bool Tokenizer::SameFileName( const char fname1[], const char fname2[] ) -{ -#ifdef __linux__ - return bool( strcmp(fname1, fname2) == 0 ); -#endif -#ifdef __GNUC__ - return bool( strcasecmp(fname1, fname2) == 0 ); -#endif -#ifdef __BORLANDC__ - return bool( stricmp(fname1, fname2) == 0 ); -#endif -#ifdef _MSC_VER - return bool( _stricmp(fname1, fname2) == 0 ); -#endif -} - -//--------------------------------------------------------------------------- +} + + +//--------------------------------------------------------------------------- +// Simplify token list +//--------------------------------------------------------------------------- + +void Tokenizer::simplifyTokenList() +{ + + // Remove the keyword 'unsigned' + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if (tok->next() && (tok->next()->str() == "unsigned")) + { + tok->deleteNext(); + } + } + + // 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 = _gettok(tok,6); tok2; tok2 = tok2->next()) + { + if (tok2->str() == sym) + { + tok2->setstr(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->setstr( 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->setstr( str.str().c_str() ); + for (int i = 0; i < 3; i++) + { + tok->deleteNext(); + } + } + } + + else if (TOKEN::Match(tok, "sizeof ( * %var% )")) + { + tok->setstr("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 = _gettok(tok,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->setstr(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::Match(tok->next(), "* 1") || TOKEN::Match(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->setstr(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->setstr(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") || TOKEN::Match(type0, "return")) + continue; + + TOKEN *tok2 = NULL; + unsigned int typelen = 0; + + if ( TOKEN::Match(type0, "%type% %var% ,|=") ) + { + if ( type0->next()->str() != "operator" ) + { + tok2 = _gettok(type0, 2); // The ',' or '=' token + typelen = 1; + } + } + + else if ( TOKEN::Match(type0, "%type% * %var% ,|=") ) + { + if ( type0->next()->next()->str() != "operator" ) + { + tok2 = _gettok(type0, 3); // The ',' token + typelen = 1; + } + } + + else if ( TOKEN::Match(type0, "%type% %var% [ %num% ] ,|=") ) + { + tok2 = _gettok(type0, 5); // The ',' token + typelen = 1; + } + + else if ( TOKEN::Match(type0, "%type% * %var% [ %num% ] ,|=") ) + { + tok2 = _gettok(type0, 6); // The ',' token + typelen = 1; + } + + else if ( TOKEN::Match(type0, "struct %type% %var% ,|=") ) + { + tok2 = _gettok(type0, 3); + typelen = 2; + } + + else if ( TOKEN::Match(type0, "struct %type% * %var% ,|=") ) + { + tok2 = _gettok(type0, 4); + typelen = 2; + } + + + if (tok2) + { + if (tok2->str() == ",") + { + tok2->setstr(";"); + 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 = _gettok(type0,typelen); + if (VarTok->aaaa0()=='*') + VarTok = VarTok->next(); + InsertTokens(eq, VarTok, 2); + eq->setstr(";"); + + // "= x, " => "= x; type " + if (tok2->str() == ",") + { + tok2->setstr(";"); + InsertTokens( tok2, type0, typelen ); + } + break; + } + + tok2 = tok2->next(); + } + } + } + } + + // Replace NULL with 0.. + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if ( TOKEN::Match(tok, "NULL") ) + tok->setstr("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::Match(tok->next(),"0")) + tok->deleteNext(); + } + } + + bool modified = true; + while ( modified ) + { + modified = false; + modified |= simplifyConditions(); + modified |= simplifyCasts(); + modified |= simplifyFunctionReturn(); + modified |= simplifyKnownVariables(); + } +} +//--------------------------------------------------------------------------- + + +bool Tokenizer::simplifyConditions() +{ + bool ret = false; + + for ( TOKEN *tok = _tokens; tok; tok = tok->next() ) + { + if (TOKEN::Match(tok, "( true &&") || TOKEN::Match(tok, "&& true &&") || TOKEN::Match(tok->next(), "&& true )")) + { + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + else if (TOKEN::Match(tok, "( false ||") || TOKEN::Match(tok, "|| false ||") || TOKEN::Match(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 ((TOKEN::Match(tok, "(") || TOKEN::Match(tok, "&&") || TOKEN::Match(tok, "||")) && + TOKEN::Match(tok->next(), "%num%") && + (TOKEN::Match(tok2, ")") || TOKEN::Match(tok2, "&&") || TOKEN::Match(tok2, "||")) ) + { + tok->next()->setstr((tok->next()->str() != "0") ? "true" : "false"); + ret = true; + } + + // Reduce "(%num% == %num%)" => "(true)"/"(false)" + if ( (TOKEN::Match(tok, "&&") || TOKEN::Match(tok, "||") || TOKEN::Match(tok, "(")) && + TOKEN::Match(tok->tokAt(1), "%num% %any% %num%") && + (TOKEN::Match(tok->tokAt(4), "&&") || TOKEN::Match(tok->tokAt(4), "||") || TOKEN::Match(tok->tokAt(4), ")")) ) + { + 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->setstr( 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::Match(tok2->next(), "(") ) + ++parlevel; + else if ( TOKEN::Match(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->setstr( 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 ) + continue; + } + + else if ( TOKEN::Match(tok2, "%var% = %num% ;") ) + { + unsigned int varid = tok2->varId(); + TOKEN *tok3 = tok2; + while ( 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% )", 0, 0, varid) ) + { + tok3 = tok3->next()->next(); + tok3->setstr( 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(); + + bool staticfunc = false; + bool classfunc = false; + + 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 (strchr("};", tok->aaaa0())) + staticfunc = classfunc = false; + + else if ( tok->str() == "static" ) + staticfunc = true; + + else if ( tok->str() == "::" ) + classfunc = true; + + else if (TOKEN::Match(tok, "%var% (")) + { + // Check if this is the first token of a function implementation.. + for ( const TOKEN *tok2 = tok; 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; + + while (_dsymlist) + { + struct DefineSymbol *next = _dsymlist->next; + free(_dsymlist->name); + free(_dsymlist->value); + delete _dsymlist; + _dsymlist = next; + } + + _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 ( TOKEN::Match(ftok, ",") ) + ++_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(); +} + +//--------------------------------------------------------------------------- + +bool Tokenizer::SameFileName( const char fname1[], const char fname2[] ) +{ +#ifdef __linux__ + return bool( strcmp(fname1, fname2) == 0 ); +#endif +#ifdef __GNUC__ + return bool( strcasecmp(fname1, fname2) == 0 ); +#endif +#ifdef __BORLANDC__ + return bool( stricmp(fname1, fname2) == 0 ); +#endif +#ifdef _MSC_VER + return bool( _stricmp(fname1, fname2) == 0 ); +#endif +} + +//--------------------------------------------------------------------------- diff --git a/tokenize.h b/tokenize.h index 4e826d88c..c54aecdb2 100644 --- a/tokenize.h +++ b/tokenize.h @@ -1,139 +1,139 @@ -/* - * c++check - c/c++ syntax checking - * Copyright (C) 2007 Daniel Marjamäki - * - * 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(); - - /** - * Helper function for "tokenize". This recursively parses into included header files. - */ - void tokenizeCode(std::istream &code, const unsigned int FileIndex=0); - -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 - - struct DefineSymbol - { - char *name; - char *value; - struct DefineSymbol *next; - }; - - void Define(const char Name[], const char Value[]); - - void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno); - - /** Simplify conditions - * @return true if something is modified - * false if nothing is done. - */ - bool simplifyConditions(); - - /** 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(); - - TOKEN *_gettok(TOKEN *tok, int index); - - void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n); - - TOKEN *_tokensBack; - std::map _typeSize; - std::vector _functionList; - std::vector _files; - struct DefineSymbol * _dsymlist; - TOKEN *_tokens; -}; - -//--------------------------------------------------------------------------- -#endif +/* + * c++check - c/c++ syntax checking + * Copyright (C) 2007 Daniel Marjamäki + * + * 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(); + + /** + * Helper function for "tokenize". This recursively parses into included header files. + */ + void tokenizeCode(std::istream &code, const unsigned int FileIndex=0); + +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 + + struct DefineSymbol + { + char *name; + char *value; + struct DefineSymbol *next; + }; + + void Define(const char Name[], const char Value[]); + + void addtoken(const char str[], const unsigned int lineno, const unsigned int fileno); + + /** Simplify conditions + * @return true if something is modified + * false if nothing is done. + */ + bool simplifyConditions(); + + /** 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(); + + TOKEN *_gettok(TOKEN *tok, int index); + + void InsertTokens(TOKEN *dest, TOKEN *src, unsigned int n); + + TOKEN *_tokensBack; + std::map _typeSize; + std::vector _functionList; + std::vector _files; + struct DefineSymbol * _dsymlist; + TOKEN *_tokens; +}; + +//--------------------------------------------------------------------------- +#endif