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
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 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"
2023-04-30 07:33:19 +02:00
# include "utils.h"
2017-05-27 04:33:47 +02:00
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 <cstring>
2022-09-24 11:59:13 +02:00
# include <fstream> // IWYU pragma: keep
2023-03-02 21:50:14 +01:00
# include <map>
2022-09-16 07:15:49 +02:00
# include <sstream> // IWYU pragma: keep
2023-03-02 21:50:14 +01:00
# include <tuple>
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
2023-01-14 20:16:55 +01:00
static std : : string stripTemplateParameters ( const std : : string & funcName ) {
std : : string name = funcName ;
const auto pos = name . find ( ' < ' ) ;
if ( pos > 0 & & pos ! = std : : string : : npos )
name . erase ( pos - 1 ) ;
return name ;
}
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 ) ;
2009-06-14 07:58:36 +02:00
2014-03-26 08:54:56 +01:00
// Function declarations..
2023-05-03 11:19:13 +02:00
if ( ! doMarkup ) {
const SymbolDatabase * symbolDatabase = tokenizer . getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
const Function * func = scope - > function ;
if ( ! func | | ! func - > token | | scope - > bodyStart - > fileIndex ( ) ! = 0 )
continue ;
2012-04-09 11:00:31 +02:00
2023-05-03 11:19:13 +02:00
// Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor))
if ( func - > isAttributeConstructor ( ) | | func - > isAttributeDestructor ( ) | | func - > type ! = Function : : eFunction | | func - > isOperator ( ) )
continue ;
2013-08-30 06:27:46 +02:00
2023-05-03 11:19:13 +02:00
if ( func - > isExtern ( ) )
continue ;
2022-07-14 20:59:58 +02:00
2023-05-03 11:19:13 +02:00
mFunctionDecl . emplace_back ( func ) ;
2016-10-31 20:43:57 +01:00
2023-05-03 11:19:13 +02:00
FunctionUsage & usage = mFunctions [ stripTemplateParameters ( func - > name ( ) ) ] ;
2008-12-18 22:28:57 +01:00
2023-05-03 11:19:13 +02:00
if ( ! usage . lineNumber )
usage . lineNumber = func - > token - > linenr ( ) ;
2011-09-18 07:13:39 +02:00
2023-05-03 11:19:13 +02:00
// No filename set yet..
if ( usage . filename . empty ( ) ) {
usage . filename = tokenizer . list . getSourceFilePath ( ) ;
}
// Multiple files => filename = "+"
else if ( usage . filename ! = tokenizer . list . getSourceFilePath ( ) ) {
//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 + + ;
2022-09-20 07:30:12 +02:00
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 ;
2022-10-06 20:12:07 +02:00
mFunctionCalls . insert ( std : : move ( 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
2023-05-03 11:19:13 +02:00
if ( doMarkup )
funcname = Token : : Match ( tok , " %name% ( " ) ? tok : nullptr ;
else 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 ) ;
2023-05-14 12:16:17 +02:00
} else if ( tok - > scope ( ) - > type ! = Scope : : ScopeType : : eEnum & & ( Token : : Match ( tok , " [ ; { } . , ( ) [ = + - / | ! ? : ] " ) || Token::Match(tok, " return | throw " ))) {
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
2023-01-14 20:16:55 +01:00
if ( ! Token : : Match ( funcname , " %name% [(),;]:}>] " ) | | funcname - > varId ( ) )
2012-06-14 22:04:21 +02:00
continue ;
}
2009-05-31 08:01:16 +02:00
2023-01-14 20:16:55 +01:00
if ( ! funcname | | funcname - > isKeyword ( ) | | funcname - > isStandardType ( ) )
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 ) {
2023-01-14 20:16:55 +01:00
const auto baseName = stripTemplateParameters ( funcname - > str ( ) ) ;
FunctionUsage & func = mFunctions [ baseName ] ;
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
2023-01-14 20:16:55 +01:00
mFunctionCalls . insert ( baseName ) ;
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
2022-12-30 15:13:47 +01:00
return std : : find ( additionalOperators . cbegin ( ) , additionalOperators . cend ( ) , funcName . substr ( operatorPrefix . length ( ) ) ) ! = additionalOperators . cend ( ) ;
2018-03-12 10:25:30 +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
{
2023-01-08 14:30:42 +01:00
using ErrorParams = std : : tuple < std : : string , unsigned int , std : : string > ;
std : : vector < ErrorParams > errors ; // ensure well-defined order
2022-12-20 20:32:16 +01:00
for ( std : : unordered_map < std : : string , FunctionUsage > : : const_iterator it = mFunctions . cbegin ( ) ; it ! = mFunctions . cend ( ) ; + + 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 ;
2023-01-18 20:52:33 +01:00
if ( settings . library . isentrypoint ( it - > first ) )
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 ;
2023-01-08 14:30:42 +01:00
errors . emplace_back ( filename , func . lineNumber , it - > first ) ;
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
}
}
2023-01-08 14:30:42 +01:00
std : : sort ( errors . begin ( ) , errors . end ( ) ) ;
for ( const auto & e : errors )
unusedFunctionError ( errorLogger , std : : get < 0 > ( e ) , std : : get < 1 > ( e ) , std : : get < 2 > ( e ) ) ;
return ! errors . empty ( ) ;
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 ( ) ) {
2022-09-29 21:47:17 +02:00
locationList . emplace_back ( filename , lineNumber ) ;
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
2023-08-18 13:45:25 +02:00
Check : : writeToErrorList ( 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 ;
2023-04-08 18:06:38 +02:00
if ( settings - > useSingleJob ( ) & & 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 ;
2023-08-29 12:00:52 +02:00
CheckUnusedFunctions dummy ( nullptr , & settings , & errorLogger ) ;
dummy .
logChecker ( " CheckUnusedFunctions::analyseWholeProgram " ) ; // unusedFunctions
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
}
2023-01-18 20:52:33 +01:00
void CheckUnusedFunctions : : analyseWholeProgram ( const Settings & settings , 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 ;
2023-06-20 18:43:21 +02:00
}
if ( std : : strcmp ( e2 - > Name ( ) , " functiondecl " ) = = 0 ) {
2017-08-24 17:23:44 +02:00
const char * lineNumber = e2 - > Attribute ( " lineNumber " ) ;
if ( lineNumber )
2023-04-08 22:29:09 +02:00
decls [ functionName ] = Location ( sourcefile , strToInt < int > ( lineNumber ) ) ;
2016-11-05 21:26:56 +01:00
}
}
}
}
2022-12-20 20:32:16 +01:00
for ( std : : map < std : : string , Location > : : const_iterator decl = decls . cbegin ( ) ; decl ! = decls . cend ( ) ; + + decl ) {
2023-05-26 06:13:48 +02:00
const std : : string & functionName = stripTemplateParameters ( decl - > first ) ;
2016-11-07 21:49:58 +01:00
2023-01-18 20:52:33 +01:00
if ( settings . library . isentrypoint ( functionName ) )
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
}
}
}