2012-05-26 08:53:46 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2018-01-14 15:37:52 +01:00
* Copyright ( C ) 2007 - 2017 Cppcheck team .
2012-05-26 08:53:46 +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/>.
*/
//---------------------------------------------------------------------------
// Leaks when using auto variables
//---------------------------------------------------------------------------
# include "checkleakautovar.h"
2017-05-27 04:33:47 +02:00
# include "astutils.h"
2012-06-30 16:23:10 +02:00
# include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak
2016-05-26 19:21:45 +02:00
# include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# include "mathlib.h"
# include "settings.h"
2012-05-26 08:53:46 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "valueflow.h"
2012-05-26 08:53:46 +02:00
2017-05-27 04:33:47 +02:00
# include <cstddef>
2014-05-23 14:17:39 +02:00
# include <iostream>
2017-05-27 04:33:47 +02:00
# include <list>
2015-07-22 01:24:07 +02:00
# include <stack>
2017-05-27 04:33:47 +02:00
# include <utility>
2012-05-26 08:53:46 +02:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckLeakAutoVar instance ;
}
2016-01-25 20:01:48 +01:00
static const CWE CWE672 ( 672U ) ;
static const CWE CWE415 ( 415U ) ;
2018-04-15 01:53:00 +02:00
// Hardcoded allocation types (not from library)
static const int NEW_ARRAY = - 2 ;
static const int NEW = - 1 ;
2012-05-26 08:53:46 +02:00
//---------------------------------------------------------------------------
void VarInfo : : print ( )
{
std : : cout < < " size= " < < alloctype . size ( ) < < std : : endl ;
2018-04-05 15:58:28 +02:00
for ( std : : map < unsigned int , AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it ) {
2012-05-26 08:53:46 +02:00
std : : string strusage ;
2018-04-05 15:58:28 +02:00
const std : : map < unsigned int , std : : string > : : const_iterator use =
possibleUsage . find ( it - > first ) ;
2012-05-26 08:53:46 +02:00
if ( use ! = possibleUsage . end ( ) )
strusage = use - > second ;
2016-02-07 19:53:55 +01:00
std : : string status ;
switch ( it - > second . status ) {
2018-04-16 11:11:13 +02:00
case OWNED :
status = " owned " ;
break ;
2016-02-08 10:44:04 +01:00
case DEALLOC :
status = " dealloc " ;
break ;
case ALLOC :
status = " alloc " ;
break ;
case NOALLOC :
status = " noalloc " ;
break ;
default :
status = " ? " ;
break ;
2016-02-07 19:53:55 +01:00
} ;
std : : cout < < " status= " < < status < < " "
< < " alloctype=' " < < it - > second . type < < " ' "
2016-02-08 10:44:04 +01:00
< < " possibleUsage=' " < < strusage < < " ' "
2016-02-07 19:53:55 +01:00
< < " conditionalAlloc= " < < ( conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ? " yes " : " no " ) < < " "
< < " referenced= " < < ( referenced . find ( it - > first ) ! = referenced . end ( ) ? " yes " : " no " ) < < " "
< < std : : endl ;
2012-05-26 08:53:46 +02:00
}
}
void VarInfo : : possibleUsageAll ( const std : : string & functionName )
{
possibleUsage . clear ( ) ;
2018-04-05 15:58:28 +02:00
for ( std : : map < unsigned int , AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it )
2012-05-26 08:53:46 +02:00
possibleUsage [ it - > first ] = functionName ;
}
2013-07-02 07:18:19 +02:00
void CheckLeakAutoVar : : leakError ( const Token * tok , const std : : string & varname , int type )
2012-05-26 08:53:46 +02:00
{
2013-07-05 20:55:31 +02:00
const CheckMemoryLeak checkmemleak ( _tokenizer , _errorLogger , _settings ) ;
2013-07-08 18:26:18 +02:00
if ( _settings - > library . isresource ( type ) )
2012-09-18 16:20:09 +02:00
checkmemleak . resourceLeakError ( tok , varname ) ;
else
checkmemleak . memleakError ( tok , varname ) ;
2012-05-26 08:53:46 +02:00
}
void CheckLeakAutoVar : : mismatchError ( const Token * tok , const std : : string & varname )
{
2013-07-05 20:55:31 +02:00
const CheckMemoryLeak c ( _tokenizer , _errorLogger , _settings ) ;
2018-04-04 21:51:31 +02:00
const std : : list < const Token * > callstack ( 1 , tok ) ;
2012-06-30 16:23:10 +02:00
c . mismatchAllocDealloc ( callstack , varname ) ;
2012-05-26 08:53:46 +02:00
}
void CheckLeakAutoVar : : deallocUseError ( const Token * tok , const std : : string & varname )
{
2013-07-05 20:55:31 +02:00
const CheckMemoryLeak c ( _tokenizer , _errorLogger , _settings ) ;
2012-06-30 16:23:10 +02:00
c . deallocuseError ( tok , varname ) ;
2012-05-26 08:53:46 +02:00
}
2012-06-11 18:28:31 +02:00
void CheckLeakAutoVar : : deallocReturnError ( const Token * tok , const std : : string & varname )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " deallocret " , " $symbol: " + varname + " \n Returning/dereferencing '$symbol' after it is deallocated / released " , CWE672 , false ) ;
2012-06-11 18:28:31 +02:00
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : configurationInfo ( const Token * tok , const std : : string & functionName )
{
2017-04-11 11:49:09 +02:00
if ( _settings - > checkLibrary & & _settings - > isEnabled ( Settings : : INFORMATION ) ) {
2012-05-26 08:53:46 +02:00
reportError ( tok ,
Severity : : information ,
2013-07-16 18:50:25 +02:00
" checkLibraryUseIgnore " ,
2015-02-03 17:50:32 +01:00
" --check-library: Function " + functionName + " () should have <use>/<leak-ignore> configuration " ) ;
2012-05-26 08:53:46 +02:00
}
}
2015-01-28 13:45:40 +01:00
void CheckLeakAutoVar : : doubleFreeError ( const Token * tok , const std : : string & varname , int type )
{
if ( _settings - > library . isresource ( type ) )
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " doubleFree " , " $symbol: " + varname + " \n Resource handle '$symbol' freed twice. " , CWE415 , false ) ;
2015-01-28 13:45:40 +01:00
else
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " doubleFree " , " $symbol: " + varname + " \n Memory pointed to by '$symbol' is freed twice. " , CWE415 , false ) ;
2015-01-28 13:45:40 +01:00
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : check ( )
{
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2015-01-28 13:45:40 +01:00
// Local variables that are known to be non-zero.
const std : : set < unsigned int > notzero ;
2012-05-26 08:53:46 +02:00
// Check function scopes
2012-10-21 09:05:01 +02:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
2016-05-26 18:07:56 +02:00
if ( scope - > hasInlineOrLambdaFunction ( ) )
2016-05-26 17:42:27 +02:00
continue ;
2012-10-21 09:05:01 +02:00
// Empty variable info
VarInfo varInfo ;
2018-04-27 22:36:30 +02:00
checkScope ( scope - > bodyStart , & varInfo , notzero ) ;
2012-10-21 09:05:01 +02:00
varInfo . conditionalAlloc . clear ( ) ;
// Clear reference arguments from varInfo..
2015-01-28 13:45:40 +01:00
std : : map < unsigned int , VarInfo : : AllocInfo > : : iterator it = varInfo . alloctype . begin ( ) ;
2012-10-21 09:05:01 +02:00
while ( it ! = varInfo . alloctype . end ( ) ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( it - > first ) ;
if ( ! var | |
( var - > isArgument ( ) & & var - > isReference ( ) ) | |
( ! var - > isArgument ( ) & & ! var - > isLocal ( ) ) )
varInfo . alloctype . erase ( it + + ) ;
else
+ + it ;
2012-05-26 08:53:46 +02:00
}
2012-10-21 09:05:01 +02:00
2018-04-27 22:36:30 +02:00
ret ( scope - > bodyEnd , varInfo ) ;
2012-05-26 08:53:46 +02:00
}
}
2016-05-21 13:45:08 +02:00
static bool isVarUsedInTree ( const Token * tok , unsigned int varid )
{
if ( ! tok )
return false ;
if ( tok - > varId ( ) = = varid )
return true ;
return isVarUsedInTree ( tok - > astOperand1 ( ) , varid ) | | isVarUsedInTree ( tok - > astOperand2 ( ) , varid ) ;
}
2018-05-05 18:06:49 +02:00
static bool isPointerReleased ( const Token * startToken , const Token * endToken , unsigned int varid )
{
for ( const Token * tok = startToken ; tok & & tok ! = endToken ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) ! = varid )
continue ;
2018-05-06 09:50:53 +02:00
if ( Token : : Match ( tok , " %var% . release ( ) " ) )
2018-05-05 18:06:49 +02:00
return true ;
2018-05-06 09:50:53 +02:00
if ( Token : : Match ( tok , " %var% = " ) )
2018-05-05 18:06:49 +02:00
return false ;
}
return false ;
}
2012-05-26 08:53:46 +02:00
void CheckLeakAutoVar : : checkScope ( const Token * const startToken ,
VarInfo * varInfo ,
std : : set < unsigned int > notzero )
{
2015-01-28 13:45:40 +01:00
std : : map < unsigned int , VarInfo : : AllocInfo > & alloctype = varInfo - > alloctype ;
2012-05-26 08:53:46 +02:00
std : : map < unsigned int , std : : string > & possibleUsage = varInfo - > possibleUsage ;
const std : : set < unsigned int > conditionalAlloc ( varInfo - > conditionalAlloc ) ;
// Parse all tokens
const Token * const endToken = startToken - > link ( ) ;
for ( const Token * tok = startToken ; tok & & tok ! = endToken ; tok = tok - > next ( ) ) {
2015-05-10 10:00:46 +02:00
if ( ! tok - > scope ( ) - > isExecutable ( ) ) {
2018-04-27 22:36:30 +02:00
tok = tok - > scope ( ) - > bodyEnd ;
2015-05-10 10:00:46 +02:00
if ( ! tok ) // Ticket #6666 (crash upon invalid code)
break ;
}
2015-01-29 21:26:06 +01:00
2012-05-26 08:53:46 +02:00
// Deallocation and then dereferencing pointer..
if ( tok - > varId ( ) > 0 ) {
2015-06-28 16:49:16 +02:00
const std : : map < unsigned int , VarInfo : : AllocInfo > : : const_iterator var = alloctype . find ( tok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
if ( var ! = alloctype . end ( ) ) {
2016-05-26 19:21:45 +02:00
bool unknown = false ;
if ( var - > second . status = = VarInfo : : DEALLOC & & CheckNullPointer : : isPointerDeRef ( tok , unknown ) & & ! unknown ) {
2012-05-26 08:53:46 +02:00
deallocUseError ( tok , tok - > str ( ) ) ;
} else if ( Token : : simpleMatch ( tok - > tokAt ( - 2 ) , " = & " ) ) {
varInfo - > erase ( tok - > varId ( ) ) ;
2013-03-01 12:42:04 +01:00
} else if ( tok - > strAt ( - 1 ) = = " = " ) {
2012-05-26 08:53:46 +02:00
varInfo - > erase ( tok - > varId ( ) ) ;
}
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok - > previous ( ) , " & %name% = %var% ; " )) {
2012-10-27 16:36:14 +02:00
varInfo - > referenced . insert ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
}
}
if ( tok - > str ( ) = = " ( " & & tok - > previous ( ) - > isName ( ) ) {
2018-04-04 21:51:31 +02:00
const VarInfo : : AllocInfo allocation ( 0 , VarInfo : : NOALLOC ) ;
2016-05-22 17:18:50 +02:00
functionCall ( tok - > previous ( ) , varInfo , allocation , nullptr ) ;
2012-05-26 08:53:46 +02:00
tok = tok - > link ( ) ;
continue ;
}
// look for end of statement
if ( ! Token : : Match ( tok , " [;{}] " ) | | Token : : Match ( tok - > next ( ) , " [;{}] " ) )
continue ;
2015-01-28 13:45:40 +01:00
2012-05-26 08:53:46 +02:00
tok = tok - > next ( ) ;
2012-09-17 15:22:51 +02:00
if ( ! tok | | tok = = endToken )
2012-05-26 08:53:46 +02:00
break ;
2015-01-29 21:26:06 +01:00
// parse statement, skip to last member
2016-05-13 14:53:32 +02:00
const Token * varTok = tok ;
while ( Token : : Match ( varTok , " %name% ::|. %name% !!( " ) )
varTok = varTok - > tokAt ( 2 ) ;
2012-05-26 08:53:46 +02:00
2017-02-25 11:36:48 +01:00
const Token * ftok = tok ;
if ( ftok - > str ( ) = = " :: " )
ftok = ftok - > next ( ) ;
while ( Token : : Match ( ftok , " %name% :: %name% " ) )
ftok = ftok - > tokAt ( 2 ) ;
2012-05-26 08:53:46 +02:00
// assignment..
2016-05-13 14:53:32 +02:00
if ( Token : : Match ( varTok , " %var% = " ) ) {
2012-05-26 08:53:46 +02:00
// taking address of another variable..
2016-05-13 14:53:32 +02:00
if ( Token : : Match ( varTok - > next ( ) , " = %var% [+;] " ) ) {
if ( varTok - > tokAt ( 2 ) - > varId ( ) ! = varTok - > varId ( ) ) {
2012-05-26 08:53:46 +02:00
// If variable points at allocated memory => error
2016-05-13 14:53:32 +02:00
leakIfAllocated ( varTok , * varInfo ) ;
2012-05-26 08:53:46 +02:00
// no multivariable checking currently => bail out for rhs variables
2016-05-13 14:53:32 +02:00
for ( const Token * tok2 = varTok ; tok2 ; tok2 = tok2 - > next ( ) ) {
2012-05-26 08:53:46 +02:00
if ( tok2 - > str ( ) = = " ; " ) {
break ;
}
if ( tok2 - > varId ( ) ) {
varInfo - > erase ( tok2 - > varId ( ) ) ;
}
}
}
}
// is variable used in rhs?
2016-05-21 13:45:08 +02:00
if ( isVarUsedInTree ( varTok - > next ( ) - > astOperand2 ( ) , varTok - > varId ( ) ) )
2012-05-26 08:53:46 +02:00
continue ;
// Variable has already been allocated => error
2016-05-13 14:53:32 +02:00
if ( conditionalAlloc . find ( varTok - > varId ( ) ) = = conditionalAlloc . end ( ) )
leakIfAllocated ( varTok , * varInfo ) ;
varInfo - > erase ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
// not a local variable nor argument?
2016-05-13 14:53:32 +02:00
const Variable * var = varTok - > variable ( ) ;
2015-08-11 12:41:42 +02:00
if ( var & & ! var - > isArgument ( ) & & ( ! var - > isLocal ( ) | | var - > isStatic ( ) ) )
2014-10-14 16:40:30 +02:00
continue ;
2012-07-17 16:28:34 +02:00
// Don't check reference variables
if ( var & & var - > isReference ( ) )
continue ;
2015-08-11 12:41:42 +02:00
// non-pod variable
if ( _tokenizer - > isCPP ( ) ) {
if ( ! var )
continue ;
// Possibly automatically deallocated memory
2016-05-13 14:53:32 +02:00
if ( ! var - > typeStartToken ( ) - > isStandardType ( ) & & Token : : Match ( varTok , " %var% = new " ) )
2015-08-11 12:41:42 +02:00
continue ;
2017-07-08 22:28:31 +02:00
if ( ! var - > isPointer ( ) & & ! var - > typeStartToken ( ) - > isStandardType ( ) )
continue ;
2015-08-11 12:41:42 +02:00
}
2012-05-26 08:53:46 +02:00
// allocation?
2016-05-13 14:53:32 +02:00
if ( varTok - > next ( ) - > astOperand2 ( ) & & Token : : Match ( varTok - > next ( ) - > astOperand2 ( ) - > previous ( ) , " %type% ( " ) ) {
2016-05-22 17:18:50 +02:00
const Library : : AllocFunc * f = _settings - > library . alloc ( varTok - > next ( ) - > astOperand2 ( ) - > previous ( ) ) ;
if ( f & & f - > arg = = - 1 ) {
2017-10-11 16:36:43 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ varTok - > varId ( ) ] ;
varAlloc . type = f - > groupId ;
varAlloc . status = VarInfo : : ALLOC ;
2012-05-26 08:53:46 +02:00
}
2017-05-04 19:39:57 +02:00
} else if ( _tokenizer - > isCPP ( ) & & Token : : Match ( varTok - > tokAt ( 2 ) , " new !!( " ) ) {
2016-10-09 10:28:19 +02:00
const Token * tok2 = varTok - > tokAt ( 2 ) - > astOperand1 ( ) ;
2018-04-04 21:51:31 +02:00
const bool arrayNew = ( tok2 & & ( tok2 - > str ( ) = = " [ " | | ( tok2 - > str ( ) = = " ( " & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > str ( ) = = " [ " ) ) ) ;
2017-10-11 16:36:43 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ varTok - > varId ( ) ] ;
2018-04-15 01:53:00 +02:00
varAlloc . type = arrayNew ? NEW_ARRAY : NEW ;
2017-10-11 16:36:43 +02:00
varAlloc . status = VarInfo : : ALLOC ;
2012-05-26 08:53:46 +02:00
}
// Assigning non-zero value variable. It might be used to
// track the execution for a later if condition.
2016-05-13 14:53:32 +02:00
if ( Token : : Match ( varTok - > tokAt ( 2 ) , " %num% ; " ) & & MathLib : : toLongNumber ( varTok - > strAt ( 2 ) ) ! = 0 )
notzero . insert ( varTok - > varId ( ) ) ;
else if ( Token : : Match ( varTok - > tokAt ( 2 ) , " - %type% ; " ) && varTok->tokAt(3)->isUpperCaseName())
notzero . insert ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
else
2016-05-13 14:53:32 +02:00
notzero . erase ( varTok - > varId ( ) ) ;
2012-05-26 08:53:46 +02:00
}
// if/else
else if ( Token : : simpleMatch ( tok , " if ( " ) ) {
// Parse function calls inside the condition
for ( const Token * innerTok = tok - > tokAt ( 2 ) ; innerTok ; innerTok = innerTok - > next ( ) ) {
2017-10-26 19:11:00 +02:00
if ( Token : : Match ( innerTok , " %var% = " ) ) {
// allocation?
if ( Token : : Match ( innerTok - > tokAt ( 2 ) , " %type% ( " ) ) {
const Library : : AllocFunc * f = _settings - > library . alloc ( innerTok - > tokAt ( 2 ) ) ;
if ( f & & f - > arg = = - 1 ) {
VarInfo : : AllocInfo & varAlloc = alloctype [ innerTok - > varId ( ) ] ;
varAlloc . type = f - > groupId ;
varAlloc . status = VarInfo : : ALLOC ;
}
} else if ( _tokenizer - > isCPP ( ) & & Token : : Match ( innerTok - > tokAt ( 2 ) , " new !!( " ) ) {
const Token * tok2 = innerTok - > tokAt ( 2 ) - > astOperand1 ( ) ;
2018-04-04 21:51:31 +02:00
const bool arrayNew = ( tok2 & & ( tok2 - > str ( ) = = " [ " | | ( tok2 - > str ( ) = = " ( " & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > str ( ) = = " [ " ) ) ) ;
2017-10-26 19:11:00 +02:00
VarInfo : : AllocInfo & varAlloc = alloctype [ innerTok - > varId ( ) ] ;
2018-04-15 01:53:00 +02:00
varAlloc . type = arrayNew ? NEW_ARRAY : NEW ;
2017-10-26 19:11:00 +02:00
varAlloc . status = VarInfo : : ALLOC ;
}
}
2012-05-26 08:53:46 +02:00
if ( innerTok - > str ( ) = = " ) " )
break ;
if ( innerTok - > str ( ) = = " ( " & & innerTok - > previous ( ) - > isName ( ) ) {
2018-04-04 21:51:31 +02:00
const VarInfo : : AllocInfo allocation ( 0 , VarInfo : : NOALLOC ) ;
2016-05-22 17:18:50 +02:00
functionCall ( innerTok - > previous ( ) , varInfo , allocation , nullptr ) ;
2012-05-26 08:53:46 +02:00
innerTok = innerTok - > link ( ) ;
}
}
const Token * tok2 = tok - > linkAt ( 1 ) ;
if ( Token : : simpleMatch ( tok2 , " ) { " ) ) {
2014-04-18 18:18:47 +02:00
VarInfo varInfo1 ( * varInfo ) ; // VarInfo for if code
VarInfo varInfo2 ( * varInfo ) ; // VarInfo for else code
2012-05-26 08:53:46 +02:00
2015-07-25 11:37:03 +02:00
// Recursively scan variable comparisons in condition
std : : stack < const Token * > tokens ;
tokens . push ( tok - > next ( ) - > astOperand2 ( ) ) ;
while ( ! tokens . empty ( ) ) {
const Token * tok3 = tokens . top ( ) ;
tokens . pop ( ) ;
if ( ! tok3 )
continue ;
if ( tok3 - > str ( ) = = " && " ) {
tokens . push ( tok3 - > astOperand1 ( ) ) ;
tokens . push ( tok3 - > astOperand2 ( ) ) ;
continue ;
}
if ( tok3 - > str ( ) = = " ( " & & Token : : Match ( tok3 - > astOperand1 ( ) , " UNLIKELY|LIKELY " ) ) {
tokens . push ( tok3 - > astOperand2 ( ) ) ;
continue ;
2017-06-30 23:05:09 +02:00
} else if ( tok3 - > str ( ) = = " ( " & & Token : : Match ( tok3 - > previous ( ) , " %name% " ) ) {
2017-07-01 09:10:23 +02:00
const std : : vector < const Token * > params = getArguments ( tok3 - > previous ( ) ) ;
2017-06-30 23:05:09 +02:00
for ( unsigned int i = 0 ; i < params . size ( ) ; + + i ) {
const Token * par = params [ i ] ;
if ( ! par - > isComparisonOp ( ) )
continue ;
const Token * vartok = nullptr ;
if ( astIsVariableComparison ( par , " != " , " 0 " , & vartok ) | |
astIsVariableComparison ( par , " == " , " 0 " , & vartok ) | |
astIsVariableComparison ( par , " < " , " 0 " , & vartok ) | |
astIsVariableComparison ( par , " > " , " 0 " , & vartok ) | |
astIsVariableComparison ( par , " == " , " -1 " , & vartok ) | |
astIsVariableComparison ( par , " != " , " -1 " , & vartok ) ) {
varInfo1 . erase ( vartok - > varId ( ) ) ;
varInfo2 . erase ( vartok - > varId ( ) ) ;
}
}
continue ;
2015-07-25 11:37:03 +02:00
}
const Token * vartok = nullptr ;
2015-08-03 09:20:50 +02:00
if ( astIsVariableComparison ( tok3 , " != " , " 0 " , & vartok ) ) {
2015-07-25 11:37:03 +02:00
varInfo2 . erase ( vartok - > varId ( ) ) ;
if ( notzero . find ( vartok - > varId ( ) ) ! = notzero . end ( ) )
varInfo2 . clear ( ) ;
2015-08-03 09:20:50 +02:00
} else if ( astIsVariableComparison ( tok3 , " == " , " 0 " , & vartok ) ) {
2015-07-25 11:37:03 +02:00
varInfo1 . erase ( vartok - > varId ( ) ) ;
2015-08-03 09:20:50 +02:00
} else if ( astIsVariableComparison ( tok3 , " < " , " 0 " , & vartok ) ) {
2015-07-25 11:37:03 +02:00
varInfo1 . erase ( vartok - > varId ( ) ) ;
2015-08-03 09:20:50 +02:00
} else if ( astIsVariableComparison ( tok3 , " > " , " 0 " , & vartok ) ) {
2015-07-25 11:37:03 +02:00
varInfo2 . erase ( vartok - > varId ( ) ) ;
2015-08-03 09:20:50 +02:00
} else if ( astIsVariableComparison ( tok3 , " == " , " -1 " , & vartok ) ) {
2015-07-25 11:37:03 +02:00
varInfo1 . erase ( vartok - > varId ( ) ) ;
}
2012-06-17 07:54:24 +02:00
}
2012-05-26 08:53:46 +02:00
checkScope ( tok2 - > next ( ) , & varInfo1 , notzero ) ;
tok2 = tok2 - > linkAt ( 1 ) ;
if ( Token : : simpleMatch ( tok2 , " } else { " ) ) {
checkScope ( tok2 - > tokAt ( 2 ) , & varInfo2 , notzero ) ;
tok = tok2 - > linkAt ( 2 ) - > previous ( ) ;
} else {
tok = tok2 - > previous ( ) ;
}
VarInfo old ;
old . swap ( * varInfo ) ;
2015-01-28 13:45:40 +01:00
std : : map < unsigned int , VarInfo : : AllocInfo > : : const_iterator it ;
2016-05-15 20:27:44 +02:00
for ( it = old . alloctype . begin ( ) ; it ! = old . alloctype . end ( ) ; + + it ) {
const unsigned int varId = it - > first ;
if ( old . conditionalAlloc . find ( varId ) = = old . conditionalAlloc . end ( ) )
continue ;
if ( varInfo1 . alloctype . find ( varId ) = = varInfo1 . alloctype . end ( ) | |
varInfo2 . alloctype . find ( varId ) = = varInfo2 . alloctype . end ( ) ) {
varInfo1 . erase ( varId ) ;
varInfo2 . erase ( varId ) ;
}
}
// Conditional allocation in varInfo1
2012-05-26 08:53:46 +02:00
for ( it = varInfo1 . alloctype . begin ( ) ; it ! = varInfo1 . alloctype . end ( ) ; + + it ) {
if ( varInfo2 . alloctype . find ( it - > first ) = = varInfo2 . alloctype . end ( ) & &
old . alloctype . find ( it - > first ) = = old . alloctype . end ( ) ) {
varInfo - > conditionalAlloc . insert ( it - > first ) ;
}
}
// Conditional allocation in varInfo2
for ( it = varInfo2 . alloctype . begin ( ) ; it ! = varInfo2 . alloctype . end ( ) ; + + it ) {
if ( varInfo1 . alloctype . find ( it - > first ) = = varInfo1 . alloctype . end ( ) & &
old . alloctype . find ( it - > first ) = = old . alloctype . end ( ) ) {
varInfo - > conditionalAlloc . insert ( it - > first ) ;
}
}
// Conditional allocation/deallocation
for ( it = varInfo1 . alloctype . begin ( ) ; it ! = varInfo1 . alloctype . end ( ) ; + + it ) {
2018-04-16 11:11:13 +02:00
if ( it - > second . managed ( ) & & conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ) {
2012-05-26 08:53:46 +02:00
varInfo - > conditionalAlloc . erase ( it - > first ) ;
varInfo2 . erase ( it - > first ) ;
}
}
for ( it = varInfo2 . alloctype . begin ( ) ; it ! = varInfo2 . alloctype . end ( ) ; + + it ) {
2018-04-16 11:11:13 +02:00
if ( it - > second . managed ( ) & & conditionalAlloc . find ( it - > first ) ! = conditionalAlloc . end ( ) ) {
2012-05-26 08:53:46 +02:00
varInfo - > conditionalAlloc . erase ( it - > first ) ;
varInfo1 . erase ( it - > first ) ;
}
}
alloctype . insert ( varInfo1 . alloctype . begin ( ) , varInfo1 . alloctype . end ( ) ) ;
alloctype . insert ( varInfo2 . alloctype . begin ( ) , varInfo2 . alloctype . end ( ) ) ;
possibleUsage . insert ( varInfo1 . possibleUsage . begin ( ) , varInfo1 . possibleUsage . end ( ) ) ;
possibleUsage . insert ( varInfo2 . possibleUsage . begin ( ) , varInfo2 . possibleUsage . end ( ) ) ;
}
}
2015-01-28 13:45:40 +01:00
// unknown control.. (TODO: handle loops)
2015-01-28 22:29:07 +01:00
else if ( ( Token : : Match ( tok , " %type% ( " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ) { " )) || Token::simpleMatch(tok, " do { " )) {
2012-05-26 08:53:46 +02:00
varInfo - > clear ( ) ;
break ;
}
2015-01-28 19:44:59 +01:00
// return
else if ( tok - > str ( ) = = " return " ) {
ret ( tok , * varInfo ) ;
varInfo - > clear ( ) ;
}
// throw
2015-06-28 16:49:16 +02:00
else if ( _tokenizer - > isCPP ( ) & & tok - > str ( ) = = " throw " ) {
2015-01-28 19:44:59 +01:00
bool tryFound = false ;
const Scope * scope = tok - > scope ( ) ;
while ( scope & & scope - > isExecutable ( ) ) {
if ( scope - > type = = Scope : : eTry )
tryFound = true ;
scope = scope - > nestedIn ;
}
// If the execution leaves the function then treat it as return
if ( ! tryFound )
ret ( tok , * varInfo ) ;
varInfo - > clear ( ) ;
}
2012-05-26 08:53:46 +02:00
// Function call..
2017-02-25 11:36:48 +01:00
else if ( Token : : Match ( ftok , " %type% ( " ) ) {
const Library : : AllocFunc * af = _settings - > library . dealloc ( ftok ) ;
2016-05-22 17:18:50 +02:00
VarInfo : : AllocInfo allocation ( af ? af - > groupId : 0 , VarInfo : : DEALLOC ) ;
2015-01-28 13:45:40 +01:00
if ( allocation . type = = 0 )
allocation . status = VarInfo : : NOALLOC ;
2017-02-25 11:36:48 +01:00
functionCall ( ftok , varInfo , allocation , af ) ;
2012-05-26 08:53:46 +02:00
2017-02-25 11:36:48 +01:00
tok = ftok - > next ( ) - > link ( ) ;
2012-05-26 08:53:46 +02:00
// Handle scopes that might be noreturn
2015-01-28 13:45:40 +01:00
if ( allocation . status = = VarInfo : : NOALLOC & & Token : : simpleMatch ( tok , " ) ; } " ) ) {
2012-06-24 15:38:37 +02:00
const std : : string & functionName ( tok - > link ( ) - > previous ( ) - > str ( ) ) ;
2012-05-26 08:53:46 +02:00
bool unknown = false ;
2014-05-04 13:11:21 +02:00
if ( _tokenizer - > IsScopeNoReturn ( tok - > tokAt ( 2 ) , & unknown ) ) {
if ( ! unknown )
2012-05-26 08:53:46 +02:00
varInfo - > clear ( ) ;
2016-12-06 12:31:16 +01:00
else if ( ! _settings - > library . isLeakIgnore ( functionName ) & & ! _settings - > library . isUse ( functionName ) )
2014-05-04 13:11:21 +02:00
varInfo - > possibleUsageAll ( functionName ) ;
2012-05-26 08:53:46 +02:00
}
}
continue ;
}
2015-01-28 13:45:40 +01:00
// delete
else if ( _tokenizer - > isCPP ( ) & & tok - > str ( ) = = " delete " ) {
2018-04-04 21:51:31 +02:00
const bool arrayDelete = ( tok - > strAt ( 1 ) = = " [ " ) ;
2016-10-09 10:28:19 +02:00
if ( arrayDelete )
2015-01-28 13:45:40 +01:00
tok = tok - > tokAt ( 3 ) ;
else
tok = tok - > next ( ) ;
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( tok , " %name% ::|. " ) )
2015-01-28 13:45:40 +01:00
tok = tok - > tokAt ( 2 ) ;
2017-04-25 14:57:48 +02:00
const bool isnull = tok - > hasKnownIntValue ( ) & & tok - > values ( ) . front ( ) . intvalue = = 0 ;
2017-04-24 22:05:16 +02:00
if ( ! isnull & & tok - > varId ( ) & & tok - > strAt ( 1 ) ! = " [ " ) {
2018-04-15 01:53:00 +02:00
const VarInfo : : AllocInfo allocation ( arrayDelete ? NEW_ARRAY : NEW , VarInfo : : DEALLOC ) ;
2015-01-28 13:45:40 +01:00
changeAllocStatus ( varInfo , allocation , tok , tok ) ;
}
}
2012-10-08 17:23:47 +02:00
// goto => weird execution path
else if ( tok - > str ( ) = = " goto " ) {
varInfo - > clear ( ) ;
}
2013-07-28 11:53:49 +02:00
2013-07-28 10:26:46 +02:00
// continue/break
else if ( Token : : Match ( tok , " continue|break ; " ) ) {
varInfo - > clear ( ) ;
}
2018-04-16 11:11:13 +02:00
// Check smart pointer
else if ( Token : : Match ( ftok , " auto_ptr|unique_ptr|shared_ptr < %type% " ) ) {
2018-04-17 08:37:41 +02:00
const Token * typeEndTok = ftok - > linkAt ( 1 ) ;
2018-04-21 11:24:19 +02:00
if ( ! Token : : Match ( typeEndTok , " > %var% {|( %var% ,|)|} " ) )
2018-04-16 11:11:13 +02:00
continue ;
2018-05-05 18:06:49 +02:00
tok = typeEndTok - > linkAt ( 2 ) ;
unsigned varid = typeEndTok - > next ( ) - > varId ( ) ;
2018-05-06 09:50:53 +02:00
if ( isPointerReleased ( typeEndTok - > tokAt ( 2 ) , endToken , varid ) )
2018-05-05 18:06:49 +02:00
continue ;
2018-04-16 11:11:13 +02:00
bool arrayDelete = false ;
2018-04-17 08:37:41 +02:00
if ( Token : : findsimplematch ( ftok - > next ( ) , " [ ] " , typeEndTok ) )
2018-04-16 11:11:13 +02:00
arrayDelete = true ;
// Check deleter
const Token * deleterToken = nullptr ;
const Token * endDeleterToken = nullptr ;
const Library : : AllocFunc * af = nullptr ;
2018-04-16 12:55:37 +02:00
if ( Token : : Match ( ftok , " unique_ptr < %type% , " ) ) {
2018-04-16 11:11:13 +02:00
deleterToken = ftok - > tokAt ( 4 ) ;
2018-04-17 08:37:41 +02:00
endDeleterToken = typeEndTok ;
} else if ( Token : : Match ( typeEndTok , " > %var% {|( %var% , " ) ) {
deleterToken = typeEndTok - > tokAt ( 5 ) ;
endDeleterToken = typeEndTok - > linkAt ( 2 ) ;
2018-04-16 11:11:13 +02:00
}
2018-04-16 12:55:37 +02:00
if ( deleterToken ) {
2018-04-16 11:11:13 +02:00
// Check if its a pointer to a function
const Token * dtok = Token : : findmatch ( deleterToken , " & %name% " , endDeleterToken ) ;
if ( dtok ) {
af = _settings - > library . dealloc ( dtok - > tokAt ( 1 ) ) ;
} else {
// If the deleter is a class, check if class calls the dealloc function
dtok = Token : : findmatch ( deleterToken , " %type% " , endDeleterToken ) ;
if ( dtok & & dtok - > type ( ) ) {
const Scope * tscope = dtok - > type ( ) - > classScope ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok2 = tscope - > bodyStart ; tok2 ! = tscope - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
2018-04-16 11:11:13 +02:00
af = _settings - > library . dealloc ( tok2 ) ;
2018-04-16 12:55:37 +02:00
if ( af )
2018-04-16 11:11:13 +02:00
break ;
}
}
}
}
2018-04-17 08:37:41 +02:00
const Token * vtok = typeEndTok - > tokAt ( 3 ) ;
2018-04-16 11:11:13 +02:00
const VarInfo : : AllocInfo allocation ( af ? af - > groupId : ( arrayDelete ? NEW_ARRAY : NEW ) , VarInfo : : OWNED ) ;
changeAllocStatus ( varInfo , allocation , vtok , vtok ) ;
}
2012-05-26 08:53:46 +02:00
}
}
2015-01-28 13:45:40 +01:00
void CheckLeakAutoVar : : changeAllocStatus ( VarInfo * varInfo , const VarInfo : : AllocInfo & allocation , const Token * tok , const Token * arg )
{
std : : map < unsigned int , VarInfo : : AllocInfo > & alloctype = varInfo - > alloctype ;
std : : map < unsigned int , std : : string > & possibleUsage = varInfo - > possibleUsage ;
const std : : map < unsigned int , VarInfo : : AllocInfo > : : iterator var = alloctype . find ( arg - > varId ( ) ) ;
if ( var ! = alloctype . end ( ) ) {
if ( allocation . status = = VarInfo : : NOALLOC ) {
// possible usage
possibleUsage [ arg - > varId ( ) ] = tok - > str ( ) ;
if ( var - > second . status = = VarInfo : : DEALLOC & & arg - > previous ( ) - > str ( ) = = " & " )
varInfo - > erase ( arg - > varId ( ) ) ;
2018-04-16 11:11:13 +02:00
} else if ( var - > second . managed ( ) ) {
2015-01-28 13:45:40 +01:00
doubleFreeError ( tok , arg - > str ( ) , allocation . type ) ;
} else if ( var - > second . type ! = allocation . type ) {
// mismatching allocation and deallocation
mismatchError ( tok , arg - > str ( ) ) ;
varInfo - > erase ( arg - > varId ( ) ) ;
} else {
// deallocation
2018-04-16 11:11:13 +02:00
var - > second . status = allocation . status ;
2015-01-28 13:45:40 +01:00
var - > second . type = allocation . type ;
}
} else if ( allocation . status ! = VarInfo : : NOALLOC ) {
alloctype [ arg - > varId ( ) ] . status = VarInfo : : DEALLOC ;
}
}
2016-05-22 17:18:50 +02:00
void CheckLeakAutoVar : : functionCall ( const Token * tok , VarInfo * varInfo , const VarInfo : : AllocInfo & allocation , const Library : : AllocFunc * af )
2012-05-26 08:53:46 +02:00
{
2012-06-24 15:38:37 +02:00
// Ignore function call?
2016-12-06 12:31:16 +01:00
if ( _settings - > library . isLeakIgnore ( tok - > str ( ) ) )
2012-06-24 15:38:37 +02:00
return ;
2016-05-22 17:18:50 +02:00
int argNr = 1 ;
2012-05-26 08:53:46 +02:00
for ( const Token * arg = tok - > tokAt ( 2 ) ; arg ; arg = arg - > nextArgument ( ) ) {
2016-05-21 14:03:28 +02:00
if ( _tokenizer - > isCPP ( ) & & arg - > str ( ) = = " new " ) {
2014-09-30 13:02:02 +02:00
arg = arg - > next ( ) ;
2016-05-21 14:03:28 +02:00
if ( Token : : simpleMatch ( arg , " ( std :: nothrow ) " ) )
arg = arg - > tokAt ( 5 ) ;
}
2014-09-30 13:02:02 +02:00
2017-07-07 21:51:48 +02:00
while ( Token : : Match ( arg , " %var% . %var% " ) )
arg = arg - > tokAt ( 2 ) ;
2016-05-04 12:14:42 +02:00
if ( Token : : Match ( arg , " %var% [-,)] !!. " ) | | Token : : Match ( arg , " & %var% " ) ) {
2012-05-26 08:53:46 +02:00
// goto variable
if ( arg - > str ( ) = = " & " )
arg = arg - > next ( ) ;
2018-04-04 21:51:31 +02:00
const bool isnull = arg - > hasKnownIntValue ( ) & & arg - > values ( ) . front ( ) . intvalue = = 0 ;
2017-03-03 18:36:06 +01:00
2012-05-26 08:53:46 +02:00
// Is variable allocated?
2017-03-03 18:36:06 +01:00
if ( ! isnull & & ( ! af | | af - > arg = = argNr ) )
2016-05-22 17:18:50 +02:00
changeAllocStatus ( varInfo , allocation , tok , arg ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( arg , " %name% ( " ) ) {
2016-12-29 13:17:46 +01:00
const Library : : AllocFunc * allocFunc = _settings - > library . dealloc ( arg ) ;
VarInfo : : AllocInfo alloc ( allocFunc ? allocFunc - > groupId : 0 , VarInfo : : DEALLOC ) ;
if ( alloc . type = = 0 )
alloc . status = VarInfo : : NOALLOC ;
functionCall ( arg , varInfo , alloc , allocFunc ) ;
2012-05-26 08:53:46 +02:00
}
2016-05-22 17:18:50 +02:00
argNr + + ;
2012-05-26 08:53:46 +02:00
}
}
void CheckLeakAutoVar : : leakIfAllocated ( const Token * vartok ,
const VarInfo & varInfo )
{
2015-01-28 13:45:40 +01:00
const std : : map < unsigned int , VarInfo : : AllocInfo > & alloctype = varInfo . alloctype ;
2012-05-26 08:53:46 +02:00
const std : : map < unsigned int , std : : string > & possibleUsage = varInfo . possibleUsage ;
2015-01-28 13:45:40 +01:00
const std : : map < unsigned int , VarInfo : : AllocInfo > : : const_iterator var = alloctype . find ( vartok - > varId ( ) ) ;
2018-04-16 11:11:13 +02:00
if ( var ! = alloctype . end ( ) & & var - > second . status = = VarInfo : : ALLOC ) {
2012-05-26 08:53:46 +02:00
const std : : map < unsigned int , std : : string > : : const_iterator use = possibleUsage . find ( vartok - > varId ( ) ) ;
if ( use = = possibleUsage . end ( ) ) {
2015-01-28 13:45:40 +01:00
leakError ( vartok , vartok - > str ( ) , var - > second . type ) ;
2012-05-26 08:53:46 +02:00
} else {
configurationInfo ( vartok , use - > second ) ;
}
}
}
void CheckLeakAutoVar : : ret ( const Token * tok , const VarInfo & varInfo )
{
2015-01-28 13:45:40 +01:00
const std : : map < unsigned int , VarInfo : : AllocInfo > & alloctype = varInfo . alloctype ;
2012-05-26 08:53:46 +02:00
const std : : map < unsigned int , std : : string > & possibleUsage = varInfo . possibleUsage ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2015-01-28 13:45:40 +01:00
for ( std : : map < unsigned int , VarInfo : : AllocInfo > : : const_iterator it = alloctype . begin ( ) ; it ! = alloctype . end ( ) ; + + it ) {
2012-05-26 08:53:46 +02:00
// don't warn if variable is conditionally allocated
2018-04-16 11:11:13 +02:00
if ( ! it - > second . managed ( ) & & varInfo . conditionalAlloc . find ( it - > first ) ! = varInfo . conditionalAlloc . end ( ) )
2012-05-26 08:53:46 +02:00
continue ;
2012-10-27 16:36:14 +02:00
// don't warn if there is a reference of the variable
if ( varInfo . referenced . find ( it - > first ) ! = varInfo . referenced . end ( ) )
continue ;
2012-05-26 08:53:46 +02:00
const unsigned int varid = it - > first ;
const Variable * var = symbolDatabase - > getVariableFromVarId ( varid ) ;
if ( var ) {
bool used = false ;
for ( const Token * tok2 = tok ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ; " )
break ;
if ( Token : : Match ( tok2 , " return|(|, %varid% [);,] " , varid ) ) {
used = true ;
break ;
}
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok2 , " return|(|, & %varid% . %name% [);,] " , varid ) ) {
2012-05-26 08:53:46 +02:00
used = true ;
break ;
}
}
2012-06-11 18:28:31 +02:00
// return deallocated pointer
2015-01-28 13:45:40 +01:00
if ( used & & it - > second . status = = VarInfo : : DEALLOC )
2012-06-11 18:28:31 +02:00
deallocReturnError ( tok , var - > name ( ) ) ;
2018-04-16 11:11:13 +02:00
else if ( ! used & & ! it - > second . managed ( ) ) {
2012-06-11 18:28:31 +02:00
const std : : map < unsigned int , std : : string > : : const_iterator use = possibleUsage . find ( varid ) ;
if ( use = = possibleUsage . end ( ) ) {
2015-01-28 13:45:40 +01:00
leakError ( tok , var - > name ( ) , it - > second . type ) ;
2012-06-11 18:28:31 +02:00
} else {
configurationInfo ( tok , use - > second ) ;
}
2012-05-26 08:53:46 +02:00
}
}
}
}