2011-08-19 20:35:25 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2020-05-10 11:16:32 +02:00
* Copyright ( C ) 2007 - 2020 Cppcheck team .
2011-08-19 20:35:25 +02: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 "checkunusedvar.h"
2017-05-27 04:33:47 +02:00
2018-04-17 14:23:04 +02:00
# include "astutils.h"
2020-06-30 10:59:57 +02:00
# include "preprocessor.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2011-08-19 20:35:25 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "valueflow.h"
2012-12-17 18:19:05 +01:00
# include <algorithm>
2017-05-27 04:33:47 +02:00
# include <cctype>
# include <cstddef>
# include <list>
# include <set>
2013-04-01 12:41:14 +02:00
# include <utility>
2017-05-27 04:33:47 +02:00
# include <vector>
2011-08-19 20:35:25 +02:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckUnusedVar instance ;
2011-08-19 20:35:25 +02:00
}
2016-07-16 21:21:24 +02:00
static const struct CWE CWE563 ( 563U ) ; // Assignment to Variable without Use ('Unused Variable')
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
static const struct CWE CWE665 ( 665U ) ; // Improper Initialization
2016-07-15 15:49:21 +02:00
2020-09-27 19:15:15 +02:00
/** Is scope a raii class scope */
static bool isRaiiClassScope ( const Scope * classScope )
{
return classScope & & classScope - > getDestructor ( ) ! = nullptr ;
}
/** Is ValueType a raii class? */
static bool isRaiiClass ( const ValueType * valueType , bool cpp , bool defaultReturn = true )
{
if ( ! cpp )
return false ;
if ( ! valueType )
return defaultReturn ;
if ( valueType - > smartPointerType & & isRaiiClassScope ( valueType - > smartPointerType - > classScope ) )
return true ;
switch ( valueType - > type ) {
case ValueType : : Type : : UNKNOWN_TYPE :
case ValueType : : Type : : NONSTD :
return defaultReturn ;
case ValueType : : Type : : RECORD :
if ( isRaiiClassScope ( valueType - > typeScope ) )
return true ;
return defaultReturn ;
case ValueType : : Type : : CONTAINER :
case ValueType : : Type : : ITERATOR :
case ValueType : : Type : : VOID :
case ValueType : : Type : : BOOL :
case ValueType : : Type : : CHAR :
case ValueType : : Type : : SHORT :
case ValueType : : Type : : WCHAR_T :
case ValueType : : Type : : INT :
case ValueType : : Type : : LONG :
case ValueType : : Type : : LONGLONG :
case ValueType : : Type : : UNKNOWN_INT :
case ValueType : : Type : : FLOAT :
case ValueType : : Type : : DOUBLE :
case ValueType : : Type : : LONGDOUBLE :
return false ;
}
return defaultReturn ;
}
2016-07-15 15:49:21 +02:00
2011-08-19 20:35:25 +02:00
/**
* @ brief This class is used create a list of variables within a function .
*/
2011-10-13 20:53:06 +02:00
class Variables {
2011-08-19 20:35:25 +02:00
public :
2011-12-18 20:15:41 +01:00
enum VariableType { standard , array , pointer , reference , pointerArray , referenceArray , pointerPointer , none } ;
2011-08-19 20:35:25 +02:00
/** Store information about variable usage */
2011-10-13 20:53:06 +02:00
class VariableUsage {
2011-08-19 20:35:25 +02:00
public :
2014-11-13 21:39:14 +01:00
explicit VariableUsage ( const Variable * var = nullptr ,
VariableType type = standard ,
bool read = false ,
bool write = false ,
bool modified = false ,
bool allocateMemory = false ) :
2012-09-04 14:53:24 +02:00
_var ( var ) ,
2017-08-09 20:00:26 +02:00
_lastAccess ( var ? var - > nameToken ( ) : nullptr ) ,
2018-06-16 20:25:54 +02:00
mType ( type ) ,
2011-08-19 20:35:25 +02:00
_read ( read ) ,
_write ( write ) ,
_modified ( modified ) ,
2014-11-20 14:20:09 +01:00
_allocateMemory ( allocateMemory ) {
2011-08-19 20:35:25 +02:00
}
/** variable is used.. set both read+write */
2018-12-17 17:48:45 +01:00
void use ( ) {
2011-08-19 20:35:25 +02:00
_read = true ;
_write = true ;
}
/** is variable unused? */
2014-11-20 14:20:09 +01:00
bool unused ( ) const {
2017-10-08 07:54:39 +02:00
return ( ! _read & & ! _write ) ;
2011-08-19 20:35:25 +02:00
}
2012-05-14 20:46:23 +02:00
std : : set < unsigned int > _aliases ;
std : : set < const Scope * > _assignments ;
2012-09-04 14:53:24 +02:00
const Variable * _var ;
const Token * _lastAccess ;
2018-06-16 20:25:54 +02:00
VariableType mType ;
2011-08-19 20:35:25 +02:00
bool _read ;
bool _write ;
bool _modified ; // read/modify/write
bool _allocateMemory ;
} ;
2014-11-20 14:20:09 +01:00
void clear ( ) {
2018-06-17 18:55:41 +02:00
mVarUsage . clear ( ) ;
2011-08-19 20:35:25 +02:00
}
2014-11-20 14:20:09 +01:00
const std : : map < unsigned int , VariableUsage > & varUsage ( ) const {
2018-06-17 18:55:41 +02:00
return mVarUsage ;
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void addVar ( const Variable * var , VariableType type , bool write_ ) ;
void allocateMemory ( unsigned int varid , const Token * tok ) ;
void read ( unsigned int varid , const Token * tok ) ;
void readAliases ( unsigned int varid , const Token * tok ) ;
void readAll ( unsigned int varid , const Token * tok ) ;
void write ( unsigned int varid , const Token * tok ) ;
void writeAliases ( unsigned int varid , const Token * tok ) ;
void writeAll ( unsigned int varid , const Token * tok ) ;
void use ( unsigned int varid , const Token * tok ) ;
void modified ( unsigned int varid , const Token * tok ) ;
2011-08-19 20:35:25 +02:00
VariableUsage * find ( unsigned int varid ) ;
void alias ( unsigned int varid1 , unsigned int varid2 , bool replace ) ;
2014-11-20 14:20:09 +01:00
void erase ( unsigned int varid ) {
2018-06-17 18:55:41 +02:00
mVarUsage . erase ( varid ) ;
2011-08-19 20:35:25 +02:00
}
void eraseAliases ( unsigned int varid ) ;
void eraseAll ( unsigned int varid ) ;
void clearAliases ( unsigned int varid ) ;
private :
2012-12-04 21:39:51 +01:00
2018-06-17 18:55:41 +02:00
std : : map < unsigned int , VariableUsage > mVarUsage ;
2011-08-19 20:35:25 +02:00
} ;
2011-12-18 20:15:41 +01:00
2011-08-19 20:35:25 +02:00
/**
* Alias the 2 given variables . Either replace the existing aliases if
* they exist or merge them . You would replace an existing alias when this
* assignment is in the same scope as the previous assignment . You might
* merge the aliases when this assignment is in a different scope from the
* previous assignment depending on the relationship of the 2 scopes .
*/
void Variables : : alias ( unsigned int varid1 , unsigned int varid2 , bool replace )
{
VariableUsage * var1 = find ( varid1 ) ;
VariableUsage * var2 = find ( varid2 ) ;
2014-08-14 16:10:12 +02:00
if ( ! var1 | | ! var2 )
return ;
2011-08-19 20:35:25 +02:00
// alias to self
2011-10-13 20:53:06 +02:00
if ( varid1 = = varid2 ) {
2018-12-17 17:48:45 +01:00
var1 - > use ( ) ;
2011-08-19 20:35:25 +02:00
return ;
}
2011-10-13 20:53:06 +02:00
if ( replace ) {
2011-08-19 20:35:25 +02:00
// remove var1 from all aliases
2015-06-16 22:45:33 +02:00
for ( std : : set < unsigned int > : : const_iterator i = var1 - > _aliases . begin ( ) ; i ! = var1 - > _aliases . end ( ) ; + + i ) {
2011-08-19 20:35:25 +02:00
VariableUsage * temp = find ( * i ) ;
if ( temp )
2013-07-20 12:31:04 +02:00
temp - > _aliases . erase ( var1 - > _var - > declarationId ( ) ) ;
2011-08-19 20:35:25 +02:00
}
// remove all aliases from var1
var1 - > _aliases . clear ( ) ;
}
// var1 gets all var2s aliases
2015-06-16 22:45:33 +02:00
for ( std : : set < unsigned int > : : const_iterator i = var2 - > _aliases . begin ( ) ; i ! = var2 - > _aliases . end ( ) ; + + i ) {
2011-08-19 20:35:25 +02:00
if ( * i ! = varid1 )
var1 - > _aliases . insert ( * i ) ;
}
// var2 is an alias of var1
var2 - > _aliases . insert ( varid1 ) ;
var1 - > _aliases . insert ( varid2 ) ;
2018-06-16 20:25:54 +02:00
if ( var2 - > mType = = Variables : : pointer ) {
2011-08-19 20:35:25 +02:00
var2 - > _read = true ;
2012-12-04 21:39:51 +01:00
}
2011-08-19 20:35:25 +02:00
}
void Variables : : clearAliases ( unsigned int varid )
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2011-08-19 20:35:25 +02:00
// remove usage from all aliases
2015-06-16 22:45:33 +02:00
std : : set < unsigned int > : : const_iterator i ;
2011-08-19 20:35:25 +02:00
2011-10-13 20:53:06 +02:00
for ( i = usage - > _aliases . begin ( ) ; i ! = usage - > _aliases . end ( ) ; + + i ) {
2011-08-19 20:35:25 +02:00
VariableUsage * temp = find ( * i ) ;
if ( temp )
2013-07-20 12:31:04 +02:00
temp - > _aliases . erase ( usage - > _var - > declarationId ( ) ) ;
2011-08-19 20:35:25 +02:00
}
// remove all aliases from usage
usage - > _aliases . clear ( ) ;
}
}
void Variables : : eraseAliases ( unsigned int varid )
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2018-04-06 08:07:15 +02:00
for ( std : : set < unsigned int > : : const_iterator aliases = usage - > _aliases . begin ( ) ; aliases ! = usage - > _aliases . end ( ) ; + + aliases )
2011-08-19 20:35:25 +02:00
erase ( * aliases ) ;
}
}
void Variables : : eraseAll ( unsigned int varid )
{
eraseAliases ( varid ) ;
erase ( varid ) ;
}
2012-09-04 14:53:24 +02:00
void Variables : : addVar ( const Variable * var ,
2011-08-19 20:35:25 +02:00
VariableType type ,
bool write_ )
{
2013-07-20 12:31:04 +02:00
if ( var - > declarationId ( ) > 0 ) {
2018-06-17 18:55:41 +02:00
mVarUsage . insert ( std : : make_pair ( var - > declarationId ( ) , VariableUsage ( var , type , false , write_ , false ) ) ) ;
2012-12-04 21:39:51 +01:00
}
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : allocateMemory ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2012-09-04 14:53:24 +02:00
if ( usage ) {
2011-08-19 20:35:25 +02:00
usage - > _allocateMemory = true ;
2012-09-04 14:53:24 +02:00
usage - > _lastAccess = tok ;
}
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : read ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2012-09-04 14:53:24 +02:00
if ( usage ) {
2011-08-19 20:35:25 +02:00
usage - > _read = true ;
2012-12-04 21:39:51 +01:00
if ( tok )
usage - > _lastAccess = tok ;
2012-09-04 14:53:24 +02:00
}
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : readAliases ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2020-09-21 19:30:47 +02:00
for ( unsigned int aliases : usage - > _aliases ) {
VariableUsage * aliased = find ( aliases ) ;
2011-08-19 20:35:25 +02:00
2012-09-04 14:53:24 +02:00
if ( aliased ) {
2011-08-19 20:35:25 +02:00
aliased - > _read = true ;
2012-09-04 14:53:24 +02:00
aliased - > _lastAccess = tok ;
}
2011-08-19 20:35:25 +02:00
}
}
}
2012-09-04 14:53:24 +02:00
void Variables : : readAll ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
2012-09-11 14:24:12 +02:00
read ( varid , tok ) ;
readAliases ( varid , tok ) ;
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : write ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2012-09-02 18:50:17 +02:00
if ( usage ) {
2011-08-19 20:35:25 +02:00
usage - > _write = true ;
2012-09-30 17:22:35 +02:00
if ( ! usage - > _var - > isStatic ( ) & & ! Token : : simpleMatch ( tok - > next ( ) , " = 0 ; " ) )
2012-09-11 14:14:35 +02:00
usage - > _read = false ;
2012-09-04 14:53:24 +02:00
usage - > _lastAccess = tok ;
2012-09-02 18:50:17 +02:00
}
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : writeAliases ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2018-04-06 08:07:15 +02:00
for ( std : : set < unsigned int > : : const_iterator aliases = usage - > _aliases . begin ( ) ; aliases ! = usage - > _aliases . end ( ) ; + + aliases ) {
2011-08-19 20:35:25 +02:00
VariableUsage * aliased = find ( * aliases ) ;
2012-09-04 14:53:24 +02:00
if ( aliased ) {
2011-08-19 20:35:25 +02:00
aliased - > _write = true ;
2012-09-04 14:53:24 +02:00
aliased - > _lastAccess = tok ;
}
2011-08-19 20:35:25 +02:00
}
}
}
2012-09-04 14:53:24 +02:00
void Variables : : writeAll ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
2012-09-11 14:24:12 +02:00
write ( varid , tok ) ;
writeAliases ( varid , tok ) ;
2011-08-19 20:35:25 +02:00
}
2012-09-04 14:53:24 +02:00
void Variables : : use ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2018-12-17 17:48:45 +01:00
usage - > use ( ) ;
2012-09-04 14:53:24 +02:00
usage - > _lastAccess = tok ;
2011-08-19 20:35:25 +02:00
2018-04-06 08:07:15 +02:00
for ( std : : set < unsigned int > : : const_iterator aliases = usage - > _aliases . begin ( ) ; aliases ! = usage - > _aliases . end ( ) ; + + aliases ) {
2011-08-19 20:35:25 +02:00
VariableUsage * aliased = find ( * aliases ) ;
2012-09-04 14:53:24 +02:00
if ( aliased ) {
2018-12-17 17:48:45 +01:00
aliased - > use ( ) ;
2012-09-04 14:53:24 +02:00
aliased - > _lastAccess = tok ;
}
2011-08-19 20:35:25 +02:00
}
}
}
2012-09-04 14:53:24 +02:00
void Variables : : modified ( unsigned int varid , const Token * tok )
2011-08-19 20:35:25 +02:00
{
VariableUsage * usage = find ( varid ) ;
2011-10-13 20:53:06 +02:00
if ( usage ) {
2016-10-10 21:27:40 +02:00
if ( ! usage - > _var - > isStatic ( ) )
usage - > _read = false ;
2011-08-19 20:35:25 +02:00
usage - > _modified = true ;
2012-09-04 14:53:24 +02:00
usage - > _lastAccess = tok ;
2011-08-19 20:35:25 +02:00
2018-04-06 08:07:15 +02:00
for ( std : : set < unsigned int > : : const_iterator aliases = usage - > _aliases . begin ( ) ; aliases ! = usage - > _aliases . end ( ) ; + + aliases ) {
2011-08-19 20:35:25 +02:00
VariableUsage * aliased = find ( * aliases ) ;
2012-09-04 14:53:24 +02:00
if ( aliased ) {
2011-08-19 20:35:25 +02:00
aliased - > _modified = true ;
2012-09-04 14:53:24 +02:00
aliased - > _lastAccess = tok ;
}
2011-08-19 20:35:25 +02:00
}
}
}
Variables : : VariableUsage * Variables : : find ( unsigned int varid )
{
2011-10-13 20:53:06 +02:00
if ( varid ) {
2018-06-17 18:55:41 +02:00
std : : map < unsigned int , VariableUsage > : : iterator i = mVarUsage . find ( varid ) ;
if ( i ! = mVarUsage . end ( ) )
2011-08-19 20:35:25 +02:00
return & i - > second ;
}
2017-08-09 20:00:26 +02:00
return nullptr ;
2011-08-19 20:35:25 +02:00
}
2012-02-29 20:57:48 +01:00
static const Token * doAssignment ( Variables & variables , const Token * tok , bool dereference , const Scope * scope )
2011-08-19 20:35:25 +02:00
{
// a = a + b;
2017-09-26 23:51:04 +02:00
if ( Token : : Match ( tok , " %var% = %var% !!; " ) ) {
const Token * rhsVarTok = tok - > tokAt ( 2 ) ;
if ( tok - > varId ( ) = = rhsVarTok - > varId ( ) ) {
return rhsVarTok ;
}
2011-08-19 20:35:25 +02:00
}
2015-12-31 01:15:49 +01:00
if ( Token : : Match ( tok , " %var% %assign% " ) & & tok - > strAt ( 1 ) ! = " = " )
return tok - > next ( ) ;
2012-02-29 20:57:48 +01:00
const Token * const tokOld = tok ;
2011-12-18 20:15:41 +01:00
2011-08-19 20:35:25 +02:00
// check for aliased variable
const unsigned int varid1 = tok - > varId ( ) ;
Variables : : VariableUsage * var1 = variables . find ( varid1 ) ;
2011-10-13 20:53:06 +02:00
if ( var1 ) {
2012-02-29 20:57:48 +01:00
// jump behind '='
tok = tok - > next ( ) ;
2015-12-31 01:15:49 +01:00
while ( ! tok - > isAssignmentOp ( ) ) {
2012-08-22 19:47:46 +02:00
if ( tok - > varId ( ) )
2012-09-04 14:53:24 +02:00
variables . read ( tok - > varId ( ) , tok ) ;
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
2012-08-22 19:47:46 +02:00
}
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
2013-02-20 17:43:16 +01:00
if ( Token : : Match ( tok , " ( const| struct|union| %type% * ) ( ( " ) )
tok = tok - > link ( ) - > next ( ) ;
if ( Token : : Match ( tok , " ( [(<] const| struct|union| %type% *| [>)] " ) )
tok = tok - > next ( ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " (| &| %name% " ) | |
2016-11-26 17:08:36 +01:00
( Token : : Match ( tok - > next ( ) , " < const| struct|union| %type% *| > ( &| %name% " ) ) ) {
2011-08-19 20:35:25 +02:00
bool addressOf = false ;
2012-02-29 20:57:48 +01:00
if ( Token : : Match ( tok , " %var% . " ) )
2012-09-04 14:53:24 +02:00
variables . use ( tok - > varId ( ) , tok ) ; // use = read + write
2011-08-19 20:35:25 +02:00
// check for C style cast
2012-02-29 20:57:48 +01:00
if ( tok - > str ( ) = = " ( " ) {
tok = tok - > next ( ) ;
if ( tok - > str ( ) = = " const " )
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2012-02-29 20:57:48 +01:00
if ( Token : : Match ( tok , " struct|union " ) )
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2013-03-04 19:13:49 +01:00
while ( ( tok - > isName ( ) & & tok - > varId ( ) = = 0 ) | | ( tok - > str ( ) = = " * " ) | | ( tok - > str ( ) = = " ) " ) )
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2012-02-29 20:57:48 +01:00
if ( tok - > str ( ) = = " & " ) {
2011-08-19 20:35:25 +02:00
addressOf = true ;
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
} else if ( tok - > str ( ) = = " ( " ) {
tok = tok - > next ( ) ;
if ( tok - > str ( ) = = " & " ) {
2011-08-19 20:35:25 +02:00
addressOf = true ;
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
}
2013-09-03 17:02:46 +02:00
} else if ( Token : : Match ( tok , " %cop% %var% " ) ) {
variables . read ( tok - > next ( ) - > varId ( ) , tok ) ;
2012-02-29 20:57:48 +01:00
}
2011-08-19 20:35:25 +02:00
}
// check for C++ style cast
2012-02-29 20:57:48 +01:00
else if ( tok - > str ( ) . find ( " cast " ) ! = std : : string : : npos & &
tok - > strAt ( 1 ) = = " < " ) {
tok = tok - > tokAt ( 2 ) ;
if ( tok - > str ( ) = = " const " )
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2012-02-29 20:57:48 +01:00
if ( Token : : Match ( tok , " struct|union " ) )
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
if ( tok - > str ( ) = = " * " )
tok = tok - > next ( ) ;
2011-08-19 20:35:25 +02:00
2012-02-29 20:57:48 +01:00
tok = tok - > tokAt ( 2 ) ;
2016-01-31 10:25:09 +01:00
if ( ! tok )
return tokOld ;
2012-02-29 20:57:48 +01:00
if ( tok - > str ( ) = = " & " ) {
2011-08-19 20:35:25 +02:00
addressOf = true ;
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
}
2011-08-19 20:35:25 +02:00
}
2012-02-29 20:57:48 +01:00
// no cast, no ?
2015-01-31 10:50:39 +01:00
else if ( ! Token : : Match ( tok , " %name% ? " ) ) {
2012-02-29 20:57:48 +01:00
if ( tok - > str ( ) = = " & " ) {
2011-08-19 20:35:25 +02:00
addressOf = true ;
2012-02-29 20:57:48 +01:00
tok = tok - > next ( ) ;
} else if ( tok - > str ( ) = = " new " )
return tokOld ;
2011-08-19 20:35:25 +02:00
}
// check if variable is local
2018-04-04 21:51:31 +02:00
const unsigned int varid2 = tok - > varId ( ) ;
2015-06-03 17:17:53 +02:00
const Variables : : VariableUsage * var2 = variables . find ( varid2 ) ;
2011-08-19 20:35:25 +02:00
2011-10-13 20:53:06 +02:00
if ( var2 ) { // local variable (alias or read it)
2018-06-16 20:25:54 +02:00
if ( var1 - > mType = = Variables : : pointer | | var1 - > mType = = Variables : : pointerArray ) {
2011-08-19 20:35:25 +02:00
if ( dereference )
2012-09-04 14:53:24 +02:00
variables . read ( varid2 , tok ) ;
2011-10-13 20:53:06 +02:00
else {
2011-08-19 20:35:25 +02:00
if ( addressOf | |
2018-06-16 20:25:54 +02:00
var2 - > mType = = Variables : : array | |
var2 - > mType = = Variables : : pointer ) {
2012-02-29 20:57:48 +01:00
bool replace = true ;
2011-08-19 20:35:25 +02:00
2012-06-24 16:54:37 +02:00
// pointerArray => don't replace
2018-06-16 20:25:54 +02:00
if ( var1 - > mType = = Variables : : pointerArray )
2012-06-24 16:54:37 +02:00
replace = false ;
2011-08-19 20:35:25 +02:00
// check if variable declared in same scope
2012-09-04 14:53:24 +02:00
else if ( scope = = var1 - > _var - > scope ( ) )
2011-08-19 20:35:25 +02:00
replace = true ;
// not in same scope as declaration
2011-10-13 20:53:06 +02:00
else {
2011-08-19 20:35:25 +02:00
// no other assignment in this scope
2012-04-28 15:43:42 +02:00
if ( var1 - > _assignments . find ( scope ) = = var1 - > _assignments . end ( ) | |
scope - > type = = Scope : : eSwitch ) {
2011-08-19 20:35:25 +02:00
// nothing to replace
if ( var1 - > _assignments . empty ( ) )
replace = false ;
// this variable has previous assignments
2011-10-13 20:53:06 +02:00
else {
2011-08-19 20:35:25 +02:00
/**
* @ todo determine if existing aliases should be replaced or merged
*/
replace = false ;
}
}
// assignment in this scope
2011-10-13 20:53:06 +02:00
else {
2012-02-29 20:57:48 +01:00
// replace when only one other assignment, merge them otherwise
replace = ( var1 - > _assignments . size ( ) = = 1 ) ;
2011-08-19 20:35:25 +02:00
}
}
variables . alias ( varid1 , varid2 , replace ) ;
2012-02-29 20:57:48 +01:00
} else if ( tok - > strAt ( 1 ) = = " ? " ) {
2018-06-16 20:25:54 +02:00
if ( var2 - > mType = = Variables : : reference )
2012-09-04 14:53:24 +02:00
variables . readAliases ( varid2 , tok ) ;
2011-08-19 20:35:25 +02:00
else
2012-09-04 14:53:24 +02:00
variables . read ( varid2 , tok ) ;
2012-08-22 19:47:46 +02:00
} else {
2012-12-28 18:18:36 +01:00
variables . readAll ( varid2 , tok ) ;
2011-08-19 20:35:25 +02:00
}
}
2018-06-16 20:25:54 +02:00
} else if ( var1 - > mType = = Variables : : reference ) {
2011-08-19 20:35:25 +02:00
variables . alias ( varid1 , varid2 , true ) ;
2018-12-30 20:20:20 +01:00
} else if ( var1 - > mType = = Variables : : standard & & addressOf ) {
variables . alias ( varid1 , varid2 , true ) ;
2011-10-13 20:53:06 +02:00
} else {
2018-06-16 20:25:54 +02:00
if ( ( var2 - > mType = = Variables : : pointer | | var2 - > mType = = Variables : : pointerArray ) & & tok - > strAt ( 1 ) = = " [ " )
2012-09-04 14:53:24 +02:00
variables . readAliases ( varid2 , tok ) ;
2011-08-19 20:35:25 +02:00
2012-09-04 14:53:24 +02:00
variables . read ( varid2 , tok ) ;
2011-08-19 20:35:25 +02:00
}
2011-10-13 20:53:06 +02:00
} else { // not a local variable (or an unsupported local variable)
2018-06-16 20:25:54 +02:00
if ( var1 - > mType = = Variables : : pointer & & ! dereference ) {
2011-08-19 20:35:25 +02:00
// check if variable declaration is in this scope
2017-07-09 11:19:00 +02:00
if ( var1 - > _var - > scope ( ) = = scope ) {
// If variable is used in RHS then "use" variable
for ( const Token * rhs = tok ; rhs & & rhs - > str ( ) ! = " ; " ; rhs = rhs - > next ( ) ) {
if ( rhs - > varId ( ) = = varid1 ) {
variables . use ( varid1 , tok ) ;
break ;
}
}
2011-08-19 20:35:25 +02:00
variables . clearAliases ( varid1 ) ;
2017-07-09 11:19:00 +02:00
} else {
2011-08-19 20:35:25 +02:00
// no other assignment in this scope
2011-12-18 20:15:41 +01:00
if ( var1 - > _assignments . find ( scope ) = = var1 - > _assignments . end ( ) ) {
2011-08-19 20:35:25 +02:00
/**
* @ todo determine if existing aliases should be discarded
*/
}
// this assignment replaces the last assignment in this scope
2011-10-13 20:53:06 +02:00
else {
2011-08-19 20:35:25 +02:00
// aliased variables in a larger scope are not supported
// remove all aliases
variables . clearAliases ( varid1 ) ;
}
}
}
}
2012-02-29 20:57:48 +01:00
} else
tok = tokOld ;
2011-08-19 20:35:25 +02:00
var1 - > _assignments . insert ( scope ) ;
}
// check for alias to struct member
// char c[10]; a.b = c;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok - > tokAt ( - 2 ) , " %name% . " ) ) {
2017-09-26 23:51:04 +02:00
const Token * rhsVarTok = tok - > tokAt ( 2 ) ;
if ( rhsVarTok & & rhsVarTok - > varId ( ) ) {
const unsigned int varid2 = rhsVarTok - > varId ( ) ;
2015-06-03 17:17:53 +02:00
const Variables : : VariableUsage * var2 = variables . find ( varid2 ) ;
2011-08-19 20:35:25 +02:00
// struct member aliased to local variable
2018-06-16 20:25:54 +02:00
if ( var2 & & ( var2 - > mType = = Variables : : array | |
var2 - > mType = = Variables : : pointer ) ) {
2011-08-19 20:35:25 +02:00
// erase aliased variable and all variables that alias it
// to prevent false positives
variables . eraseAll ( varid2 ) ;
}
}
}
2013-03-20 06:38:53 +01:00
// Possible pointer alias
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok , " %name% = %name% ; " ) ) {
2013-03-20 06:38:53 +01:00
const unsigned int varid2 = tok - > tokAt ( 2 ) - > varId ( ) ;
2015-06-03 17:17:53 +02:00
const Variables : : VariableUsage * var2 = variables . find ( varid2 ) ;
2018-06-16 20:25:54 +02:00
if ( var2 & & ( var2 - > mType = = Variables : : array | |
var2 - > mType = = Variables : : pointer ) ) {
2013-03-20 06:38:53 +01:00
variables . use ( varid2 , tok ) ;
}
}
2012-02-29 20:57:48 +01:00
return tok ;
2011-08-19 20:35:25 +02:00
}
2011-12-18 20:15:41 +01:00
static bool isPartOfClassStructUnion ( const Token * tok )
{
for ( ; tok ; tok = tok - > previous ( ) ) {
if ( tok - > str ( ) = = " } " | | tok - > str ( ) = = " ) " )
tok = tok - > link ( ) ;
else if ( tok - > str ( ) = = " ( " )
2013-08-07 16:27:37 +02:00
return ( false ) ;
2011-12-18 20:15:41 +01:00
else if ( tok - > str ( ) = = " { " ) {
2013-08-07 16:27:37 +02:00
return ( tok - > strAt ( - 1 ) = = " struct " | | tok - > strAt ( - 2 ) = = " struct " | | tok - > strAt ( - 1 ) = = " class " | | tok - > strAt ( - 2 ) = = " class " | | tok - > strAt ( - 1 ) = = " union " | | tok - > strAt ( - 2 ) = = " union " ) ;
2011-12-18 20:15:41 +01:00
}
}
return false ;
}
2020-04-10 11:53:32 +02:00
static bool isVarDecl ( const Token * tok )
{
return tok & & tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok ;
}
2011-12-26 08:12:23 +01:00
// Skip [ .. ]
static const Token * skipBrackets ( const Token * tok )
{
while ( tok & & tok - > str ( ) = = " [ " )
tok = tok - > link ( ) - > next ( ) ;
return tok ;
}
2012-02-26 08:29:02 +01:00
// Skip [ .. ] . x
static const Token * skipBracketsAndMembers ( const Token * tok )
{
while ( tok ) {
if ( tok - > str ( ) = = " [ " )
tok = tok - > link ( ) - > next ( ) ;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok , " . %name% " ) )
2012-02-26 08:29:02 +01:00
tok = tok - > tokAt ( 2 ) ;
else
break ;
}
return tok ;
}
2017-02-28 22:04:05 +01:00
static void useFunctionArgs ( const Token * tok , Variables & variables )
{
// TODO: Match function args to see if they are const or not. Assume that const data is not written.
if ( ! tok )
return ;
2017-07-01 22:45:51 +02:00
if ( tok - > str ( ) = = " , " ) {
useFunctionArgs ( tok - > astOperand1 ( ) , variables ) ;
useFunctionArgs ( tok - > astOperand2 ( ) , variables ) ;
} else if ( Token : : Match ( tok , " [+:] " ) & & ( ! tok - > valueType ( ) | | tok - > valueType ( ) - > pointer ) ) {
2017-02-28 22:04:05 +01:00
useFunctionArgs ( tok - > astOperand1 ( ) , variables ) ;
useFunctionArgs ( tok - > astOperand2 ( ) , variables ) ;
} else if ( tok - > variable ( ) & & tok - > variable ( ) - > isArray ( ) ) {
variables . use ( tok - > varId ( ) , tok ) ;
}
}
2012-02-26 08:29:02 +01:00
2011-08-19 20:35:25 +02:00
//---------------------------------------------------------------------------
// Usage of function variables
//---------------------------------------------------------------------------
2018-12-17 17:48:45 +01:00
void CheckUnusedVar : : checkFunctionVariableUsage_iterateScopes ( const Scope * const scope , Variables & variables )
2011-08-19 20:35:25 +02:00
{
2012-02-25 12:56:33 +01:00
// Find declarations if the scope is executable..
2012-09-04 14:53:24 +02:00
if ( scope - > isExecutable ( ) ) {
2012-02-25 12:56:33 +01:00
// Find declarations
for ( std : : list < Variable > : : const_iterator i = scope - > varlist . begin ( ) ; i ! = scope - > varlist . end ( ) ; + + i ) {
2012-06-28 17:22:56 +02:00
if ( i - > isThrow ( ) | | i - > isExtern ( ) )
2012-03-26 21:19:42 +02:00
continue ;
2012-02-25 12:56:33 +01:00
Variables : : VariableType type = Variables : : none ;
if ( i - > isArray ( ) & & ( i - > nameToken ( ) - > previous ( ) - > str ( ) = = " * " | | i - > nameToken ( ) - > strAt ( - 2 ) = = " * " ) )
type = Variables : : pointerArray ;
else if ( i - > isArray ( ) & & i - > nameToken ( ) - > previous ( ) - > str ( ) = = " & " )
type = Variables : : referenceArray ;
else if ( i - > isArray ( ) )
2017-06-03 15:31:29 +02:00
type = ( i - > dimensions ( ) . size ( ) = = 1U ) ? Variables : : array : Variables : : pointerArray ;
2012-02-25 12:56:33 +01:00
else if ( i - > isReference ( ) )
type = Variables : : reference ;
else if ( i - > nameToken ( ) - > previous ( ) - > str ( ) = = " * " & & i - > nameToken ( ) - > strAt ( - 2 ) = = " * " )
type = Variables : : pointerPointer ;
2015-08-27 23:56:26 +02:00
else if ( i - > isPointerToArray ( ) )
type = Variables : : pointerPointer ;
2012-02-25 12:56:33 +01:00
else if ( i - > isPointer ( ) )
type = Variables : : pointer ;
2018-06-16 16:10:28 +02:00
else if ( mTokenizer - > isC ( ) | |
2013-01-07 20:28:43 +01:00
i - > typeEndToken ( ) - > isStandardType ( ) | |
2013-03-05 15:28:40 +01:00
isRecordTypeWithoutSideEffects ( i - > type ( ) ) | |
2014-01-30 05:26:48 +01:00
( i - > isStlType ( ) & &
2014-12-07 15:32:09 +01:00
! Token : : Match ( i - > typeStartToken ( ) - > tokAt ( 2 ) , " lock_guard|unique_lock|shared_ptr|unique_ptr|auto_ptr|shared_lock " ) ) )
2012-02-25 12:56:33 +01:00
type = Variables : : standard ;
if ( type = = Variables : : none | | isPartOfClassStructUnion ( i - > typeStartToken ( ) ) )
continue ;
const Token * defValTok = i - > nameToken ( ) - > next ( ) ;
2016-01-30 20:59:32 +01:00
if ( Token : : Match ( i - > nameToken ( ) - > previous ( ) , " * %var% ) ( " ) ) // function pointer. Jump behind parameter list.
defValTok = defValTok - > linkAt ( 1 ) - > next ( ) ;
2012-02-25 12:56:33 +01:00
for ( ; defValTok ; defValTok = defValTok - > next ( ) ) {
if ( defValTok - > str ( ) = = " [ " )
defValTok = defValTok - > link ( ) ;
2015-10-27 14:46:58 +01:00
else if ( defValTok - > str ( ) = = " ( " | | defValTok - > str ( ) = = " { " || defValTok->str() == " = " || defValTok->str() == " : " ) {
2012-09-04 14:53:24 +02:00
variables . addVar ( & * i , type , true ) ;
2012-02-25 12:56:33 +01:00
break ;
} else if ( defValTok - > str ( ) = = " ; " || defValTok->str() == " , " || defValTok->str() == " ) " ) {
2012-09-04 14:53:24 +02:00
variables . addVar ( & * i , type , i - > isStatic ( ) ) ;
2012-02-25 12:56:33 +01:00
break ;
}
2011-08-19 20:35:25 +02:00
}
2012-03-20 19:00:16 +01:00
if ( i - > isArray ( ) & & i - > isClass ( ) ) // Array of class/struct members. Initialized by ctor.
2013-07-20 12:31:04 +02:00
variables . write ( i - > declarationId ( ) , i - > nameToken ( ) ) ;
2015-01-31 10:50:39 +01:00
if ( i - > isArray ( ) & & Token : : Match ( i - > nameToken ( ) , " %name% [ %var% ] " ) ) // Array index variable read.
2012-09-04 14:53:24 +02:00
variables . read ( i - > nameToken ( ) - > tokAt ( 2 ) - > varId ( ) , i - > nameToken ( ) ) ;
2012-02-25 12:56:33 +01:00
2017-01-22 10:16:40 +01:00
if ( defValTok & & defValTok - > next ( ) ) {
// simple assignment "var = 123"
if ( defValTok - > str ( ) = = " = " & & defValTok - > next ( ) - > str ( ) ! = " { " ) {
2012-02-25 12:56:33 +01:00
doAssignment ( variables , i - > nameToken ( ) , false , scope ) ;
2017-01-22 10:16:40 +01:00
} else {
// could be "var = {...}" OR "var{...}" (since C++11)
2017-07-23 23:32:14 +02:00
const Token * tokBraceStart = nullptr ;
if ( Token : : simpleMatch ( defValTok , " = { " ) ) {
2017-01-22 10:16:40 +01:00
// "var = {...}"
tokBraceStart = defValTok - > next ( ) ;
} else if ( defValTok - > str ( ) = = " { " ) {
// "var{...}"
tokBraceStart = defValTok ;
}
if ( tokBraceStart ) {
for ( const Token * tok = tokBraceStart - > next ( ) ; tok & & tok ! = tokBraceStart - > link ( ) ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) ) {
// Variables used to initialize the array read.
variables . read ( tok - > varId ( ) , i - > nameToken ( ) ) ;
}
}
}
}
2014-08-05 15:33:57 +02:00
}
2011-12-18 20:15:41 +01:00
}
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
// Check variable usage
2014-08-03 19:13:37 +02:00
const Token * tok ;
if ( scope - > type = = Scope : : eFunction )
2018-04-27 22:36:30 +02:00
tok = scope - > bodyStart - > next ( ) ;
2014-08-03 19:13:37 +02:00
else
tok = scope - > classDef - > next ( ) ;
2018-04-27 22:36:30 +02:00
for ( ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " { " & & tok ! = scope - > bodyStart & & ! tok - > previous ( ) - > varId ( ) ) {
2018-12-17 17:27:45 +01:00
for ( const Scope * i : scope - > nestedList ) {
if ( i - > bodyStart = = tok ) { // Find associated scope
2018-12-17 17:48:45 +01:00
checkFunctionVariableUsage_iterateScopes ( tok - > scope ( ) , variables ) ; // Scan child scope
2011-12-18 20:15:41 +01:00
tok = tok - > link ( ) ;
break ;
}
2011-08-19 20:35:25 +02:00
}
2011-12-18 20:15:41 +01:00
if ( ! tok )
break ;
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
if ( Token : : Match ( tok , " asm ( %str% ) " ) ) {
variables . clear ( ) ;
break ;
}
2016-01-31 12:39:43 +01:00
// templates
2018-03-29 22:00:04 +02:00
if ( tok - > isName ( ) & & endsWith ( tok - > str ( ) , ' > ' ) ) {
2016-01-31 09:11:52 +01:00
// TODO: This is a quick fix to handle when constants are used
// as template parameters. Try to handle this better, perhaps
// only remove constants.
2016-01-30 16:49:39 +01:00
variables . clear ( ) ;
}
2013-03-28 06:44:37 +01:00
2014-07-02 00:17:35 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " [ ; { } ] " )) {
2012-07-24 20:47:29 +02:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) ) {
2016-01-31 12:39:43 +01:00
// Is this a variable declaration?
2013-02-06 06:39:58 +01:00
const Variable * var = tok2 - > variable ( ) ;
2016-01-31 12:39:43 +01:00
if ( ! var | | var - > nameToken ( ) ! = tok2 )
continue ;
// Mark template parameters used in declaration as use..
if ( tok2 - > strAt ( - 1 ) = = " > " ) {
for ( const Token * tok3 = tok ; tok3 ! = tok2 ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > varId ( ) > 0U )
variables . use ( tok3 - > varId ( ) , tok3 ) ;
}
2012-07-24 20:47:29 +02:00
}
2016-01-31 12:39:43 +01:00
// Skip variable declaration..
tok = tok2 - > next ( ) ;
if ( Token : : Match ( tok , " ( %name% ) " ) ) // Simple initialization through copy ctor
tok = tok - > next ( ) ;
else if ( Token : : Match ( tok , " = %var% ; " ) ) { // Simple initialization
tok = tok - > next ( ) ;
if ( ! var - > isReference ( ) )
variables . read ( tok - > varId ( ) , tok ) ;
2017-07-23 23:32:14 +02:00
} else if ( tok - > str ( ) = = " [ " & & Token : : simpleMatch ( skipBrackets ( tok ) , " = { " )) {
const Token * const rhs1 = skipBrackets ( tok ) - > next ( ) ;
for ( const Token * rhs = rhs1 - > link ( ) ; rhs ! = rhs1 ; rhs = rhs - > previous ( ) ) {
if ( rhs - > varId ( ) )
variables . readAll ( rhs - > varId ( ) , rhs ) ;
}
2016-01-31 12:39:43 +01:00
} else if ( var - > typeEndToken ( ) - > str ( ) = = " > " ) // Be careful with types like std::vector
tok = tok - > previous ( ) ;
break ;
2012-07-24 20:47:29 +02:00
} else if ( Token : : Match ( tok2 , " [;({=] " ) )
2012-02-19 15:25:46 +01:00
break ;
}
2011-12-18 20:15:41 +01:00
}
// Freeing memory (not considered "using" the pointer if it was also allocated in this function)
if ( Token : : Match ( tok , " free|g_free|kfree|vfree ( %var% ) " ) | |
2018-06-16 16:10:28 +02:00
( mTokenizer - > isCPP ( ) & & ( Token : : Match ( tok , " delete %var% ; " ) | | Token : : Match ( tok , " delete [ ] %var% ; " ) ) ) ) {
2011-12-18 20:15:41 +01:00
unsigned int varid = 0 ;
if ( tok - > str ( ) ! = " delete " ) {
2017-09-26 23:51:04 +02:00
const Token * varTok = tok - > tokAt ( 2 ) ;
varid = varTok - > varId ( ) ;
tok = varTok - > next ( ) ;
2011-12-18 20:15:41 +01:00
} else if ( tok - > strAt ( 1 ) = = " [ " ) {
2017-09-26 23:51:04 +02:00
const Token * varTok = tok - > tokAt ( 3 ) ;
varid = varTok - > varId ( ) ;
tok = varTok ;
2011-12-18 20:15:41 +01:00
} else {
varid = tok - > next ( ) - > varId ( ) ;
2011-08-19 20:35:25 +02:00
tok = tok - > next ( ) ;
}
2018-09-23 20:24:51 +02:00
const Variables : : VariableUsage * const var = variables . find ( varid ) ;
2018-11-14 17:04:14 +01:00
if ( var ) {
if ( ! var - > _aliases . empty ( ) )
variables . use ( varid , tok ) ;
else if ( ! var - > _allocateMemory )
variables . readAll ( varid , tok ) ;
2011-08-19 20:35:25 +02:00
}
2011-12-18 20:15:41 +01:00
}
2011-08-19 20:35:25 +02:00
2012-03-20 19:00:16 +01:00
else if ( Token : : Match ( tok , " return|throw " ) ) {
2012-01-08 08:44:18 +01:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) )
2012-09-04 14:53:24 +02:00
variables . readAll ( tok2 - > varId ( ) , tok ) ;
2012-01-08 08:44:18 +01:00
else if ( tok2 - > str ( ) = = " ; " )
break ;
}
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
// assignment
2015-12-31 01:15:49 +01:00
else if ( Token : : Match ( tok , " *| ++|--| %name% ++|--| %assign% " ) | |
Token : : Match ( tok , " *| ( const| %type% *| ) %name% %assign% " ) ) {
2011-12-18 20:15:41 +01:00
bool dereference = false ;
bool pre = false ;
bool post = false ;
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
if ( tok - > str ( ) = = " * " ) {
dereference = true ;
2011-08-19 20:35:25 +02:00
tok = tok - > next ( ) ;
}
2015-12-31 01:15:49 +01:00
if ( Token : : Match ( tok , " ( const| %type% *| ) %name% %assign% " ) )
2011-12-18 20:15:41 +01:00
tok = tok - > link ( ) - > next ( ) ;
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
else if ( tok - > str ( ) = = " ( " )
2011-08-19 20:35:25 +02:00
tok = tok - > next ( ) ;
2015-08-14 20:46:13 +02:00
if ( tok - > tokType ( ) = = Token : : eIncDecOp ) {
2011-12-18 20:15:41 +01:00
pre = true ;
2011-08-19 20:35:25 +02:00
tok = tok - > next ( ) ;
}
2015-08-14 20:46:13 +02:00
if ( tok - > next ( ) - > tokType ( ) = = Token : : eIncDecOp )
2011-12-18 20:15:41 +01:00
post = true ;
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
const unsigned int varid1 = tok - > varId ( ) ;
2012-11-15 08:36:43 +01:00
const Token * const start = tok ;
2011-08-19 20:35:25 +02:00
2016-01-01 12:14:18 +01:00
// assignment in while head..
bool inwhile = false ;
{
const Token * parent = tok - > astParent ( ) ;
while ( parent ) {
2017-08-24 17:10:33 +02:00
if ( Token : : simpleMatch ( parent - > previous ( ) , " while ( " ) ) {
2016-01-01 12:14:18 +01:00
inwhile = true ;
2017-08-24 17:10:33 +02:00
break ;
}
2016-01-01 12:14:18 +01:00
parent = parent - > astParent ( ) ;
}
}
2012-02-29 20:57:48 +01:00
tok = doAssignment ( variables , tok , dereference , scope ) ;
2011-08-19 20:35:25 +02:00
2015-12-31 01:15:49 +01:00
if ( tok & & tok - > isAssignmentOp ( ) & & tok - > str ( ) ! = " = " ) {
variables . use ( varid1 , tok ) ;
2015-12-31 15:30:33 +01:00
if ( Token : : Match ( tok , " %assign% %name% " ) ) {
2015-12-31 01:15:49 +01:00
tok = tok - > next ( ) ;
2015-12-31 15:30:33 +01:00
variables . read ( tok - > varId ( ) , tok ) ;
}
2015-12-31 01:15:49 +01:00
}
2011-12-18 20:15:41 +01:00
if ( pre | | post )
2012-09-04 14:53:24 +02:00
variables . use ( varid1 , tok ) ;
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
if ( dereference ) {
2018-09-23 20:24:51 +02:00
const Variables : : VariableUsage * const var = variables . find ( varid1 ) ;
2018-06-16 20:25:54 +02:00
if ( var & & var - > mType = = Variables : : array )
2012-09-04 14:53:24 +02:00
variables . write ( varid1 , tok ) ;
variables . writeAliases ( varid1 , tok ) ;
variables . read ( varid1 , tok ) ;
2011-12-18 20:15:41 +01:00
} else {
2018-09-23 20:24:51 +02:00
const Variables : : VariableUsage * const var = variables . find ( varid1 ) ;
2016-01-01 12:14:18 +01:00
if ( var & & ( inwhile | | start - > strAt ( - 1 ) = = " , " ) ) {
2012-11-15 08:36:43 +01:00
variables . use ( varid1 , tok ) ;
2018-06-16 20:25:54 +02:00
} else if ( var & & var - > mType = = Variables : : reference ) {
2012-09-04 14:53:24 +02:00
variables . writeAliases ( varid1 , tok ) ;
variables . read ( varid1 , tok ) ;
2011-08-19 20:35:25 +02:00
}
2011-12-18 20:15:41 +01:00
// Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
2018-06-16 20:25:54 +02:00
else if ( var & & var - > mType = = Variables : : pointer & &
2015-01-31 10:50:39 +01:00
Token : : Match ( start , " %name% = new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf " ) ) {
2011-12-18 20:15:41 +01:00
bool allocate = true ;
if ( start - > strAt ( 2 ) = = " new " ) {
const Token * type = start - > tokAt ( 3 ) ;
// skip nothrow
2018-06-16 16:10:28 +02:00
if ( mTokenizer - > isCPP ( ) & & ( Token : : simpleMatch ( type , " ( nothrow ) " ) | |
2015-07-01 07:50:13 +02:00
Token : : simpleMatch ( type , " ( std :: nothrow ) " ) ) )
2011-12-18 20:15:41 +01:00
type = type - > link ( ) - > next ( ) ;
// is it a user defined type?
if ( ! type - > isStandardType ( ) ) {
2013-02-06 06:39:58 +01:00
const Variable * variable = start - > variable ( ) ;
2013-03-05 15:28:40 +01:00
if ( ! variable | | ! isRecordTypeWithoutSideEffects ( variable - > type ( ) ) )
2011-12-18 20:15:41 +01:00
allocate = false ;
}
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
if ( allocate )
2012-09-04 14:53:24 +02:00
variables . allocateMemory ( varid1 , tok ) ;
2011-12-18 20:15:41 +01:00
else
2012-09-04 14:53:24 +02:00
variables . write ( varid1 , tok ) ;
2011-12-18 20:15:41 +01:00
} else if ( varid1 & & Token : : Match ( tok , " %varid% . " , varid1 ) ) {
2017-08-01 14:56:53 +02:00
variables . read ( varid1 , tok ) ;
variables . write ( varid1 , start ) ;
2016-10-18 21:44:02 +02:00
} else if ( var & &
2018-06-16 20:25:54 +02:00
var - > mType = = Variables : : pointer & &
2016-10-18 21:44:02 +02:00
Token : : Match ( tok , " %name% ; " ) & &
tok - > varId ( ) = = 0 & &
tok - > hasKnownIntValue ( ) & &
2017-03-27 18:48:34 +02:00
tok - > values ( ) . front ( ) . intvalue = = 0 ) {
2016-10-18 21:44:02 +02:00
variables . use ( varid1 , tok ) ;
2011-10-13 20:53:06 +02:00
} else {
2012-09-04 14:53:24 +02:00
variables . write ( varid1 , tok ) ;
2011-08-19 20:35:25 +02:00
}
2015-12-31 01:15:49 +01:00
}
2011-08-19 20:35:25 +02:00
2018-09-23 20:24:51 +02:00
const Variables : : VariableUsage * const var2 = variables . find ( tok - > varId ( ) ) ;
2015-12-31 01:15:49 +01:00
if ( var2 ) {
2018-06-16 20:25:54 +02:00
if ( var2 - > mType = = Variables : : reference ) {
2015-12-31 01:15:49 +01:00
variables . writeAliases ( tok - > varId ( ) , tok ) ;
variables . read ( tok - > varId ( ) , tok ) ;
} else if ( tok - > varId ( ) ! = varid1 & & Token : : Match ( tok , " %name% .|[ " ) )
variables . read ( tok - > varId ( ) , tok ) ;
else if ( tok - > varId ( ) ! = varid1 & &
2018-06-16 20:25:54 +02:00
var2 - > mType = = Variables : : standard & &
2015-12-31 01:15:49 +01:00
tok - > strAt ( - 1 ) ! = " & " )
variables . use ( tok - > varId ( ) , tok ) ;
2011-08-19 20:35:25 +02:00
}
2012-02-26 08:29:02 +01:00
const Token * const equal = skipBracketsAndMembers ( tok - > next ( ) ) ;
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
// checked for chained assignments
2011-12-26 08:12:23 +01:00
if ( tok ! = start & & equal & & equal - > str ( ) = = " = " ) {
2015-06-04 17:45:12 +02:00
const unsigned int varId = tok - > varId ( ) ;
2018-09-23 20:24:51 +02:00
const Variables : : VariableUsage * const var = variables . find ( varId ) ;
2011-08-19 20:35:25 +02:00
2018-06-16 20:25:54 +02:00
if ( var & & var - > mType ! = Variables : : reference ) {
2012-12-04 21:39:51 +01:00
variables . read ( varId , tok ) ;
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
tok = tok - > previous ( ) ;
}
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
// assignment
2015-01-31 10:50:39 +01:00
else if ( ( Token : : Match ( tok , " %name% [ " ) & & Token : : simpleMatch ( skipBracketsAndMembers ( tok - > next ( ) ) , " = " ) ) | |
2012-08-25 13:07:33 +02:00
( Token : : simpleMatch ( tok , " * ( " ) & & Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) = " ) ) ) {
2016-05-17 16:03:55 +02:00
const Token * eq = tok ;
while ( eq & & ! eq - > isAssignmentOp ( ) )
eq = eq - > astParent ( ) ;
const bool deref = eq & & eq - > astOperand1 ( ) & & eq - > astOperand1 ( ) - > valueType ( ) & & eq - > astOperand1 ( ) - > valueType ( ) - > pointer = = 0U ;
2012-08-25 13:07:33 +02:00
if ( tok - > str ( ) = = " * " ) {
tok = tok - > tokAt ( 2 ) ;
if ( tok - > str ( ) = = " ( " )
tok = tok - > link ( ) - > next ( ) ;
}
2015-06-04 17:45:12 +02:00
const unsigned int varid = tok - > varId ( ) ;
2011-12-18 20:15:41 +01:00
const Variables : : VariableUsage * var = variables . find ( varid ) ;
if ( var ) {
// Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
2018-06-16 20:25:54 +02:00
if ( var - > mType = = Variables : : pointer & &
2012-02-29 20:57:48 +01:00
Token : : Match ( skipBrackets ( tok - > next ( ) ) , " = new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf " ) ) {
2012-09-04 14:53:24 +02:00
variables . allocateMemory ( varid , tok ) ;
2018-06-16 20:25:54 +02:00
} else if ( var - > mType = = Variables : : pointer | | var - > mType = = Variables : : reference ) {
2012-09-04 14:53:24 +02:00
variables . read ( varid , tok ) ;
variables . writeAliases ( varid , tok ) ;
2018-06-16 20:25:54 +02:00
} else if ( var - > mType = = Variables : : pointerArray ) {
2016-05-17 16:03:55 +02:00
tok = doAssignment ( variables , tok , deref , scope ) ;
2011-12-18 20:15:41 +01:00
} else
2012-09-04 14:53:24 +02:00
variables . writeAll ( varid , tok ) ;
2011-12-18 20:15:41 +01:00
}
}
2011-08-19 20:35:25 +02:00
2018-06-16 16:10:28 +02:00
else if ( mTokenizer - > isCPP ( ) & & Token : : Match ( tok , " [ ; { } ] % var % < < " )) {
2012-12-18 19:02:30 +01:00
variables . erase ( tok - > next ( ) - > varId ( ) ) ;
}
2012-03-20 19:00:16 +01:00
else if ( Token : : Match ( tok , " & %var% " ) ) {
2014-07-02 00:17:35 +02:00
if ( tok - > astOperand2 ( ) ) { // bitop
2012-09-04 14:53:24 +02:00
variables . read ( tok - > next ( ) - > varId ( ) , tok ) ;
2012-03-20 19:00:16 +01:00
} else // addressof
2012-09-04 14:53:24 +02:00
variables . use ( tok - > next ( ) - > varId ( ) , tok ) ; // use = read + write
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " >>|>>= %name% " ) ) {
2018-06-16 16:10:28 +02:00
if ( isLikelyStreamRead ( mTokenizer - > isCPP ( ) , tok ) )
2013-08-27 15:46:51 +02:00
variables . use ( tok - > next ( ) - > varId ( ) , tok ) ; // use = read + write
2018-04-22 17:08:23 +02:00
else
variables . read ( tok - > next ( ) - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
} else if ( Token : : Match ( tok , " %var% >>|& " ) & & Token : : Match ( tok - > previous ( ) , " [ { } ; : ] " )) {
2012-09-04 14:53:24 +02:00
variables . read ( tok - > varId ( ) , tok ) ;
2018-06-16 16:10:28 +02:00
} else if ( isLikelyStreamRead ( mTokenizer - > isCPP ( ) , tok - > previous ( ) ) ) {
2018-04-17 14:23:04 +02:00
variables . use ( tok - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-12-18 20:15:41 +01:00
// function parameter
2012-09-04 13:06:04 +02:00
else if ( Token : : Match ( tok , " [(,] %var% [ " ) ) {
2012-09-04 14:53:24 +02:00
variables . use ( tok - > next ( ) - > varId ( ) , tok ) ; // use = read + write
2012-09-04 13:06:04 +02:00
} else if ( Token : : Match ( tok , " [(,] %var% [,) ] " ) && tok->previous()->str() != " * " ) {
2012-09-04 14:53:24 +02:00
variables . use ( tok - > next ( ) - > varId ( ) , tok ) ; // use = read + write
2016-09-03 20:38:36 +02:00
} else if ( Token : : Match ( tok , " [(,] & %var% [,) ] " )) {
variables . eraseAll ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
2011-12-18 20:15:41 +01:00
} else if ( Token : : Match ( tok , " [(,] ( " ) & &
2012-09-04 13:06:04 +02:00
Token : : Match ( tok - > next ( ) - > link ( ) , " ) %var% [,)] " ) ) {
2012-09-04 14:53:24 +02:00
variables . use ( tok - > next ( ) - > link ( ) - > next ( ) - > varId ( ) , tok ) ; // use = read + write
2016-08-02 08:50:04 +02:00
} else if ( Token : : Match ( tok , " [(,] *| %var% = " ) ) {
tok = tok - > next ( ) ;
if ( tok - > str ( ) = = " * " )
tok = tok - > next ( ) ;
variables . use ( tok - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-12-18 20:15:41 +01:00
// function
2017-02-28 22:04:05 +01:00
else if ( Token : : Match ( tok , " %name% ( " ) ) {
2012-09-04 14:53:24 +02:00
variables . read ( tok - > varId ( ) , tok ) ;
2017-02-28 22:04:05 +01:00
useFunctionArgs ( tok - > next ( ) - > astOperand2 ( ) , variables ) ;
2016-09-03 20:38:36 +02:00
} else if ( Token : : Match ( tok , " std :: ref ( %var% ) " )) {
variables . eraseAll ( tok - > tokAt ( 4 ) - > varId ( ) ) ;
2011-12-18 20:15:41 +01:00
}
2011-08-19 20:35:25 +02:00
2014-09-30 12:39:27 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " [ { , ] % var % [ , } ] " )) {
variables . read ( tok - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-08-19 20:35:25 +02:00
2014-07-02 00:17:35 +02:00
else if ( tok - > varId ( ) & & Token : : Match ( tok , " %var% . " ) ) {
2012-09-04 14:53:24 +02:00
variables . use ( tok - > varId ( ) , tok ) ; // use = read + write
2012-09-04 13:06:04 +02:00
}
2011-08-19 20:35:25 +02:00
2017-07-01 22:45:51 +02:00
else if ( tok - > str ( ) = = " : " & & ( ! tok - > valueType ( ) | | tok - > valueType ( ) - > pointer ) ) {
2017-07-01 11:31:51 +02:00
if ( tok - > astOperand1 ( ) )
variables . use ( tok - > astOperand1 ( ) - > varId ( ) , tok - > astOperand1 ( ) ) ;
if ( tok - > astOperand2 ( ) )
variables . use ( tok - > astOperand2 ( ) - > varId ( ) , tok - > astOperand2 ( ) ) ;
}
2020-04-10 11:53:32 +02:00
else if ( tok - > isExtendedOp ( ) & & tok - > next ( ) & & tok - > next ( ) - > varId ( ) & & tok - > strAt ( 2 ) ! = " = " & & ! isVarDecl ( tok - > next ( ) ) ) {
2012-09-04 14:53:24 +02:00
variables . readAll ( tok - > next ( ) - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-08-19 20:35:25 +02:00
2020-04-10 11:53:32 +02:00
else if ( tok - > varId ( ) & & ! isVarDecl ( tok ) & & tok - > next ( ) & & ( tok - > next ( ) - > str ( ) = = " ) " | | tok - > next ( ) - > isExtendedOp ( ) ) ) {
2016-01-01 16:04:13 +01:00
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " %name% ( %var% [,)] " ) & &
! ( tok - > tokAt ( - 2 ) - > variable ( ) & & tok - > tokAt ( - 2 ) - > variable ( ) - > isReference ( ) ) )
variables . use ( tok - > varId ( ) , tok ) ;
else
variables . readAll ( tok - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-08-19 20:35:25 +02:00
2012-09-04 13:06:04 +02:00
else if ( Token : : Match ( tok , " %var% ; " ) & & Token : : Match ( tok - > previous ( ) , " [;{}:] " ) ) {
2012-09-04 14:53:24 +02:00
variables . readAll ( tok - > varId ( ) , tok ) ;
2012-09-04 13:06:04 +02:00
}
2011-08-19 20:35:25 +02:00
2014-07-02 00:17:35 +02:00
// ++|--
2015-08-14 20:46:13 +02:00
else if ( tok - > next ( ) & & tok - > next ( ) - > tokType ( ) = = Token : : eIncDecOp & & tok - > next ( ) - > astOperand1 ( ) & & tok - > next ( ) - > astOperand1 ( ) - > varId ( ) ) {
2014-07-02 00:17:35 +02:00
if ( tok - > next ( ) - > astParent ( ) )
variables . use ( tok - > next ( ) - > astOperand1 ( ) - > varId ( ) , tok ) ;
2011-12-18 20:15:41 +01:00
else
2014-07-02 00:17:35 +02:00
variables . modified ( tok - > next ( ) - > astOperand1 ( ) - > varId ( ) , tok ) ;
2011-12-18 20:15:41 +01:00
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
else if ( tok - > isAssignmentOp ( ) ) {
for ( const Token * tok2 = tok - > next ( ) ; tok2 & & tok2 - > str ( ) ! = " ; " ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) ) {
2014-08-31 19:46:30 +02:00
if ( tok2 - > strAt ( 1 ) = = " = " )
2012-09-04 14:53:24 +02:00
variables . write ( tok2 - > varId ( ) , tok ) ;
2015-06-04 17:45:12 +02:00
else if ( tok2 - > next ( ) & & tok2 - > next ( ) - > isAssignmentOp ( ) )
2014-08-31 19:46:30 +02:00
variables . use ( tok2 - > varId ( ) , tok ) ;
2012-08-10 12:36:08 +02:00
else
2012-09-04 14:53:24 +02:00
variables . read ( tok2 - > varId ( ) , tok ) ;
2011-08-19 20:35:25 +02:00
}
}
2020-10-25 07:12:30 +01:00
} else if ( tok - > variable ( ) & & tok - > variable ( ) - > isClass ( ) & & tok - > variable ( ) - > type ( ) & &
( tok - > variable ( ) - > type ( ) - > needInitialization = = Type : : NeedInitialization : : False ) & &
tok - > next ( ) - > str ( ) = = " ; " ) {
2020-10-25 07:11:45 +01:00
variables . write ( tok - > varId ( ) , tok ) ;
}
2011-12-18 20:15:41 +01:00
}
}
2011-08-19 20:35:25 +02:00
2011-12-18 20:15:41 +01:00
void CheckUnusedVar : : checkFunctionVariableUsage ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : STYLE ) )
2011-12-18 20:15:41 +01:00
return ;
2011-08-19 20:35:25 +02:00
2020-01-25 17:01:17 +01:00
if ( mSettings - > clang )
return ;
2011-12-18 20:15:41 +01:00
// Parse all executing scopes..
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-08-19 20:35:25 +02:00
2012-11-16 06:50:49 +01:00
// only check functions
2018-11-14 06:58:21 +01:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2016-05-26 18:07:56 +02:00
// Bailout when there are lambdas or inline functions
// TODO: Handle lambdas and inline functions properly
if ( scope - > hasInlineOrLambdaFunction ( ) )
continue ;
2018-12-13 18:52:56 +01:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2019-01-26 21:43:44 +01:00
if ( findLambdaEndToken ( tok ) )
2018-12-13 18:52:56 +01:00
// todo: handle lambdas
break ;
if ( Token : : simpleMatch ( tok , " try { " ) )
// todo: check try blocks
tok = tok - > linkAt ( 1 ) ;
2018-12-16 19:01:05 +01:00
const Token * varDecl = nullptr ;
if ( tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok ) {
const Token * eq = tok - > next ( ) ;
while ( Token : : simpleMatch ( eq , " [ " ) )
eq = eq - > link ( ) - > next ( ) ;
if ( Token : : simpleMatch ( eq , " = " ) ) {
varDecl = tok ;
tok = eq ;
}
}
2019-07-29 08:49:19 +02:00
// not assignment/initialization/increment => continue
const bool isAssignment = tok - > isAssignmentOp ( ) & & tok - > astOperand1 ( ) ;
const bool isInitialization = ( Token : : Match ( tok , " %var% ( " ) & & tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) = = tok ) ;
const bool isIncrementOrDecrement = ( tok - > tokType ( ) = = Token : : Type : : eIncDecOp ) ;
if ( ! isAssignment & & ! isInitialization & & ! isIncrementOrDecrement )
2018-12-13 18:52:56 +01:00
continue ;
2020-09-26 22:13:05 +02:00
if ( isIncrementOrDecrement & & tok - > astParent ( ) & & precedes ( tok , tok - > astOperand1 ( ) ) )
continue ;
2020-09-27 19:15:15 +02:00
if ( tok - > str ( ) = = " = " & & isRaiiClass ( tok - > valueType ( ) , mTokenizer - > isCPP ( ) , false ) )
continue ;
2018-12-13 18:52:56 +01:00
if ( tok - > isName ( ) ) {
2020-09-27 19:15:15 +02:00
if ( isRaiiClass ( tok - > valueType ( ) , mTokenizer - > isCPP ( ) , true ) )
continue ;
2018-12-13 18:52:56 +01:00
tok = tok - > next ( ) ;
}
if ( tok - > astParent ( ) & & tok - > str ( ) ! = " ( " ) {
const Token * parent = tok - > astParent ( ) ;
while ( Token : : Match ( parent , " %oror%|%comp%|!|&& " ) )
parent = parent - > astParent ( ) ;
if ( ! parent )
continue ;
if ( ! Token : : simpleMatch ( parent - > previous ( ) , " if ( " ) )
continue ;
}
// Do not warn about assignment with NULL
2020-02-09 11:16:08 +01:00
if ( isNullOperand ( tok - > astOperand2 ( ) ) )
2018-12-13 18:52:56 +01:00
continue ;
2019-07-29 19:05:36 +02:00
if ( ! tok - > astOperand1 ( ) )
continue ;
2019-08-04 10:21:16 +02:00
const Token * iteratorToken = tok - > astOperand1 ( ) ;
while ( Token : : Match ( iteratorToken , " [.*] " ) )
iteratorToken = iteratorToken - > astOperand1 ( ) ;
if ( iteratorToken & & iteratorToken - > variable ( ) & & iteratorToken - > variable ( ) - > typeEndToken ( ) - > str ( ) . find ( " iterator " ) ! = std : : string : : npos )
continue ;
2019-11-04 17:59:16 +01:00
const Token * op1tok = tok - > astOperand1 ( ) ;
while ( Token : : Match ( op1tok , " .|[|* " ) )
op1tok = op1tok - > astOperand1 ( ) ;
const Variable * op1Var = op1tok ? op1tok - > variable ( ) : nullptr ;
2019-11-17 12:08:21 +01:00
std : : string bailoutTypeName ;
2019-11-16 19:49:43 +01:00
if ( op1Var ) {
if ( op1Var - > isReference ( ) & & op1Var - > nameToken ( ) ! = tok - > astOperand1 ( ) )
// todo: check references
continue ;
2018-12-13 18:52:56 +01:00
2019-11-16 19:49:43 +01:00
if ( op1Var - > isStatic ( ) )
// todo: check static variables
continue ;
2018-12-13 18:52:56 +01:00
2019-11-16 19:49:43 +01:00
if ( op1Var - > nameToken ( ) - > isAttributeUnused ( ) )
continue ;
2020-10-02 20:22:09 +02:00
// Avoid FP for union..
if ( op1Var - > type ( ) & & op1Var - > type ( ) - > isUnionType ( ) )
continue ;
2019-11-16 19:49:43 +01:00
// Bailout for unknown template classes, we have no idea what side effects such assignments have
if ( mTokenizer - > isCPP ( ) & &
op1Var - > isClass ( ) & &
2019-11-17 12:08:21 +01:00
( ! op1Var - > valueType ( ) | | op1Var - > valueType ( ) - > type = = ValueType : : Type : : UNKNOWN_TYPE ) ) {
// Check in the library if we should bailout or not..
const std : : string typeName = op1Var - > getTypeName ( ) ;
switch ( mSettings - > library . getTypeCheck ( " unusedvar " , typeName ) ) {
case Library : : TypeCheck : : def :
bailoutTypeName = typeName ;
break ;
case Library : : TypeCheck : : check :
break ;
case Library : : TypeCheck : : suppress :
continue ;
2020-04-21 17:27:51 +02:00
}
2019-11-17 12:08:21 +01:00
}
2019-11-16 19:49:43 +01:00
}
2018-12-13 18:52:56 +01:00
// Is there a redundant assignment?
const Token * start = tok - > findExpressionStartEndTokens ( ) . second - > next ( ) ;
2018-12-16 19:01:05 +01:00
const Token * expr = varDecl ? varDecl : tok - > astOperand1 ( ) ;
2018-12-13 18:52:56 +01:00
2020-09-14 18:44:50 +02:00
if ( isInitialization )
expr = tok - > previous ( ) ;
2019-12-20 18:31:44 +01:00
// Is variable in lhs a union member?
if ( tok - > previous ( ) & & tok - > previous ( ) - > variable ( ) & & tok - > previous ( ) - > variable ( ) - > nameToken ( ) - > scope ( ) - > type = = Scope : : eUnion )
continue ;
2018-12-31 18:00:47 +01:00
FwdAnalysis fwdAnalysis ( mTokenizer - > isCPP ( ) , mSettings - > library ) ;
2019-11-17 12:08:21 +01:00
if ( fwdAnalysis . unusedValue ( expr , start , scope - > bodyEnd ) ) {
2019-11-17 15:44:57 +01:00
if ( ! bailoutTypeName . empty ( ) & & bailoutTypeName ! = " auto " ) {
2019-11-17 12:08:21 +01:00
if ( mSettings - > checkLibrary & & mSettings - > isEnabled ( Settings : : INFORMATION ) ) {
reportError ( tok ,
Severity : : information ,
" checkLibraryCheckType " ,
" --check-library: Provide <type-checks><unusedvar> configuration for " + bailoutTypeName ) ;
2019-11-17 12:33:26 +01:00
continue ;
2019-11-17 12:08:21 +01:00
}
}
2018-12-13 18:52:56 +01:00
// warn
2020-04-13 12:52:00 +02:00
if ( ! expr - > variable ( ) | | ! expr - > variable ( ) - > isMaybeUnused ( ) )
2020-04-12 20:35:54 +02:00
unreadVariableError ( tok , expr - > expressionString ( ) , false ) ;
2019-11-17 12:08:21 +01:00
}
2018-12-13 18:52:56 +01:00
}
2011-12-18 20:15:41 +01:00
// varId, usage {read, write, modified}
Variables variables ;
2011-08-19 20:35:25 +02:00
2018-12-17 17:48:45 +01:00
checkFunctionVariableUsage_iterateScopes ( scope , variables ) ;
2011-08-19 20:35:25 +02:00
// Check usage of all variables in the current scope..
2012-12-05 20:18:14 +01:00
for ( std : : map < unsigned int , Variables : : VariableUsage > : : const_iterator it = variables . varUsage ( ) . begin ( ) ;
it ! = variables . varUsage ( ) . end ( ) ;
+ + it ) {
2011-08-19 20:35:25 +02:00
const Variables : : VariableUsage & usage = it - > second ;
// variable has been marked as unused so ignore it
2014-08-06 11:13:58 +02:00
if ( usage . _var - > nameToken ( ) - > isAttributeUnused ( ) | | usage . _var - > nameToken ( ) - > isAttributeUsed ( ) )
2011-08-19 20:35:25 +02:00
continue ;
// skip things that are only partially implemented to prevent false positives
2018-06-16 20:25:54 +02:00
if ( usage . mType = = Variables : : pointerPointer | |
usage . mType = = Variables : : pointerArray | |
usage . mType = = Variables : : referenceArray )
2011-08-19 20:35:25 +02:00
continue ;
2014-09-16 11:34:16 +02:00
const std : : string & varname = usage . _var - > name ( ) ;
const Variable * var = symbolDatabase - > getVariableFromVarId ( it - > first ) ;
2011-08-19 20:35:25 +02:00
// variable has had memory allocated for it, but hasn't done
// anything with that memory other than, perhaps, freeing it
if ( usage . unused ( ) & & ! usage . _modified & & usage . _allocateMemory )
2012-09-04 14:53:24 +02:00
allocatedButUnusedVariableError ( usage . _lastAccess , varname ) ;
2011-08-19 20:35:25 +02:00
// variable has not been written, read, or modified
2020-04-12 20:35:54 +02:00
else if ( usage . unused ( ) & & ! usage . _modified ) {
if ( ! usage . _var - > isMaybeUnused ( ) ) {
unusedVariableError ( usage . _var - > nameToken ( ) , varname ) ;
}
}
2011-08-19 20:35:25 +02:00
// variable has not been written but has been modified
2015-01-14 22:43:23 +01:00
else if ( usage . _modified & & ! usage . _write & & ! usage . _allocateMemory & & var & & ! var - > isStlType ( ) )
2012-09-04 14:53:24 +02:00
unassignedVariableError ( usage . _var - > nameToken ( ) , varname ) ;
2011-08-19 20:35:25 +02:00
// variable has been read but not written
2015-01-18 13:02:09 +01:00
else if ( ! usage . _write & & ! usage . _allocateMemory & & var & & ! var - > isStlType ( ) & & ! isEmptyType ( var - > type ( ) ) )
2012-09-04 14:53:24 +02:00
unassignedVariableError ( usage . _var - > nameToken ( ) , varname ) ;
2011-08-19 20:35:25 +02:00
}
}
}
void CheckUnusedVar : : unusedVariableError ( const Token * tok , const std : : string & varname )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : style , " unusedVariable " , " $symbol: " + varname + " \n Unused variable: $symbol " , CWE563 , false ) ;
2011-08-19 20:35:25 +02:00
}
void CheckUnusedVar : : allocatedButUnusedVariableError ( const Token * tok , const std : : string & varname )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : style , " unusedAllocatedMemory " , " $symbol: " + varname + " \n Variable '$symbol' is allocated memory that is never used. " , CWE563 , false ) ;
2011-08-19 20:35:25 +02:00
}
2016-10-10 21:27:40 +02:00
void CheckUnusedVar : : unreadVariableError ( const Token * tok , const std : : string & varname , bool modified )
2011-08-19 20:35:25 +02:00
{
2016-10-10 21:27:40 +02:00
if ( modified )
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : style , " unreadVariable " , " $symbol: " + varname + " \n Variable '$symbol' is modified but its new value is never used. " , CWE563 , false ) ;
2016-10-10 21:27:40 +02:00
else
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : style , " unreadVariable " , " $symbol: " + varname + " \n Variable '$symbol' is assigned a value that is never used. " , CWE563 , false ) ;
2011-08-19 20:35:25 +02:00
}
void CheckUnusedVar : : unassignedVariableError ( const Token * tok , const std : : string & varname )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : style , " unassignedVariable " , " $symbol: " + varname + " \n Variable '$symbol' is not assigned a value. " , CWE665 , false ) ;
2011-08-19 20:35:25 +02:00
}
//---------------------------------------------------------------------------
// Check that all struct members are used
//---------------------------------------------------------------------------
void CheckUnusedVar : : checkStructMemberUsage ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : STYLE ) )
2011-08-19 20:35:25 +02:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2016-05-24 23:10:39 +02:00
2018-11-14 06:58:21 +01:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . type ! = Scope : : eStruct & & scope . type ! = Scope : : eUnion )
2011-08-19 20:35:25 +02:00
continue ;
2018-11-14 06:58:21 +01:00
if ( scope . bodyStart - > fileIndex ( ) ! = 0 | | scope . className . empty ( ) )
2016-05-25 11:13:31 +02:00
continue ;
2011-08-19 20:35:25 +02:00
2016-09-05 17:27:12 +02:00
// Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
2018-11-14 06:58:21 +01:00
if ( scope . bodyEnd - > isAttributePacked ( ) )
2016-09-05 17:27:12 +02:00
continue ;
2020-06-30 10:59:57 +02:00
if ( const Preprocessor * preprocessor = mTokenizer - > getPreprocessor ( ) ) {
bool isPacked = false ;
for ( const Directive & d : preprocessor - > getDirectives ( ) ) {
if ( d . str = = " #pragma pack(1) " & & d . file = = mTokenizer - > list . getFiles ( ) . front ( ) & & d . linenr < scope . bodyStart - > linenr ( ) ) {
isPacked = true ;
break ;
}
}
if ( isPacked )
continue ;
}
2016-09-05 17:27:12 +02:00
2016-05-25 11:13:31 +02:00
// Bail out if struct/union contains any functions
2018-11-14 06:58:21 +01:00
if ( ! scope . functionList . empty ( ) )
2016-05-25 11:13:31 +02:00
continue ;
2011-08-19 20:35:25 +02:00
2019-03-06 20:51:48 +01:00
// Bail out for template struct, members might be used in non-matching instantiations
if ( scope . className . find ( " < " ) ! = std : : string : : npos )
continue ;
2016-05-25 11:13:31 +02:00
// bail out if struct is inherited
bool bailout = false ;
2018-11-14 06:58:21 +01:00
for ( const Scope & derivedScope : symbolDatabase - > scopeList ) {
if ( derivedScope . definedType ) {
for ( const Type : : BaseInfo & derivedFrom : derivedScope . definedType - > derivedFrom ) {
if ( derivedFrom . type = = scope . definedType ) {
2016-05-25 11:13:31 +02:00
bailout = true ;
break ;
2012-11-18 15:24:47 +01:00
}
}
}
2016-05-25 11:13:31 +02:00
}
if ( bailout )
continue ;
2015-09-22 15:38:49 +02:00
2016-05-25 11:13:31 +02:00
// bail out for extern/global struct
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2018-11-14 06:58:21 +01:00
if ( var & & ( var - > isExtern ( ) | | ( var - > isGlobal ( ) & & ! var - > isStatic ( ) ) ) & & var - > typeEndToken ( ) - > str ( ) = = scope . className ) {
2016-05-25 11:13:31 +02:00
bailout = true ;
break ;
2013-01-07 19:20:15 +01:00
}
2011-08-19 20:35:25 +02:00
}
2016-05-25 11:13:31 +02:00
if ( bailout )
continue ;
2011-08-19 20:35:25 +02:00
2016-05-25 11:13:31 +02:00
// Bail out if some data is casted to struct..
2018-11-14 06:58:21 +01:00
const std : : string castPattern ( " ( struct| " + scope . className + " * ) & %name% [ " ) ;
if ( Token : : findmatch ( scope . bodyEnd , castPattern . c_str ( ) ) )
2016-05-25 11:13:31 +02:00
continue ;
2011-08-19 20:35:25 +02:00
2017-11-12 22:33:17 +01:00
// (struct S){..}
2018-11-14 06:58:21 +01:00
const std : : string initPattern ( " ( struct| " + scope . className + " ) { " ) ;
if ( Token : : findmatch ( scope . bodyEnd , initPattern . c_str ( ) ) )
2017-11-12 22:33:17 +01:00
continue ;
2016-08-02 18:50:04 +02:00
// Bail out if struct is used in sizeof..
2018-11-14 06:58:21 +01:00
for ( const Token * tok = scope . bodyEnd ; nullptr ! = ( tok = Token : : findsimplematch ( tok , " sizeof ( " ) ) ; ) {
2016-08-02 19:23:45 +02:00
tok = tok - > tokAt ( 2 ) ;
2018-11-14 06:58:21 +01:00
if ( Token : : Match ( tok , ( " struct| " + scope . className ) . c_str ( ) ) ) {
2016-08-02 18:50:04 +02:00
bailout = true ;
break ;
}
}
if ( bailout )
continue ;
2016-05-25 11:13:31 +02:00
// Try to prevent false positives when struct members are not used directly.
2018-11-14 06:58:21 +01:00
if ( Token : : findmatch ( scope . bodyEnd , ( scope . className + " %type%| * " ) . c_str ( ) ) )
2016-05-25 11:13:31 +02:00
continue ;
2015-08-15 20:24:26 +02:00
2018-11-14 06:58:21 +01:00
for ( const Variable & var : scope . varlist ) {
2016-05-25 11:13:31 +02:00
// declaring a POD member variable?
2018-11-14 06:58:21 +01:00
if ( ! var . typeStartToken ( ) - > isStandardType ( ) & & ! var . isPointer ( ) )
2011-08-19 20:35:25 +02:00
continue ;
2015-09-24 18:29:08 +02:00
// Check if the struct member variable is used anywhere in the file
2020-05-26 20:13:56 +02:00
std : : string tmp ( " . " + var . name ( ) ) ;
if ( Token : : findsimplematch ( mTokenizer - > tokens ( ) , tmp . c_str ( ) , tmp . size ( ) ) )
2015-12-16 14:51:50 +01:00
continue ;
2020-05-26 20:13:56 +02:00
tmp = ( " :: " + var . name ( ) ) ;
if ( Token : : findsimplematch ( mTokenizer - > tokens ( ) , tmp . c_str ( ) , tmp . size ( ) ) )
2020-04-08 18:09:20 +02:00
continue ;
2011-08-19 20:35:25 +02:00
2018-11-14 06:58:21 +01:00
unusedStructMemberError ( var . nameToken ( ) , scope . className , var . name ( ) , scope . type = = Scope : : eUnion ) ;
2011-08-19 20:35:25 +02:00
}
}
}
2015-09-22 16:38:23 +02:00
void CheckUnusedVar : : unusedStructMemberError ( const Token * tok , const std : : string & structname , const std : : string & varname , bool isUnion )
2011-08-19 20:35:25 +02:00
{
2018-04-09 06:43:48 +02:00
const std : : string prefix = isUnion ? " union member " : " struct member " ;
reportError ( tok , Severity : : style , " unusedStructMember " , " $symbol: " + structname + " :: " + varname + ' \n ' + prefix + " '$symbol' is never used. " , CWE563 , false ) ;
2011-08-19 20:35:25 +02:00
}
2013-04-01 12:41:14 +02:00
bool CheckUnusedVar : : isRecordTypeWithoutSideEffects ( const Type * type )
{
// a type that has no side effects (no constructors and no members with constructors)
/** @todo false negative: check constructors for side effects */
2018-06-17 19:04:06 +02:00
const std : : pair < std : : map < const Type * , bool > : : iterator , bool > found = mIsRecordTypeWithoutSideEffectsMap . insert (
2014-01-24 06:10:19 +01:00
std : : pair < const Type * , bool > ( type , false ) ) ; //Initialize with side effects for possible recursions
2020-07-23 11:10:08 +02:00
bool & withoutSideEffects = found . first - > second ;
2013-04-01 12:41:14 +02:00
if ( ! found . second )
return withoutSideEffects ;
2020-07-23 11:10:08 +02:00
// unknown types are assumed to have side effects
if ( ! type | | ! type - > classScope )
return ( withoutSideEffects = false ) ;
// Non-empty constructors => possible side effects
for ( const Function & f : type - > classScope - > functionList ) {
if ( ! f . isConstructor ( ) )
continue ;
2020-07-23 18:54:40 +02:00
if ( f . argDef & & Token : : simpleMatch ( f . argDef - > link ( ) , " ) = " ) )
2020-07-23 11:10:08 +02:00
continue ; // ignore default/deleted constructors
const bool emptyBody = ( f . functionScope & & Token : : simpleMatch ( f . functionScope - > bodyStart , " { } " ) ) ;
2020-10-25 07:11:45 +01:00
Token * nextToken = f . argDef - > link ( ) ;
if ( Token : : simpleMatch ( nextToken , " ) : " ) ) {
// validating initialization list
nextToken = nextToken - > next ( ) ; // goto ":"
2020-10-25 07:12:30 +01:00
for ( const Token * initListToken = nextToken ; Token : : Match ( initListToken , " [:,] %var% [({] " ) ; initListToken = initListToken - > linkAt ( 2 ) - > next ( ) ) {
2020-10-25 07:11:45 +01:00
const Token * varToken = initListToken - > next ( ) ;
const Variable * variable = varToken - > variable ( ) ;
2020-10-25 07:12:30 +01:00
if ( variable & & ! isVariableWithoutSideEffects ( * variable ) ) {
2020-10-25 07:11:45 +01:00
return withoutSideEffects = false ;
}
const Token * valueEnd = initListToken - > linkAt ( 2 ) ;
for ( const Token * valueToken = initListToken - > tokAt ( 3 ) ; valueToken ! = valueEnd ; valueToken = valueToken - > next ( ) ) {
const Variable * initValueVar = valueToken - > variable ( ) ;
2020-10-25 07:12:30 +01:00
if ( initValueVar & & ! isVariableWithoutSideEffects ( * initValueVar ) ) {
2020-10-25 07:11:45 +01:00
return withoutSideEffects = false ;
}
2020-11-26 17:34:42 +01:00
if ( ( valueToken - > tokType ( ) = = Token : : Type : : eName ) | |
2020-10-25 07:11:45 +01:00
( valueToken - > tokType ( ) = = Token : : Type : : eLambda ) | |
2020-10-25 07:12:30 +01:00
( valueToken - > tokType ( ) = = Token : : Type : : eOther ) ) {
2020-10-25 07:11:45 +01:00
return withoutSideEffects = false ;
}
2020-11-26 17:34:42 +01:00
const Function * initValueFunc = valueToken - > function ( ) ;
if ( initValueFunc & & ! isFunctionWithoutSideEffects ( * initValueFunc , valueToken ,
std : : list < const Function * > { } ) ) {
return withoutSideEffects = false ;
}
2020-10-25 07:11:45 +01:00
}
}
}
if ( ! emptyBody )
2020-07-23 11:10:08 +02:00
return ( withoutSideEffects = false ) ;
}
// Derived from type that has side effects?
for ( const Type : : BaseInfo & derivedFrom : type - > derivedFrom ) {
if ( ! isRecordTypeWithoutSideEffects ( derivedFrom . type ) )
return ( withoutSideEffects = false ) ;
}
// Is there a member variable with possible side effects
for ( const Variable & var : type - > classScope - > varlist ) {
2020-10-25 07:11:45 +01:00
withoutSideEffects = isVariableWithoutSideEffects ( var ) ;
2020-10-25 07:12:30 +01:00
if ( ! withoutSideEffects ) {
2020-10-25 07:11:45 +01:00
return withoutSideEffects ;
2013-04-01 12:41:14 +02:00
}
}
2020-07-23 11:10:08 +02:00
return ( withoutSideEffects = true ) ;
2013-04-01 12:41:14 +02:00
}
2014-02-01 22:40:35 +01:00
2020-10-25 07:11:45 +01:00
bool CheckUnusedVar : : isVariableWithoutSideEffects ( const Variable & var )
{
if ( var . isPointer ( ) )
return true ;
const Type * variableType = var . type ( ) ;
if ( variableType ) {
if ( ! isRecordTypeWithoutSideEffects ( variableType ) )
return false ;
} else {
ValueType : : Type valueType = var . valueType ( ) - > type ;
if ( ( valueType = = ValueType : : Type : : UNKNOWN_TYPE ) | | ( valueType = = ValueType : : Type : : NONSTD ) )
return false ;
}
return true ;
}
2014-02-01 22:40:35 +01:00
bool CheckUnusedVar : : isEmptyType ( const Type * type )
{
// a type that has no variables and no constructor
2018-06-17 19:04:06 +02:00
const std : : pair < std : : map < const Type * , bool > : : iterator , bool > found = mIsEmptyTypeMap . insert (
2014-02-01 22:40:35 +01:00
std : : pair < const Type * , bool > ( type , false ) ) ;
bool & emptyType = found . first - > second ;
if ( ! found . second )
return emptyType ;
if ( type & & type - > classScope & & type - > classScope - > numConstructors = = 0 & &
( type - > classScope - > varlist . empty ( ) ) ) {
for ( std : : vector < Type : : BaseInfo > : : const_iterator i = type - > derivedFrom . begin ( ) ; i ! = type - > derivedFrom . end ( ) ; + + i ) {
if ( ! isEmptyType ( i - > type ) ) {
emptyType = false ;
return emptyType ;
}
}
emptyType = true ;
return emptyType ;
}
emptyType = false ; // unknown types are assumed to be nonempty
return emptyType ;
}
2020-11-26 17:34:42 +01:00
bool CheckUnusedVar : : isFunctionWithoutSideEffects ( const Function & func , const Token * functionUsageToken ,
std : : list < const Function * > checkedFuncs ) {
// no body to analyze
if ( ! func . hasBody ( ) ) {
return false ;
}
for ( const Token * argsToken = functionUsageToken - > next ( ) ; ! Token : : simpleMatch ( argsToken , " ) " ) ; argsToken = argsToken - > next ( ) ) {
const Variable * argVar = argsToken - > variable ( ) ;
if ( argVar & & argVar - > isGlobal ( ) ) {
return false ; // TODO: analyze global variable usage
}
}
bool sideEffectReturnFound = false ;
for ( Token * bodyToken = func . functionScope - > bodyStart - > next ( ) ; bodyToken ! = func . functionScope - > bodyEnd ;
bodyToken = bodyToken - > next ( ) )
{
const Variable * bodyVariable = bodyToken - > variable ( ) ;
if ( bodyVariable ) {
// check variable for side-effects
if ( ! isVariableWithoutSideEffects ( * bodyVariable ) ) {
return false ;
}
// check if global variable is changed
if ( bodyVariable - > isGlobal ( ) ) {
return false ; // TODO: analyze global variable usage
}
}
// check nested function
const Function * bodyFunction = bodyToken - > function ( ) ;
if ( bodyFunction ) {
if ( std : : find ( checkedFuncs . begin ( ) , checkedFuncs . end ( ) , bodyFunction ) ! = checkedFuncs . end ( ) ) { // recursion found
continue ;
}
checkedFuncs . push_back ( bodyFunction ) ;
if ( ! isFunctionWithoutSideEffects ( * bodyFunction , bodyToken , checkedFuncs ) ) {
return false ;
}
}
// check returned value
if ( Token : : simpleMatch ( bodyToken , " return " ) ) {
const Token * returnValueToken = bodyToken - > next ( ) ;
// TODO: handle complex return expressions
if ( ! Token : : simpleMatch ( returnValueToken - > next ( ) , " ; " ) ) {
sideEffectReturnFound = true ;
continue ;
}
// simple one-token return
const Variable * returnVariable = returnValueToken - > variable ( ) ;
if ( returnValueToken - > isLiteral ( ) | |
( returnVariable & & isVariableWithoutSideEffects ( * returnVariable ) ) ) {
continue ;
}
sideEffectReturnFound = true ;
}
// unknown name
if ( bodyToken - > isNameOnly ( ) ) {
return false ;
}
}
return ! sideEffectReturnFound ;
}