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
2022-02-05 11:45:17 +01:00
* Copyright ( C ) 2007 - 2022 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"
2017-05-27 04:33:47 +02:00
2019-04-27 17:17:51 +02:00
# include "astutils.h"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "library.h"
# include "settings.h"
2014-03-26 08:54:56 +01:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "tokenlist.h"
2018-03-12 10:25:30 +01:00
# include <algorithm>
2022-01-27 19:03:20 +01:00
# include <cctype>
2017-05-27 04:33:47 +02:00
# include <cstdlib>
# include <cstring>
2022-01-27 19:03:20 +01:00
# include <istream>
# include <memory>
2022-08-19 10:16:23 +02:00
# include <sstream>
2017-05-27 04:33:47 +02:00
# include <utility>
2022-01-27 19:03:20 +01:00
# include <vector>
# include <tinyxml2.h>
namespace CTU {
class FileInfo ;
}
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
//---------------------------------------------------------------------------
2018-02-10 15:34:49 +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..
2019-04-27 17:04:14 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2014-03-26 08:54:56 +01:00
const Function * func = scope - > function ;
2018-04-27 22:36:30 +02:00
if ( ! func | | ! func - > token | | scope - > bodyStart - > 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))
2016-12-08 13:54:26 +01:00
if ( func - > isAttributeConstructor ( ) | | func - > isAttributeDestructor ( ) | | func - > type ! = Function : : eFunction | | func - > isOperator ( ) )
2013-08-30 06:27:46 +02:00
continue ;
2022-07-14 20:59:58 +02:00
if ( func - > isExtern ( ) )
continue ;
2014-03-26 08:54:56 +01:00
// Don't care about templates
2020-11-01 15:10:02 +01:00
if ( tokenizer . isCPP ( ) & & func - > templateDef ! = nullptr )
continue ;
2008-12-18 22:28:57 +01:00
2018-06-17 18:58:27 +02:00
mFunctionDecl . emplace_back ( func ) ;
2016-10-31 20:43:57 +01:00
2018-06-17 18:58:27 +02:00
FunctionUsage & usage = mFunctions [ 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..
2019-04-27 17:17:51 +02:00
const Token * lambdaEndToken = nullptr ;
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
2019-04-27 17:17:51 +02:00
if ( tok = = lambdaEndToken )
lambdaEndToken = nullptr ;
else if ( ! lambdaEndToken & & tok - > str ( ) = = " [ " )
lambdaEndToken = findLambdaEndToken ( tok ) ;
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 ) ) ;
2017-05-28 16:39:18 +02:00
// not found
if ( ! markupVarToken )
continue ;
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)
2017-05-28 16:39:18 +02:00
while ( ( scope | | start ) & & markupVarToken ) {
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 ( ) ) ) {
2018-06-17 18:58:27 +02:00
mFunctionCalls . insert ( markupVarToken - > str ( ) ) ;
if ( mFunctions . find ( markupVarToken - > str ( ) ) ! = mFunctions . end ( ) )
mFunctions [ markupVarToken - > str ( ) ] . usedOtherFile = true ;
2014-01-02 21:47:35 +01:00
else if ( markupVarToken - > next ( ) - > str ( ) = = " ( " ) {
2018-06-17 18:58:27 +02:00
FunctionUsage & func = mFunctions [ markupVarToken - > str ( ) ] ;
2014-11-24 06:37:08 +01:00
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
2017-08-09 20:00:26 +02:00
& & settings - > library . isexporter ( tok - > str ( ) ) & & tok - > next ( ) ! = nullptr ) {
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 ( ) ;
2018-06-17 18:58:27 +02:00
if ( mFunctions . find ( value ) ! = mFunctions . end ( ) ) {
mFunctions [ value ] . usedOtherFile = true ;
2013-10-20 14:09:10 +02:00
}
2018-06-17 18:58:27 +02:00
mFunctionCalls . 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 ( ) ;
2018-06-17 18:58:27 +02:00
if ( value ! = " ) " & & mFunctions . find ( value ) ! = mFunctions . end ( ) ) {
mFunctions [ value ] . usedOtherFile = true ;
2013-10-20 14:09:10 +02:00
}
2018-06-17 18:58:27 +02:00
mFunctionCalls . 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 ( ) ) {
2018-06-17 18:58:27 +02:00
mFunctions [ value ] . usedOtherFile = true ;
mFunctionCalls . 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 ;
2017-06-02 20:38:00 +02:00
value . clear ( ) ;
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 ) ;
2018-06-17 18:58:27 +02:00
mFunctions [ value ] . usedOtherFile = true ;
mFunctionCalls . 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
2019-04-27 17:17:51 +02:00
if ( ( lambdaEndToken | | tok - > scope ( ) - > isExecutable ( ) ) & & Token : : Match ( tok , " %name% ( " ) ) {
2014-12-09 22:04:51 +01:00
funcname = tok ;
2019-04-27 17:17:51 +02:00
} else if ( ( lambdaEndToken | | tok - > scope ( ) - > isExecutable ( ) ) & & Token : : Match ( tok , " %name% < " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " > ( " ) ) {
2014-12-09 22:04:51 +01:00
funcname = tok ;
2021-09-12 20:27:49 +02:00
} else if ( Token : : Match ( tok , " < %name% " ) & & tok - > link ( ) ) {
funcname = tok - > next ( ) ;
while ( Token : : Match ( funcname , " %name% :: %name% " ) )
funcname = funcname - > tokAt ( 2 ) ;
2014-12-09 22:04:51 +01:00
} 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
2018-08-08 11:31:25 +02: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 ) {
2021-08-07 20:51:18 +02:00
FunctionUsage & func = mFunctions [ 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
2018-06-17 18:58:27 +02:00
mFunctionCalls . 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
2018-03-12 10:19:55 +01:00
static bool isOperatorFunction ( const std : : string & funcName )
{
2018-03-12 10:25:30 +01:00
/* Operator functions are invalid function names for C, so no need to check
* this in here . As result the returned error function might be incorrect .
*
* List of valid operators can be found at :
* http : //en.cppreference.com/w/cpp/language/operators
*
* Conversion functions must be a member function ( at least for gcc ) , so no
* need to cover them for unused functions .
*
2018-05-14 13:11:45 +02:00
* To speed up the comparison , not the whole list of operators is used .
2018-03-12 10:25:30 +01:00
* Instead only the character after the operator prefix is checked to be a
* none alpa numeric value , but the ' _ ' , to cover function names like
* " operator_unused " . In addition the following valid operators are checked :
* - new
* - new [ ]
* - delete
* - delete [ ]
*/
const std : : string operatorPrefix = " operator " ;
if ( funcName . compare ( 0 , operatorPrefix . length ( ) , operatorPrefix ) ! = 0 ) {
return false ;
}
// Taking care of funcName == "operator", which is no valid operator
if ( funcName . length ( ) = = operatorPrefix . length ( ) ) {
return false ;
}
const char firstOperatorChar = funcName [ operatorPrefix . length ( ) ] ;
if ( firstOperatorChar = = ' _ ' ) {
return false ;
}
if ( ! std : : isalnum ( firstOperatorChar ) ) {
return true ;
}
2018-04-08 22:54:10 +02:00
const std : : vector < std : : string > additionalOperators = {
" new " , " new[] " , " delete " , " delete[] "
} ;
2018-03-12 10:25:30 +01:00
2020-04-21 17:27:51 +02:00
return std : : find ( additionalOperators . begin ( ) , additionalOperators . end ( ) , funcName . substr ( operatorPrefix . length ( ) ) ) ! = additionalOperators . end ( ) ;
2018-03-12 10:25:30 +01:00
}
2008-12-18 22:28:57 +01:00
2014-11-15 18:44:23 +01:00
2022-03-02 07:46:47 +01:00
bool CheckUnusedFunctions : : check ( ErrorLogger * const errorLogger , const Settings & settings ) const
2014-11-24 06:37:08 +01:00
{
2018-01-12 08:24:01 +01:00
bool errors = false ;
2018-06-17 18:58:27 +02:00
for ( std : : map < std : : string , FunctionUsage > : : const_iterator it = mFunctions . begin ( ) ; it ! = mFunctions . 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 " ) ) | |
2016-12-08 13:54:26 +01:00
it - > first = = " if " )
2009-01-16 17:20:35 +01:00
continue ;
2016-12-08 13:54:26 +01:00
if ( ! func . usedSameFile ) {
2018-03-12 10:25:30 +01:00
if ( isOperatorFunction ( it - > first ) )
continue ;
2009-01-11 07:44:32 +01:00
std : : string filename ;
2016-12-08 13:54:26 +01:00
if ( func . filename ! = " + " )
2018-03-12 10:19:55 +01:00
filename = func . filename ;
unusedFunctionError ( errorLogger , filename , func . lineNumber , it - > first ) ;
errors = true ;
2021-08-07 20:51:18 +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" */
/*
2021-08-07 20:51:18 +02: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. " ;
mErrorLogger - > reportErr ( errmsg . str ( ) ) ;
errors = true ;
*/
2008-12-18 22:28:57 +01:00
}
}
2018-02-06 08:59:36 +01:00
return errors ;
2008-12-18 22:28:57 +01:00
}
2011-09-18 07:13:39 +02:00
void CheckUnusedFunctions : : unusedFunctionError ( ErrorLogger * const errorLogger ,
2021-08-07 20:51:18 +02:00
const std : : string & filename , unsigned int lineNumber ,
const std : : string & funcname )
2010-04-04 22:55:28 +02:00
{
2020-05-23 07:16:49 +02:00
std : : list < ErrorMessage : : FileLocation > locationList ;
2011-10-13 20:53:06 +02:00
if ( ! filename . empty ( ) ) {
2020-05-23 07:16:49 +02:00
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 ;
2022-09-08 09:21:35 +02:00
locationList . push_back ( std : : move ( fileLoc ) ) ;
2010-07-08 13:01:53 +02:00
}
2021-02-24 22:00:06 +01:00
const ErrorMessage errmsg ( locationList , emptyString , Severity : : style , " $symbol: " + funcname + " \n The function '$symbol' is never used. " , " unusedFunction " , CWE561 , Certainty : : normal ) ;
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
{
2021-02-24 22:00:06 +01:00
if ( ! settings - > checks . isEnabled ( Checks : : unusedFunction ) )
2016-12-09 20:11:20 +01:00
return nullptr ;
if ( 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 ;
}
2018-12-25 21:11:23 +01:00
bool CheckUnusedFunctions : : analyseWholeProgram ( const CTU : : FileInfo * ctu , const std : : list < Check : : FileInfo * > & fileInfo , const Settings & settings , ErrorLogger & errorLogger )
2014-12-02 06:41:18 +01:00
{
2018-12-25 21:11:23 +01:00
( void ) ctu ;
2014-12-02 06:41:18 +01:00
( void ) fileInfo ;
2018-01-12 08:24:01 +01:00
return 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 ( ) )
2021-08-07 20:51:18 +02:00
{ }
2016-10-31 20:43:57 +01:00
2016-11-01 13:34:26 +01:00
std : : string CheckUnusedFunctions : : analyzerInfo ( ) const
2016-10-29 12:18:11 +02:00
{
std : : ostringstream ret ;
2019-04-27 17:04:14 +02:00
for ( const FunctionDecl & functionDecl : mFunctionDecl ) {
2016-10-31 20:43:57 +01:00
ret < < " <functiondecl "
2019-04-27 17:04:14 +02:00
< < " functionName= \" " < < ErrorLogger : : toxml ( functionDecl . functionName ) < < ' \" '
< < " lineNumber= \" " < < functionDecl . lineNumber < < " \" /> \n " ;
2016-10-31 20:43:57 +01:00
}
2019-04-27 17:04:14 +02:00
for ( const std : : string & fc : mFunctionCalls ) {
ret < < " <functioncall functionName= \" " < < ErrorLogger : : toxml ( fc ) < < " \" /> \n " ;
2016-10-29 12:18:11 +02:00
}
return ret . str ( ) ;
}
2016-11-05 21:26:56 +01:00
namespace {
struct Location {
2016-12-27 21:20:17 +01:00
Location ( ) : lineNumber ( 0 ) { }
2022-07-28 22:51:45 +02:00
Location ( std : : string f , const int l ) : fileName ( std : : move ( f ) ) , lineNumber ( l ) { }
2016-11-05 21:26:56 +01:00
std : : string fileName ;
int lineNumber ;
} ;
2016-10-31 20:43:57 +01:00
}
2016-12-08 22:46:44 +01:00
void CheckUnusedFunctions : : analyseWholeProgram ( ErrorLogger * const errorLogger , const std : : string & buildDir )
2016-10-31 20:43:57 +01:00
{
2016-11-05 21:26:56 +01:00
std : : map < std : : string , Location > decls ;
std : : set < std : : string > calls ;
2016-12-08 22:46:44 +01:00
const std : : string filesTxt ( buildDir + " /files.txt " ) ;
std : : ifstream fin ( filesTxt . c_str ( ) ) ;
std : : string filesTxtLine ;
while ( std : : getline ( fin , filesTxtLine ) ) {
const std : : string : : size_type firstColon = filesTxtLine . find ( ' : ' ) ;
if ( firstColon = = std : : string : : npos )
continue ;
2018-05-01 08:09:11 +02:00
const std : : string : : size_type secondColon = filesTxtLine . find ( ' : ' , firstColon + 1 ) ;
if ( secondColon = = std : : string : : npos )
2016-12-08 22:46:44 +01:00
continue ;
2016-12-09 20:11:20 +01:00
const std : : string xmlfile = buildDir + ' / ' + filesTxtLine . substr ( 0 , firstColon ) ;
2018-05-01 08:09:11 +02:00
const std : : string sourcefile = filesTxtLine . substr ( secondColon + 1 ) ;
2016-11-05 21:26:56 +01:00
tinyxml2 : : XMLDocument doc ;
2018-04-04 21:51:31 +02:00
const tinyxml2 : : XMLError error = doc . LoadFile ( xmlfile . c_str ( ) ) ;
2016-11-05 21:26:56 +01:00
if ( error ! = tinyxml2 : : XML_SUCCESS )
continue ;
const tinyxml2 : : XMLElement * const rootNode = doc . FirstChildElement ( ) ;
if ( rootNode = = nullptr )
continue ;
for ( const tinyxml2 : : XMLElement * e = rootNode - > FirstChildElement ( ) ; e ; e = e - > NextSiblingElement ( ) ) {
2017-08-24 17:23:44 +02:00
if ( std : : strcmp ( e - > Name ( ) , " FileInfo " ) ! = 0 )
continue ;
const char * checkattr = e - > Attribute ( " check " ) ;
if ( checkattr = = nullptr | | std : : strcmp ( checkattr , " CheckUnusedFunctions " ) ! = 0 )
continue ;
for ( const tinyxml2 : : XMLElement * e2 = e - > FirstChildElement ( ) ; e2 ; e2 = e2 - > NextSiblingElement ( ) ) {
const char * functionName = e2 - > Attribute ( " functionName " ) ;
if ( functionName = = nullptr )
continue ;
if ( std : : strcmp ( e2 - > Name ( ) , " functioncall " ) = = 0 ) {
calls . insert ( functionName ) ;
continue ;
} else if ( std : : strcmp ( e2 - > Name ( ) , " functiondecl " ) = = 0 ) {
const char * lineNumber = e2 - > Attribute ( " lineNumber " ) ;
if ( lineNumber )
decls [ functionName ] = Location ( sourcefile , std : : atoi ( lineNumber ) ) ;
2016-11-05 21:26:56 +01:00
}
}
}
}
for ( std : : map < std : : string , Location > : : const_iterator decl = decls . begin ( ) ; decl ! = decls . end ( ) ; + + decl ) {
const std : : string & functionName = decl - > first ;
2016-11-07 21:49:58 +01:00
2022-01-18 22:02:25 +01:00
// TODO: move to configuration files
// TODO: WinMain, wmain and _tmain only apply to Windows code
// TODO: also skip other known entry functions i.e. annotated with "constructor" and "destructor" attributes
if ( functionName = = " main " | | functionName = = " WinMain " | | functionName = = " wmain " | | functionName = = " _tmain " | |
2016-12-08 13:54:26 +01:00
functionName = = " if " )
2016-11-07 21:49:58 +01:00
continue ;
2018-03-12 10:25:30 +01:00
if ( calls . find ( functionName ) = = calls . end ( ) & & ! isOperatorFunction ( functionName ) ) {
2016-11-05 21:26:56 +01:00
const Location & loc = decl - > second ;
unusedFunctionError ( errorLogger , loc . fileName , loc . lineNumber , functionName ) ;
2016-10-31 20:43:57 +01:00
}
}
}