2008-12-18 22:28:57 +01:00
/*
2008-12-19 22:15:06 +01:00
* cppcheck - c / c + + syntax checking
* Copyright ( C ) 2007 - 2008 Daniel Marjamäki , Reijo Tomperi , Nicolas Le Cam
2008-12-18 22:28:57 +01:00
*
* 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 < http : //www.gnu.org/licenses/
*/
# include "cppcheck.h"
# include "preprocessor.h" // preprocessor.
# include "tokenize.h" // <- Tokenizer
# include "checkmemoryleak.h"
# include "checkbufferoverrun.h"
# include "checkclass.h"
# include "checkheaders.h"
# include "checkother.h"
# include "checkfunctionusage.h"
# include "filelister.h"
# include <algorithm>
# include <sstream>
# include <cstring>
# include <fstream>
# include <map>
//---------------------------------------------------------------------------
2008-12-19 20:31:12 +01:00
CppCheck : : CppCheck ( ErrorLogger & errorLogger )
2008-12-18 22:28:57 +01:00
{
_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 < std : : string > pathnames ;
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
2008-12-19 19:47:31 +01:00
else if ( strcmp ( argv [ i ] , " -a " ) = = 0 | | strcmp ( argv [ i ] , " --all " ) = = 0 )
2008-12-18 22:28:57 +01:00
_settings . _showAll = true ;
// Only print something when there are errors
2008-12-19 19:47:31 +01:00
else if ( strcmp ( argv [ i ] , " -q " ) = = 0 | | strcmp ( argv [ i ] , " --quiet " ) = = 0 )
2008-12-18 22:28:57 +01:00
_settings . _errorsOnly = true ;
// Checking coding style
2008-12-19 19:47:31 +01:00
else if ( strcmp ( argv [ i ] , " -s " ) = = 0 | | strcmp ( argv [ i ] , " --style " ) = = 0 )
2008-12-18 22:28:57 +01:00
_settings . _checkCodingStyle = true ;
// Verbose error messages (configuration info)
2008-12-19 19:47:31 +01:00
else if ( strcmp ( argv [ i ] , " -v " ) = = 0 | | strcmp ( argv [ i ] , " --verbose " ) = = 0 )
2008-12-18 22:28:57 +01:00
_settings . _verbose = true ;
2008-12-27 08:52:07 +01:00
// Force checking of files that have "too many" configurations
else if ( strcmp ( argv [ i ] , " -f " ) = = 0 | | strcmp ( argv [ i ] , " --force " ) = = 0 )
_settings . _force = true ;
2008-12-18 22:28:57 +01:00
else
pathnames . push_back ( argv [ i ] ) ;
}
2008-12-19 19:18:29 +01:00
if ( pathnames . size ( ) > 0 )
2008-12-18 22:28:57 +01:00
{
2008-12-19 19:18:29 +01:00
// Execute RecursiveAddFiles() to each given file parameter
2008-12-18 22:28:57 +01:00
std : : vector < std : : string > : : const_iterator iter ;
for ( iter = pathnames . begin ( ) ; iter ! = pathnames . end ( ) ; iter + + )
2008-12-19 19:18:29 +01:00
FileLister : : RecursiveAddFiles ( _filenames , iter - > c_str ( ) , true ) ;
2008-12-18 22:28:57 +01:00
}
if ( _filenames . empty ( ) )
{
std : : ostringstream oss ;
2008-12-30 19:45:17 +01:00
oss < < " cppcheck 1.27 \n "
2008-12-18 22:28:57 +01:00
" \n "
" C/C++ code checking \n "
" \n "
" Syntax: \n "
2008-12-30 19:48:36 +01:00
" cppcheck [--all] [--force] [--quiet] [--style] [--verbose] [file or path1] [file or path] \n "
2008-12-18 22:28:57 +01:00
" \n "
2008-12-19 19:18:29 +01:00
" If path is given instead of filename, *.cpp, *.cxx, *.cc and *.c files are \n "
" checked recursively from given directory. \n \n "
2008-12-18 22:28:57 +01:00
" Options: \n "
2008-12-19 19:47:31 +01:00
" -a, --all Make the checking more sensitive. More bugs are detected, \n "
" but there are also more false positives \n "
2008-12-30 19:48:36 +01:00
" -f, --force Force checking on files that have \" too many \" configurations \n "
2008-12-19 19:47:31 +01:00
" -q, --quiet Only print error messages \n "
" -s, --style Check coding style \n "
" -v, --verbose More detailed error reports \n "
2008-12-19 19:18:29 +01:00
" \n "
" Example usage: \n "
2008-12-30 19:48:36 +01:00
" # Recursively check the current folder. Print the progress on the screen and write errors in a file: \n "
" cppcheck . 2> err.txt \n "
2008-12-19 19:18:29 +01:00
" # Recursively check ../myproject/ and print only most fatal errors: \n "
2008-12-19 19:38:22 +01:00
" cppcheck --quiet ../myproject/ \n "
2008-12-19 19:18:29 +01:00
" # Check only files one.cpp and two.cpp and give all information there is: \n "
2008-12-19 19:47:31 +01:00
" cppcheck -v -a -s one.cpp two.cpp \n " ;
2008-12-18 22:28:57 +01:00
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 ( )
{
2008-12-19 20:31:12 +01:00
_checkFunctionUsage . setErrorLogger ( this ) ;
2008-12-18 22:28:57 +01:00
std : : sort ( _filenames . begin ( ) , _filenames . end ( ) ) ;
for ( unsigned int c = 0 ; c < _filenames . size ( ) ; c + + )
{
_errout . str ( " " ) ;
std : : string fname = _filenames [ c ] ;
Preprocessor preprocessor ;
2008-12-26 23:52:27 +01:00
std : : list < std : : string > configurations ;
std : : string filedata = " " ;
2008-12-18 22:28:57 +01:00
if ( _fileContents . size ( ) > 0 & & _fileContents . find ( _filenames [ c ] ) ! = _fileContents . end ( ) )
{
// File content was given as a string
std : : istringstream iss ( _fileContents [ _filenames [ c ] ] ) ;
2009-01-02 01:13:24 +01:00
preprocessor . preprocess ( iss , filedata , configurations ) ;
2008-12-18 22:28:57 +01:00
}
else
{
// Only file name was given, read the content from file
std : : ifstream fin ( fname . c_str ( ) ) ;
2009-01-02 01:13:24 +01:00
preprocessor . preprocess ( fin , filedata , configurations ) ;
2008-12-18 22:28:57 +01:00
}
2008-12-27 08:52:07 +01:00
int checkCount = 0 ;
2008-12-26 23:52:27 +01:00
for ( std : : list < std : : string > : : const_iterator it = configurations . begin ( ) ; it ! = configurations . end ( ) ; + + it )
2008-12-18 22:28:57 +01:00
{
2008-12-27 08:52:07 +01:00
// Check only 12 first configurations, after that bail out, unless --force
// was used.
if ( ! _settings . _force & & checkCount > 11 )
{
2008-12-28 21:13:03 +01:00
if ( _settings . _errorsOnly = = false )
_errorLogger - > reportOut ( std : : string ( " Bailing out from checking " ) + fname + " : Too many configurations. Recheck this file with --force if you want to check them all. " ) ;
2008-12-27 08:52:07 +01:00
break ;
}
2008-12-26 23:52:27 +01:00
cfg = * it ;
std : : string codeWithoutCfg = Preprocessor : : getcode ( filedata , * it ) ;
// If only errors are printed, print filename after the check
if ( _settings . _errorsOnly = = false )
_errorLogger - > reportOut ( std : : string ( " Checking " ) + fname + " : " + cfg + std : : string ( " ... " ) ) ;
checkFile ( codeWithoutCfg , _filenames [ c ] . c_str ( ) ) ;
2009-01-01 23:22:28 +01:00
+ + checkCount ;
2008-12-18 22:28:57 +01:00
}
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 ) ;
2008-12-20 09:48:52 +01:00
checkClass . noMemset ( ) ;
2008-12-18 22:28:57 +01:00
// 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 ) ;
2008-12-20 09:53:42 +01:00
checkBufferOverrun . bufferOverrun ( ) ;
2008-12-18 22:28:57 +01:00
// Check that all class constructors are ok.
2008-12-20 09:48:52 +01:00
checkClass . constructors ( ) ;
2008-12-18 22:28:57 +01:00
// 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'
2008-12-20 09:53:42 +01:00
checkBufferOverrun . dangerousFunctions ( ) ;
2008-12-18 22:28:57 +01:00
// Invalid function usage..
checkOther . InvalidFunctionUsage ( ) ;
if ( _settings . _checkCodingStyle )
{
// Check that all private functions are called.
2008-12-20 09:48:52 +01:00
checkClass . privateFunctions ( ) ;
2008-12-18 22:28:57 +01:00
// Warning upon c-style pointer casts
const char * ext = strrchr ( FileName , ' . ' ) ;
if ( ext & & strcmp ( ext , " .cpp " ) = = 0 )
checkOther . WarningOldStylePointerCast ( ) ;
2008-12-20 09:48:52 +01:00
checkClass . operatorEq ( ) ;
2008-12-18 22:28:57 +01:00
// 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.
}