2008-12-18 22:28:57 +01:00
/*
2008-12-19 22:15:06 +01:00
* cppcheck - c / c + + syntax checking
2009-01-02 20:02:35 +01:00
* Copyright ( C ) 2007 - 2009 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"
2009-01-08 18:32:33 +01:00
# include "errormessage.h"
2008-12-18 22:28:57 +01:00
# include <algorithm>
# include <sstream>
# include <cstring>
# include <fstream>
# include <map>
//---------------------------------------------------------------------------
2009-01-05 16:49:57 +01:00
CppCheck : : CppCheck ( ErrorLogger & errorLogger )
2008-12-18 22:28:57 +01:00
{
_errorLogger = & errorLogger ;
}
CppCheck : : ~ CppCheck ( )
{
}
2009-01-05 16:49:57 +01:00
void CppCheck : : settings ( const Settings & settings )
2008-12-18 22:28:57 +01:00
{
_settings = settings ;
}
2009-01-05 16:49:57 +01:00
void CppCheck : : addFile ( const std : : string & path )
2008-12-18 22:28:57 +01:00
{
2009-01-05 16:49:57 +01:00
_filenames . push_back ( path ) ;
2008-12-18 22:28:57 +01:00
}
2009-01-05 16:49:57 +01:00
void CppCheck : : addFile ( const std : : string & path , const std : : string & content )
2008-12-18 22:28:57 +01:00
{
2009-01-05 16:49:57 +01:00
_filenames . push_back ( path ) ;
2008-12-18 22:28:57 +01:00
_fileContents [ path ] = content ;
}
2009-01-05 16:49:57 +01:00
std : : string CppCheck : : parseFromArgs ( int argc , const char * const argv [ ] )
2008-12-18 22:28:57 +01:00
{
std : : vector < std : : string > pathnames ;
for ( int i = 1 ; i < argc ; i + + )
{
// Flag used for various purposes during debugging
2009-01-05 16:49:57 +01:00
if ( strcmp ( argv [ i ] , " --debug " ) = = 0 )
2008-12-18 22:28:57 +01:00
_settings . _debug = true ;
// Show all messages
2009-01-05 16:49:57 +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
2009-01-05 16:49:57 +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
2009-01-05 16:49:57 +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)
2009-01-05 16:49:57 +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
2009-01-05 16:49:57 +01:00
else if ( strcmp ( argv [ i ] , " -f " ) = = 0 | | strcmp ( argv [ i ] , " --force " ) = = 0 )
2008-12-27 08:52:07 +01:00
_settings . _force = true ;
2008-12-18 22:28:57 +01:00
else
2009-01-05 16:49:57 +01:00
pathnames . push_back ( argv [ i ] ) ;
2008-12-18 22:28:57 +01:00
}
2009-01-05 16:49:57 +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 ;
2009-01-05 16:49:57 +01:00
for ( iter = pathnames . begin ( ) ; iter ! = pathnames . end ( ) ; iter + + )
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 "
2009-01-05 16:49:57 +01:00
" \n "
" C/C++ code checking \n "
" \n "
" Syntax: \n "
" cppcheck [--all] [--force] [--quiet] [--style] [--verbose] [file or path1] [file or path] \n "
" \n "
" If path is given instead of filename, *.cpp, *.cxx, *.cc and *.c files are \n "
" checked recursively from given directory. \n \n "
" Options: \n "
" -a, --all Make the checking more sensitive. More bugs are detected, \n "
" but there are also more false positives \n "
" -f, --force Force checking on files that have \" too many \" configurations \n "
" -q, --quiet Only print error messages \n "
" -s, --style Check coding style \n "
" -v, --verbose More detailed error reports \n "
" \n "
" Example usage: \n "
" # Recursively check the current folder. Print the progress on the screen and write errors in a file: \n "
" cppcheck . 2> err.txt \n "
" # Recursively check ../myproject/ and print only most fatal errors: \n "
" cppcheck --quiet ../myproject/ \n "
" # Check only files one.cpp and two.cpp and give all information there is: \n "
" cppcheck -v -a -s one.cpp two.cpp \n " ;
2008-12-18 22:28:57 +01:00
return oss . str ( ) ;
}
return " " ;
}
2009-01-08 22:30:25 +01:00
unsigned int CppCheck : : check ( )
2008-12-18 22:28:57 +01:00
{
2009-01-05 16:49:57 +01:00
_checkFunctionUsage . setErrorLogger ( this ) ;
std : : sort ( _filenames . begin ( ) , _filenames . end ( ) ) ;
2008-12-18 22:28:57 +01:00
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 = " " ;
2009-01-05 16:49:57 +01:00
if ( _fileContents . size ( ) > 0 & & _fileContents . find ( _filenames [ c ] ) ! = _fileContents . end ( ) )
2008-12-18 22:28:57 +01:00
{
// File content was given as a string
2009-01-05 16:49:57 +01:00
std : : istringstream iss ( _fileContents [ _filenames [ c ] ] ) ;
preprocessor . preprocess ( iss , filedata , configurations ) ;
2008-12-18 22:28:57 +01:00
}
else
{
// Only file name was given, read the content from file
2009-01-05 16:49:57 +01:00
std : : ifstream fin ( fname . c_str ( ) ) ;
preprocessor . preprocess ( fin , filedata , configurations ) ;
2008-12-18 22:28:57 +01:00
}
2008-12-27 08:52:07 +01:00
int checkCount = 0 ;
2009-01-05 16:49:57 +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.
2009-01-05 16:49:57 +01:00
if ( ! _settings . _force & & checkCount > 11 )
2008-12-27 08:52:07 +01:00
{
2009-01-05 16:49:57 +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-28 21:13:03 +01:00
2008-12-27 08:52:07 +01:00
break ;
}
2008-12-26 23:52:27 +01:00
cfg = * it ;
2009-01-05 16:49:57 +01:00
std : : string codeWithoutCfg = Preprocessor : : getcode ( filedata , * it ) ;
2008-12-26 23:52:27 +01:00
// If only errors are printed, print filename after the check
2009-01-05 16:49:57 +01:00
if ( _settings . _errorsOnly = = false )
_errorLogger - > reportOut ( std : : string ( " Checking " ) + fname + " : " + cfg + std : : string ( " ... " ) ) ;
2008-12-26 23:52:27 +01:00
2009-01-05 16:49:57 +01:00
checkFile ( codeWithoutCfg , _filenames [ c ] . c_str ( ) ) ;
2009-01-01 23:22:28 +01:00
+ + checkCount ;
2008-12-18 22:28:57 +01:00
}
2009-01-05 16:49:57 +01:00
if ( _settings . _errorsOnly = = false & & _errout . str ( ) . empty ( ) )
_errorLogger - > reportOut ( " No errors found " ) ;
2008-12-18 22:28:57 +01:00
}
// This generates false positives - especially for libraries
_settings . _verbose = false ;
2009-01-11 07:44:32 +01:00
if ( ErrorMessage : : unusedFunction ( _settings ) )
2008-12-18 22:28:57 +01:00
{
_errout . str ( " " ) ;
2009-01-05 16:49:57 +01:00
if ( _settings . _errorsOnly = = false )
_errorLogger - > reportOut ( " Checking usage of global functions (this may take several minutes).. " ) ;
2008-12-18 22:28:57 +01:00
_checkFunctionUsage . check ( ) ;
}
2009-01-08 22:30:25 +01:00
unsigned int result = _errorList . size ( ) ;
2008-12-18 22:28:57 +01:00
_errorList . clear ( ) ;
2009-01-08 22:30:25 +01:00
return result ;
2008-12-18 22:28:57 +01:00
}
//---------------------------------------------------------------------------
// CppCheck - A function that checks a specified file
//---------------------------------------------------------------------------
void CppCheck : : checkFile ( const std : : string & code , const char FileName [ ] )
{
Tokenizer _tokenizer ;
// Tokenize the file
{
2009-01-05 16:49:57 +01:00
std : : istringstream istr ( code ) ;
_tokenizer . tokenize ( istr , FileName ) ;
2008-12-18 22:28:57 +01:00
}
// 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.
2009-01-05 16:49:57 +01:00
CheckClass checkClass ( & _tokenizer , _settings , this ) ;
2009-01-10 18:51:03 +01:00
if ( ErrorMessage : : memsetClass ( _settings ) )
checkClass . noMemset ( ) ;
2008-12-18 22:28:57 +01:00
2009-01-04 19:28:05 +01:00
// Coding style checks that must be run before the simplifyTokenList
2009-01-12 18:12:14 +01:00
CheckOther checkOther ( & _tokenizer , _settings , this ) ;
// Check for unsigned divisions where one operand is signed
if ( ErrorMessage : : udivWarning ( _settings ) | | ErrorMessage : : udivError ( _settings ) )
2008-12-18 22:28:57 +01:00
checkOther . CheckUnsignedDivision ( ) ;
2009-01-12 18:12:14 +01:00
if ( _settings . _checkCodingStyle )
{
2009-01-04 19:28:05 +01:00
// Give warning when using char variable as array index
2008-12-18 22:28:57 +01:00
checkOther . CheckCharVariable ( ) ;
2009-01-04 19:28:05 +01:00
// Usage of local variables
checkOther . functionVariableUsage ( ) ;
}
2008-12-18 22:28:57 +01:00
2009-01-04 19:28:05 +01:00
2008-12-18 22:28:57 +01:00
_tokenizer . simplifyTokenList ( ) ;
2009-01-11 07:44:32 +01:00
if ( ErrorMessage : : unusedFunction ( _settings ) )
2008-12-18 22:28:57 +01:00
_checkFunctionUsage . parseTokens ( _tokenizer ) ;
2009-01-03 16:22:53 +01:00
// Class for detecting buffer overruns and related problems
2009-01-05 16:49:57 +01:00
CheckBufferOverrunClass checkBufferOverrun ( & _tokenizer , _settings , this ) ;
2009-01-03 16:22:53 +01:00
2008-12-18 22:28:57 +01:00
// Memory leak
2009-01-05 16:49:57 +01:00
CheckMemoryLeakClass checkMemoryLeak ( & _tokenizer , _settings , this ) ;
2009-01-10 20:30:41 +01:00
if ( ErrorMessage : : memleak ( _settings ) & & ErrorMessage : : mismatchAllocDealloc ( _settings ) )
2009-01-08 18:32:33 +01:00
checkMemoryLeak . CheckMemoryLeak ( ) ;
2008-12-18 22:28:57 +01:00
// Check that all class constructors are ok.
2009-01-10 20:24:23 +01:00
if ( ErrorMessage : : noConstructor ( _settings ) & & ErrorMessage : : uninitVar ( _settings ) )
2009-01-10 14:08:44 +01:00
checkClass . constructors ( ) ;
2008-12-18 22:28:57 +01:00
// Check that all base classes have virtual destructors
2009-01-10 19:03:56 +01:00
if ( ErrorMessage : : virtualDestructor ( _settings ) )
checkClass . virtualDestructor ( ) ;
2008-12-18 22:28:57 +01:00
2009-01-10 20:23:21 +01:00
// Array index out of bounds / Buffer overruns..
if ( ErrorMessage : : arrayIndexOutOfBounds ( _settings ) & & ErrorMessage : : bufferOverrun ( _settings ) )
checkBufferOverrun . bufferOverrun ( ) ;
2008-12-18 22:28:57 +01:00
if ( _settings . _showAll )
{
2009-01-03 16:22:53 +01:00
2008-12-18 22:28:57 +01:00
// Check for "if (a=b)"
checkOther . CheckIfAssignment ( ) ;
}
// 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
2009-01-08 21:56:51 +01:00
// Warning upon c-style pointer casts
2009-01-08 22:08:14 +01:00
if ( ErrorMessage : : cstyleCast ( _settings ) )
2008-12-18 22:28:57 +01:00
{
const char * ext = strrchr ( FileName , ' . ' ) ;
2009-01-05 16:49:57 +01:00
if ( ext & & strcmp ( ext , " .cpp " ) = = 0 )
2008-12-18 22:28:57 +01:00
checkOther . WarningOldStylePointerCast ( ) ;
2009-01-08 21:56:51 +01:00
}
2008-12-18 22:28:57 +01:00
2009-01-08 21:56:51 +01:00
// if (a) delete a;
2009-01-08 22:08:14 +01:00
if ( ErrorMessage : : redundantIfDelete0 ( _settings ) )
2008-12-18 22:28:57 +01:00
checkOther . WarningRedundantCode ( ) ;
2009-01-09 20:15:54 +01:00
// strtol and strtoul usage
2009-01-12 07:52:50 +01:00
if ( ErrorMessage : : dangerousUsageStrtol ( _settings ) | |
ErrorMessage : : sprintfOverlappingData ( _settings ) )
2009-01-09 20:15:54 +01:00
checkOther . InvalidFunctionUsage ( ) ;
2009-01-08 21:56:51 +01:00
2009-01-10 18:35:41 +01:00
// Check that all private functions are called.
if ( ErrorMessage : : unusedPrivateFunction ( _settings ) )
2009-01-08 21:56:51 +01:00
checkClass . privateFunctions ( ) ;
2009-01-10 18:57:31 +01:00
// 'operator=' should return something..
if ( ErrorMessage : : operatorEq ( _settings ) )
checkClass . operatorEq ( ) ;
2009-01-10 18:35:41 +01:00
2009-01-12 07:44:47 +01:00
// if (condition);
if ( ErrorMessage : : ifNoAction ( _settings ) )
2008-12-18 22:28:57 +01:00
checkOther . WarningIf ( ) ;
2009-01-12 18:21:25 +01:00
// Unused struct members..
if ( ErrorMessage : : unusedStructMember ( _settings ) )
checkOther . CheckStructMemberUsage ( ) ;
2009-01-12 18:28:04 +01:00
// Unreachable code below a 'return' statement
if ( ErrorMessage : : unreachableCode ( _settings ) )
checkOther . unreachableCode ( ) ;
2009-01-12 07:44:47 +01:00
if ( _settings . _checkCodingStyle )
{
2008-12-18 22:28:57 +01:00
// Variable scope (check if the scope could be limited)
//CheckVariableScope();
// Check if a constant function parameter is passed by value
checkOther . CheckConstantFunctionParameter ( ) ;
// Check for various types of incomplete statements that could for example
// mean that an ';' has been added by accident
checkOther . CheckIncompleteStatement ( ) ;
}
}
//---------------------------------------------------------------------------
2009-01-05 16:49:57 +01:00
void CppCheck : : reportErr ( const std : : string & errmsg )
2008-12-18 22:28:57 +01:00
{
2009-01-05 16:49:57 +01:00
if ( /*OnlyReportUniqueErrors*/ true )
2008-12-18 22:28:57 +01:00
{
2009-01-05 16:49:57 +01:00
if ( std : : find ( _errorList . begin ( ) , _errorList . end ( ) , errmsg ) ! = _errorList . end ( ) )
2008-12-18 22:28:57 +01:00
return ;
2009-01-05 16:49:57 +01:00
_errorList . push_back ( errmsg ) ;
2008-12-18 22:28:57 +01:00
}
2009-01-05 16:49:57 +01:00
std : : string errmsg2 ( errmsg ) ;
if ( _settings . _verbose )
2008-12-18 22:28:57 +01:00
{
errmsg2 + = " \n Defines= \' " + cfg + " \' \n " ;
}
2009-01-05 16:49:57 +01:00
_errorLogger - > reportErr ( errmsg2 ) ;
2008-12-18 22:28:57 +01:00
_errout < < errmsg2 < < std : : endl ;
}
2009-01-05 16:49:57 +01:00
void CppCheck : : reportOut ( const std : : string & outmsg )
2008-12-18 22:28:57 +01:00
{
// This is currently never called. It is here just to comply with
// the interface.
}