2008-12-18 22:28:57 +01:00
/*
2009-01-21 21:04:20 +01:00
* Cppcheck - A tool for static C / C + + code analysis
2016-01-01 14:34:45 +01:00
* Copyright ( C ) 2007 - 2016 Cppcheck team .
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
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2008-12-18 22:28:57 +01:00
*/
//---------------------------------------------------------------------------
2009-06-07 22:12:20 +02:00
# include "checkunusedfunctions.h"
2008-12-18 22:28:57 +01:00
# include "tokenize.h"
2009-07-13 19:11:31 +02:00
# include "token.h"
2014-03-26 08:54:56 +01:00
# include "symboldatabase.h"
2011-10-30 09:53:01 +01:00
# include <cctype>
2016-10-31 20:43:57 +01:00
# include <tinyxml2.h>
2008-12-18 22:28:57 +01:00
//---------------------------------------------------------------------------
2014-11-24 06:37:08 +01:00
2014-04-13 11:51:29 +02:00
// Register this check class
2014-11-24 06:37:08 +01:00
CheckUnusedFunctions CheckUnusedFunctions : : instance ;
2016-07-16 21:21:24 +02:00
static const struct CWE CWE561 ( 561U ) ; // Dead Code
2016-07-15 15:49:21 +02:00
2008-12-18 22:28:57 +01:00
//---------------------------------------------------------------------------
// FUNCTION USAGE - Check for unused functions etc
//---------------------------------------------------------------------------
2014-11-24 06:37:08 +01:00
void CheckUnusedFunctions : : parseTokens ( const Tokenizer & tokenizer , const char FileName [ ] , const Settings * settings )
2008-12-18 22:28:57 +01:00
{
2015-12-22 12:03:45 +01:00
const bool doMarkup = settings - > library . markupFile ( FileName ) ;
2014-11-24 06:37:08 +01:00
const SymbolDatabase * symbolDatabase = tokenizer . getSymbolDatabase ( ) ;
2009-06-14 07:58:36 +02:00
2014-03-26 08:54:56 +01:00
// Function declarations..
2016-10-31 20:43:57 +01:00
_functionDecl . clear ( ) ;
2014-03-26 08:54:56 +01:00
for ( std : : size_t i = 0 ; i < symbolDatabase - > functionScopes . size ( ) ; i + + ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
const Function * func = scope - > function ;
if ( ! func | | ! func - > token | | scope - > classStart - > fileIndex ( ) ! = 0 )
2012-04-09 11:00:31 +02:00
continue ;
2014-03-14 18:17:21 +01:00
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
2014-03-26 08:54:56 +01:00
if ( func - > isAttributeConstructor ( ) | | func - > isAttributeDestructor ( ) | | func - > type ! = Function : : eFunction )
2013-08-30 06:27:46 +02:00
continue ;
2014-03-26 08:54:56 +01:00
// Don't care about templates
2015-06-28 17:54:48 +02:00
if ( tokenizer . isCPP ( ) & & func - > retDef - > str ( ) = = " template " )
2014-03-26 08:54:56 +01:00
continue ;
2008-12-18 22:28:57 +01:00
2016-10-31 20:43:57 +01:00
_functionDecl . push_back ( FunctionDecl ( func ) ) ;
2014-11-24 06:37:08 +01:00
FunctionUsage & usage = _functions [ func - > name ( ) ] ;
2008-12-18 22:28:57 +01:00
2014-03-26 08:54:56 +01:00
if ( ! usage . lineNumber )
usage . lineNumber = func - > token - > linenr ( ) ;
2011-09-18 07:13:39 +02:00
2014-03-26 08:54:56 +01:00
// No filename set yet..
if ( usage . filename . empty ( ) ) {
2014-11-24 06:37:08 +01:00
usage . filename = tokenizer . list . getSourceFilePath ( ) ;
2014-03-26 08:54:56 +01:00
}
// Multiple files => filename = "+"
2014-11-24 06:37:08 +01:00
else if ( usage . filename ! = tokenizer . list . getSourceFilePath ( ) ) {
2014-03-26 08:54:56 +01:00
//func.filename = "+";
usage . usedOtherFile | = usage . usedSameFile ;
2008-12-18 22:28:57 +01:00
}
}
// Function usage..
2016-10-31 20:43:57 +01:00
_functionCalls . clear ( ) ;
2014-11-24 06:37:08 +01:00
for ( const Token * tok = tokenizer . tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2013-10-20 14:09:10 +02:00
// parsing of library code to find called functions
if ( settings - > library . isexecutableblock ( FileName , tok - > str ( ) ) ) {
2014-01-02 18:18:24 +01:00
const Token * markupVarToken = tok - > tokAt ( settings - > library . blockstartoffset ( FileName ) ) ;
2014-03-11 15:57:28 +01:00
int scope = 0 ;
bool start = true ;
2013-10-20 14:09:10 +02:00
// find all function calls in library code (starts with '(', not if or while etc)
2014-03-11 15:57:28 +01:00
while ( scope | | start ) {
2014-01-02 18:18:24 +01:00
if ( markupVarToken - > str ( ) = = settings - > library . blockstart ( FileName ) ) {
2013-10-20 14:09:10 +02:00
scope + + ;
2014-03-11 15:57:28 +01:00
if ( start ) {
start = false ;
}
2014-01-02 18:18:24 +01:00
} else if ( markupVarToken - > str ( ) = = settings - > library . blockend ( FileName ) )
2013-10-20 14:09:10 +02:00
scope - - ;
2014-01-02 18:18:24 +01:00
else if ( ! settings - > library . iskeyword ( FileName , markupVarToken - > str ( ) ) ) {
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( markupVarToken - > str ( ) ) ;
2014-11-24 06:37:08 +01:00
if ( _functions . find ( markupVarToken - > str ( ) ) ! = _functions . end ( ) )
_functions [ markupVarToken - > str ( ) ] . usedOtherFile = true ;
2014-01-02 21:47:35 +01:00
else if ( markupVarToken - > next ( ) - > str ( ) = = " ( " ) {
2014-11-24 06:37:08 +01:00
FunctionUsage & func = _functions [ markupVarToken - > str ( ) ] ;
func . filename = tokenizer . list . getSourceFilePath ( ) ;
2014-01-02 18:18:24 +01:00
if ( func . filename . empty ( ) | | func . filename = = " + " )
func . usedOtherFile = true ;
else
func . usedSameFile = true ;
}
2013-10-20 14:09:10 +02:00
}
2014-01-02 18:18:24 +01:00
markupVarToken = markupVarToken - > next ( ) ;
2013-10-20 14:09:10 +02:00
}
}
2015-12-22 12:03:45 +01:00
if ( ! doMarkup // only check source files
2013-10-20 14:09:10 +02:00
& & settings - > library . isexporter ( tok - > str ( ) ) & & tok - > next ( ) ! = 0 ) {
2014-09-17 13:52:03 +02:00
const Token * propToken = tok - > next ( ) ;
while ( propToken & & propToken - > str ( ) ! = " ) " ) {
if ( settings - > library . isexportedprefix ( tok - > str ( ) , propToken - > str ( ) ) ) {
const Token * nextPropToken = propToken - > next ( ) ;
const std : : string & value = nextPropToken - > str ( ) ;
2014-11-24 06:37:08 +01:00
if ( _functions . find ( value ) ! = _functions . end ( ) ) {
_functions [ value ] . usedOtherFile = true ;
2013-10-20 14:09:10 +02:00
}
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( value ) ;
2013-10-20 14:09:10 +02:00
}
2014-09-17 13:52:03 +02:00
if ( settings - > library . isexportedsuffix ( tok - > str ( ) , propToken - > str ( ) ) ) {
const Token * prevPropToken = propToken - > previous ( ) ;
const std : : string & value = prevPropToken - > str ( ) ;
2014-11-24 06:37:08 +01:00
if ( value ! = " ) " & & _functions . find ( value ) ! = _functions . end ( ) ) {
_functions [ value ] . usedOtherFile = true ;
2013-10-20 14:09:10 +02:00
}
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( value ) ;
2013-10-20 14:09:10 +02:00
}
2014-09-17 13:52:03 +02:00
propToken = propToken - > next ( ) ;
2013-10-20 14:09:10 +02:00
}
}
2015-12-22 12:03:45 +01:00
if ( doMarkup & & settings - > library . isimporter ( FileName , tok - > str ( ) ) & & tok - > next ( ) ) {
2014-09-17 13:52:03 +02:00
const Token * propToken = tok - > next ( ) ;
if ( propToken - > next ( ) ) {
propToken = propToken - > next ( ) ;
while ( propToken & & propToken - > str ( ) ! = " ) " ) {
const std : : string & value = propToken - > str ( ) ;
2013-10-20 14:09:10 +02:00
if ( ! value . empty ( ) ) {
2014-11-24 06:37:08 +01:00
_functions [ value ] . usedOtherFile = true ;
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( value ) ;
2013-10-20 14:09:10 +02:00
break ;
}
2014-09-17 13:52:03 +02:00
propToken = propToken - > next ( ) ;
2013-10-20 14:09:10 +02:00
}
}
}
2014-03-11 15:57:28 +01:00
if ( settings - > library . isreflection ( tok - > str ( ) ) ) {
2014-09-17 13:52:03 +02:00
const int argIndex = settings - > library . reflectionArgument ( tok - > str ( ) ) ;
if ( argIndex > = 0 ) {
2014-03-13 17:43:25 +01:00
const Token * funcToken = tok - > next ( ) ;
2014-09-17 13:52:03 +02:00
int index = 0 ;
2014-03-13 17:43:25 +01:00
std : : string value ;
2014-03-14 15:51:15 +01:00
while ( funcToken ) {
2014-03-13 17:43:25 +01:00
if ( funcToken - > str ( ) = = " , " ) {
2014-09-17 13:52:03 +02:00
if ( + + index = = argIndex )
2014-03-13 17:43:25 +01:00
break ;
value = " " ;
2014-03-13 17:47:00 +01:00
} else
2014-03-13 17:43:25 +01:00
value + = funcToken - > str ( ) ;
funcToken = funcToken - > next ( ) ;
}
2014-09-17 13:52:03 +02:00
if ( index = = argIndex ) {
2013-10-20 14:09:10 +02:00
value = value . substr ( 1 , value . length ( ) - 2 ) ;
2014-11-24 06:37:08 +01:00
_functions [ value ] . usedOtherFile = true ;
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( value ) ;
2013-10-20 14:09:10 +02:00
}
}
}
2014-02-16 11:47:52 +01:00
const Token * funcname = nullptr ;
2009-01-03 21:29:20 +01:00
2015-01-31 10:50:39 +01:00
if ( tok - > scope ( ) - > isExecutable ( ) & & Token : : Match ( tok , " %name% ( " ) ) {
2014-12-09 22:04:51 +01:00
funcname = tok ;
2015-01-31 10:50:39 +01:00
} else if ( tok - > scope ( ) - > isExecutable ( ) & & Token : : Match ( tok , " %name% < " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " > ( " ) ) {
2014-12-09 22:04:51 +01:00
funcname = tok ;
} else if ( Token : : Match ( tok , " [;{}.,()[=+-/|!?:] " ) ) {
2009-02-27 17:36:37 +01:00
funcname = tok - > next ( ) ;
2014-12-09 22:04:51 +01:00
if ( funcname & & funcname - > str ( ) = = " & " )
2014-03-26 09:12:41 +01:00
funcname = funcname - > next ( ) ;
2014-12-09 22:04:51 +01:00
if ( funcname & & funcname - > str ( ) = = " :: " )
2012-06-14 22:04:21 +02:00
funcname = funcname - > next ( ) ;
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( funcname , " %name% :: %name% " ) )
2012-06-14 22:04:21 +02:00
funcname = funcname - > tokAt ( 2 ) ;
2014-12-09 22:04:51 +01:00
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( funcname , " %name% [(),;]:}] " ) )
2012-06-14 22:04:21 +02:00
continue ;
}
2009-05-31 08:01:16 +02:00
2014-12-09 22:04:51 +01:00
if ( ! funcname )
2009-06-05 15:02:26 +02:00
continue ;
2013-01-16 15:37:07 +01:00
// funcname ( => Assert that the end parentheses isn't followed by {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( funcname , " %name% (|< " ) ) {
2013-08-23 06:47:52 +02:00
const Token * ftok = funcname - > next ( ) ;
if ( ftok - > str ( ) = = " < " )
ftok = ftok - > link ( ) ;
if ( Token : : Match ( ftok - > linkAt ( 1 ) , " ) const|throw|{ " ) )
2014-02-16 10:32:10 +01:00
funcname = nullptr ;
2008-12-18 22:28:57 +01:00
}
2011-10-13 20:53:06 +02:00
if ( funcname ) {
2014-11-24 06:37:08 +01:00
FunctionUsage & func = _functions [ funcname - > str ( ) ] ;
2015-01-28 22:29:07 +01:00
const std : : string & called_from_file = tokenizer . list . getSourceFilePath ( ) ;
2008-12-18 22:28:57 +01:00
2015-01-07 08:32:49 +01:00
if ( func . filename . empty ( ) | | func . filename = = " + " | | func . filename ! = called_from_file )
2008-12-18 22:28:57 +01:00
func . usedOtherFile = true ;
else
func . usedSameFile = true ;
2016-10-31 20:43:57 +01:00
_functionCalls . insert ( funcname - > str ( ) ) ;
2008-12-18 22:28:57 +01:00
}
}
2014-11-15 18:44:23 +01:00
}
2008-12-18 22:28:57 +01:00
2014-11-15 18:44:23 +01:00
2015-06-28 18:34:09 +02:00
void CheckUnusedFunctions : : check ( ErrorLogger * const errorLogger , const Settings & settings )
2014-11-24 06:37:08 +01:00
{
2011-10-13 20:53:06 +02:00
for ( std : : map < std : : string , FunctionUsage > : : const_iterator it = _functions . begin ( ) ; it ! = _functions . end ( ) ; + + it ) {
2008-12-18 22:28:57 +01:00
const FunctionUsage & func = it - > second ;
2010-04-02 07:30:58 +02:00
if ( func . usedOtherFile | | func . filename . empty ( ) )
2008-12-18 22:28:57 +01:00
continue ;
2011-10-29 20:04:43 +02:00
if ( it - > first = = " main " | |
2015-06-28 18:34:09 +02:00
( settings . isWindowsPlatform ( ) & & ( it - > first = = " WinMain " | | it - > first = = " _tmain " ) ) | |
2011-10-29 20:04:43 +02:00
it - > first = = " if " | |
( it - > first . compare ( 0 , 8 , " operator " ) = = 0 & & it - > first . size ( ) > 8 & & ! std : : isalnum ( it - > first [ 8 ] ) ) )
2009-01-16 17:20:35 +01:00
continue ;
2011-10-13 20:53:06 +02:00
if ( ! func . usedSameFile ) {
2009-01-11 07:44:32 +01:00
std : : string filename ;
2010-04-02 07:30:58 +02:00
if ( func . filename = = " + " )
2009-01-11 07:44:32 +01:00
filename = " " ;
else
filename = func . filename ;
2014-11-24 06:37:08 +01:00
unusedFunctionError ( errorLogger , filename , func . lineNumber , it - > first ) ;
2011-10-13 20:53:06 +02:00
} else if ( ! func . usedOtherFile ) {
2009-07-14 08:17:12 +02:00
/** @todo add error message "function is only used in <file> it can be static" */
/*
2008-12-18 22:28:57 +01:00
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 ( ) ) ;
*/
}
}
}
2011-09-18 07:13:39 +02:00
void CheckUnusedFunctions : : unusedFunctionError ( ErrorLogger * const errorLogger ,
2011-09-18 18:58:28 +02:00
const std : : string & filename , unsigned int lineNumber ,
const std : : string & funcname )
2010-04-04 22:55:28 +02:00
{
2010-07-08 13:01:53 +02:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2011-10-13 20:53:06 +02:00
if ( ! filename . empty ( ) ) {
2010-07-08 13:01:53 +02:00
ErrorLogger : : ErrorMessage : : FileLocation fileLoc ;
2010-07-17 00:27:40 +02:00
fileLoc . setfile ( filename ) ;
2011-09-18 07:13:39 +02:00
fileLoc . line = lineNumber ;
2010-07-08 13:01:53 +02:00
locationList . push_back ( fileLoc ) ;
}
2016-07-26 16:27:55 +02:00
const ErrorLogger : : ErrorMessage errmsg ( locationList , emptyString , Severity : : style , " The function ' " + funcname + " ' is never used. " , " unusedFunction " , CWE561 , false ) ;
2010-07-08 13:01:53 +02:00
if ( errorLogger )
errorLogger - > reportErr ( errmsg ) ;
else
reportError ( errmsg ) ;
2010-04-04 22:55:28 +02:00
}
2014-12-02 06:41:18 +01:00
Check : : FileInfo * CheckUnusedFunctions : : getFileInfo ( const Tokenizer * tokenizer , const Settings * settings ) const
{
2016-10-31 20:43:57 +01:00
if ( settings - > isEnabled ( " unusedFunction " ) & & ( settings - > jobs = = 1 | | ! settings - > buildDir . empty ( ) ) )
2014-12-02 06:41:18 +01:00
instance . parseTokens ( * tokenizer , tokenizer - > list . getFiles ( ) . front ( ) . c_str ( ) , settings ) ;
return nullptr ;
}
2015-06-28 18:07:31 +02:00
void CheckUnusedFunctions : : analyseWholeProgram ( const std : : list < Check : : FileInfo * > & fileInfo , const Settings & settings , ErrorLogger & errorLogger )
2014-12-02 06:41:18 +01:00
{
( void ) fileInfo ;
2015-06-28 18:34:09 +02:00
check ( & errorLogger , settings ) ;
2014-12-02 06:41:18 +01:00
}
2016-10-29 12:18:11 +02:00
2016-10-31 20:43:57 +01:00
CheckUnusedFunctions : : FunctionDecl : : FunctionDecl ( const Function * f )
: functionName ( f - > name ( ) ) , lineNumber ( f - > token - > linenr ( ) )
{
}
2016-10-29 12:18:11 +02:00
std : : string CheckUnusedFunctions : : analyzerInfo ( const std : : string & filename ) const
{
std : : ostringstream ret ;
2016-10-31 20:43:57 +01:00
for ( std : : list < FunctionDecl > : : const_iterator it = instance . _functionDecl . begin ( ) ; it ! = instance . _functionDecl . end ( ) ; + + it ) {
ret < < " <functiondecl "
< < " functionName= \" " < < ErrorLogger : : toxml ( it - > functionName ) < < ' \" '
< < " lineNumber= \" " < < it - > lineNumber < < " \" /> \n " ;
}
for ( std : : set < std : : string > : : const_iterator it = instance . _functionCalls . begin ( ) ; it ! = instance . _functionCalls . end ( ) ; + + it ) {
ret < < " <functioncall functionName= \" " < < ErrorLogger : : toxml ( * it ) < < " \" /> \n " ;
2016-10-29 12:18:11 +02:00
}
return ret . str ( ) ;
}
2016-10-31 20:43:57 +01:00
void CheckUnusedFunctions : : clear ( )
{
instance . _functions . clear ( ) ;
}
void CheckUnusedFunctions : : loadInfo ( const tinyxml2 : : XMLElement * info , const std : : string & filename )
{
for ( const tinyxml2 : : XMLElement * e = info - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
if ( std : : strcmp ( e - > Name ( ) , " functiondecl " ) = = 0 ) {
FunctionUsage & func = _functions [ e - > Attribute ( " functionName " ) ] ;
func . filename = filename ;
func . lineNumber = std : : atoi ( e - > Attribute ( " lineNumber " ) ) ;
} else if ( std : : strcmp ( e - > Name ( ) , " functioncall " ) = = 0 ) {
FunctionUsage & func = _functions [ e - > Attribute ( " functionName " ) ] ;
if ( func . filename = = filename )
func . usedSameFile = true ;
else
func . usedOtherFile = true ;
}
}
}