2014-01-04 20:57:02 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
* Copyright ( C ) 2007 - 2013 Daniel Marjamäki and Cppcheck team .
*
* 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 "valueflow.h"
2014-01-05 20:06:46 +01:00
# include "errorlogger.h"
2014-01-04 20:57:02 +01:00
# include "mathlib.h"
2014-01-05 20:06:46 +01:00
# include "settings.h"
# include "symboldatabase.h"
# include "token.h"
# include "tokenlist.h"
2014-01-04 20:57:02 +01:00
2014-01-06 16:37:52 +01:00
# include <iostream>
static void printvalues ( const Token * tok )
{
if ( tok - > values . empty ( ) )
std : : cout < < " empty " ;
for ( std : : list < ValueFlow : : Value > : : const_iterator it = tok - > values . begin ( ) ; it ! = tok - > values . end ( ) ; + + it )
std : : cout < < " " < < ( it - > intvalue ) ;
std : : cout < < std : : endl ;
}
2014-01-06 07:44:58 +01:00
static void bailout ( TokenList * tokenlist , ErrorLogger * errorLogger , const Token * tok , const std : : string & what )
{
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > callstack ;
callstack . push_back ( ErrorLogger : : ErrorMessage : : FileLocation ( tok , tokenlist ) ) ;
ErrorLogger : : ErrorMessage errmsg ( callstack , Severity : : debug , " ValueFlow bailout: " + what , " valueFlowBailout " , false ) ;
errorLogger - > reportErr ( errmsg ) ;
}
2014-01-11 14:31:51 +01:00
static bool bailoutFunctionPar ( const Token * tok )
{
// passing variable to subfunction?
const bool addr = tok & & Token : : Match ( tok - > previous ( ) , " & " ) ;
if ( ! tok | | ! Token : : Match ( tok - > tokAt ( addr ? - 2 : - 1 ) , " [(,] &| %var% [,)] " ) )
return false ;
// goto start of function call and get argnr
unsigned int argnr = 0 ;
while ( tok & & tok - > str ( ) ! = " ( " ) {
if ( tok - > str ( ) = = " , " )
+ + argnr ;
else if ( tok - > str ( ) = = " ) " )
tok = tok - > link ( ) ;
tok = tok - > previous ( ) ;
}
tok = tok ? tok - > previous ( ) : NULL ;
if ( ! Token : : Match ( tok , " %var% ( " ) )
return false ; // not a function => dont bailout
if ( ! tok - > function ( ) )
return true ; // unknown function bailout
const Variable * arg = tok - > function ( ) - > getArgumentVar ( argnr ) ;
return arg & & ! arg - > isConst ( ) & & arg - > isReference ( ) ;
}
2014-01-11 14:54:10 +01:00
static const Token * skipValueInConditionalExpression ( const Token * tok )
{
while ( tok & & ! Token : : Match ( tok , " %oror%|&&|?|: " ) ) {
while ( Token : : Match ( tok - > astParent ( ) , " %oror%|&&|? " ) & & tok - > astParent ( ) - > astOperand1 ( ) = = tok )
tok = tok - > astParent ( ) ;
tok = tok - > astParent ( ) ;
}
return tok ;
}
2014-01-11 15:36:09 +01:00
static bool bailoutSelfAssignment ( const Token * const tok )
{
const Token * parent = tok ;
while ( parent ) {
const Token * op = parent ;
parent = parent - > astParent ( ) ;
// Assignment where lhs variable exists in rhs => return true
2014-01-11 20:53:23 +01:00
if ( parent ! = NULL & &
parent - > astOperand2 ( ) = = op & &
parent - > astOperand1 ( ) ! = NULL & &
parent - > str ( ) = = " = " ) {
for ( const Token * lhs = parent - > astOperand1 ( ) ; lhs ; lhs = lhs - > astOperand1 ( ) ) {
if ( lhs - > varId ( ) = = tok - > varId ( ) )
return true ;
if ( lhs - > astOperand2 ( ) & & lhs - > astOperand2 ( ) - > varId ( ) = = tok - > varId ( ) )
return true ;
}
}
2014-01-11 15:36:09 +01:00
}
return false ;
}
2014-01-05 20:06:46 +01:00
static void valueFlowBeforeCondition ( TokenList * tokenlist , ErrorLogger * errorLogger , const Settings * settings )
2014-01-04 20:57:02 +01:00
{
2014-01-05 20:06:46 +01:00
for ( Token * tok = tokenlist - > front ( ) ; tok ; tok = tok - > next ( ) ) {
2014-01-04 20:57:02 +01:00
unsigned int varid ;
MathLib : : bigint num ;
2014-01-05 20:06:46 +01:00
const Variable * var ;
2014-01-10 05:54:03 +01:00
if ( tok - > isComparisonOp ( ) & & tok - > astOperand1 ( ) & & tok - > astOperand2 ( ) ) {
2014-01-04 20:57:02 +01:00
if ( tok - > astOperand1 ( ) - > isName ( ) & & tok - > astOperand2 ( ) - > isNumber ( ) ) {
varid = tok - > astOperand1 ( ) - > varId ( ) ;
2014-01-05 20:06:46 +01:00
var = tok - > astOperand1 ( ) - > variable ( ) ;
2014-01-04 21:23:17 +01:00
num = MathLib : : toLongNumber ( tok - > astOperand2 ( ) - > str ( ) ) ;
2014-01-04 20:57:02 +01:00
} else if ( tok - > astOperand1 ( ) - > isNumber ( ) & & tok - > astOperand2 ( ) - > isName ( ) ) {
varid = tok - > astOperand2 ( ) - > varId ( ) ;
2014-01-05 20:06:46 +01:00
var = tok - > astOperand2 ( ) - > variable ( ) ;
2014-01-04 20:57:02 +01:00
num = MathLib : : toLongNumber ( tok - > astOperand1 ( ) - > str ( ) ) ;
} else {
continue ;
}
} else if ( Token : : Match ( tok - > previous ( ) , " if|while ( %var% %oror%|&&|) " ) | |
Token : : Match ( tok , " %oror%|&& %var% %oror%|&&|) " ) ) {
varid = tok - > next ( ) - > varId ( ) ;
2014-01-05 20:06:46 +01:00
var = tok - > next ( ) - > variable ( ) ;
2014-01-04 20:57:02 +01:00
num = 0 ;
} else if ( tok - > str ( ) = = " ! " & & tok - > astOperand1 ( ) & & tok - > astOperand1 ( ) - > isName ( ) ) {
varid = tok - > astOperand1 ( ) - > varId ( ) ;
2014-01-05 20:06:46 +01:00
var = tok - > astOperand1 ( ) - > variable ( ) ;
2014-01-04 20:57:02 +01:00
num = 0 ;
} else {
continue ;
}
2014-01-12 11:58:10 +01:00
if ( varid = = 0U | | ! var )
2014-01-04 20:57:02 +01:00
continue ;
2014-01-10 05:47:56 +01:00
// bailout: global non-const variables
2014-01-12 11:58:10 +01:00
if ( var - > isGlobal ( ) & & ! var - > isConst ( ) ) {
2014-01-06 07:44:58 +01:00
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok , " global variable " + var - > nameToken ( ) - > str ( ) ) ;
continue ;
}
2014-01-11 20:25:49 +01:00
// bailout: while-condition, variable is changed in while loop
for ( const Token * tok2 = tok ; tok2 ; tok2 = tok2 - > previous ( ) ) {
if ( tok2 - > str ( ) = = " ) " )
tok2 = tok2 - > link ( ) ;
else if ( tok2 - > str ( ) = = " ( " ) {
if ( Token : : Match ( tok2 - > previous ( ) , " for|while ( " ) & & Token : : Match ( tok2 - > link ( ) , " ) { " ) ) {
const Token * start = tok2 - > link ( ) - > next ( ) ;
const Token * end = start - > link ( ) ;
if ( Token : : findmatch ( start , " ++|--| %varid% ++|--|= " , end , varid ) )
varid = 0U ;
}
break ;
}
}
if ( varid = = 0U ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok , " variable " + var - > nameToken ( ) - > str ( ) + " used in loop " ) ;
continue ;
}
// extra logic for unsigned variables 'i>=1' => possible value can also be 0
2014-01-07 19:20:56 +01:00
const ValueFlow : : Value val ( tok , num ) ;
2014-01-08 06:53:17 +01:00
ValueFlow : : Value val2 ;
2014-01-12 12:38:41 +01:00
if ( num = = 1U & & Token : : Match ( tok , " <=|>= " ) ) {
2014-01-11 07:52:25 +01:00
bool isunsigned = var - > typeEndToken ( ) - > isUnsigned ( ) ;
for ( const Token * type = var - > typeStartToken ( ) ; type ! = var - > typeEndToken ( ) ; type = type - > next ( ) )
isunsigned | = type - > isUnsigned ( ) ;
if ( isunsigned )
val2 = ValueFlow : : Value ( tok , 0 ) ;
}
2014-01-04 20:57:02 +01:00
2014-01-05 20:06:46 +01:00
for ( Token * tok2 = tok - > previous ( ) ; ; tok2 = tok2 - > previous ( ) ) {
if ( ! tok2 ) {
if ( settings - > debugwarnings ) {
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > callstack ;
callstack . push_back ( ErrorLogger : : ErrorMessage : : FileLocation ( tok , tokenlist ) ) ;
ErrorLogger : : ErrorMessage errmsg ( callstack , Severity : : debug , " iterated too far " , " debugValueFlowBeforeCondition " , false ) ;
errorLogger - > reportErr ( errmsg ) ;
}
break ;
}
2014-01-10 16:51:58 +01:00
2014-01-05 20:06:46 +01:00
if ( tok2 - > varId ( ) = = varid ) {
2014-01-06 07:44:58 +01:00
// bailout: assignment
2014-01-08 17:37:39 +01:00
if ( Token : : Match ( tok2 - > previous ( ) , " !!* %var% = " ) ) {
2014-01-06 07:44:58 +01:00
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " assignment of " + tok2 - > str ( ) ) ;
2014-01-06 10:23:17 +01:00
break ;
2014-01-06 07:44:58 +01:00
}
2014-01-11 15:36:09 +01:00
// bailout: variable is used in rhs in assignment to itself
if ( bailoutSelfAssignment ( tok2 ) ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " variable " + tok2 - > str ( ) + " is used in rhs in assignment to itself " ) ;
break ;
}
2014-01-11 14:31:51 +01:00
// assigned by subfunction?
if ( bailoutFunctionPar ( tok2 ) ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " possible assignment of " + tok2 - > str ( ) + " by subfunction " ) ;
break ;
}
2014-01-11 14:54:10 +01:00
// skip if variable is conditionally used in ?: expression
if ( const Token * parent = skipValueInConditionalExpression ( tok2 ) ) {
2014-01-08 06:39:15 +01:00
if ( settings - > debugwarnings )
2014-01-11 12:44:55 +01:00
bailout ( tokenlist ,
errorLogger ,
tok2 ,
2014-01-11 11:30:34 +01:00
" no simplification of " + tok2 - > str ( ) + " within " + ( Token : : Match ( parent , " [?:] " ) ? " ?: " : parent - > str ( ) ) + " expression " ) ;
2014-01-08 06:39:15 +01:00
continue ;
}
2014-01-04 20:57:02 +01:00
tok2 - > values . push_back ( val ) ;
2014-01-08 06:53:17 +01:00
if ( val2 . condition )
tok2 - > values . push_back ( val2 ) ;
2014-01-05 20:06:46 +01:00
if ( var & & tok2 = = var - > nameToken ( ) )
2014-01-04 20:57:02 +01:00
break ;
}
2014-01-06 11:27:56 +01:00
2014-01-11 21:10:01 +01:00
// skip sizeof..
if ( tok2 - > str ( ) = = " ) " & & Token : : Match ( tok2 - > link ( ) - > previous ( ) , " typeof|sizeof ( " ) )
tok2 = tok2 - > link ( ) ;
2014-01-06 11:27:56 +01:00
if ( tok2 - > str ( ) = = " } " ) {
2014-01-10 16:13:39 +01:00
if ( Token : : findmatch ( tok2 - > link ( ) , " %varid% " , tok2 , varid ) ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " variable " + var - > nameToken ( ) - > str ( ) + " stopping on } " ) ;
break ;
} else {
tok2 = tok2 - > link ( ) ;
}
2014-01-12 11:58:10 +01:00
} else if ( tok2 - > str ( ) = = " { " ) {
// if variable is assigned in loop don't look before the loop
if ( tok2 - > previous ( ) & &
( Token : : Match ( tok2 - > previous ( ) , " do " ) | |
( tok2 - > str ( ) = = " ) " & & Token : : Match ( tok2 - > linkAt ( - 1 ) - > previous ( ) , " for|while ( " ) ) ) ) {
const Token * start = tok2 ;
const Token * end = start - > link ( ) ;
if ( Token : : findmatch ( start , " ++|--| %varid% ++|--|= " , end , varid ) ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " variable " + var - > nameToken ( ) - > str ( ) + " is assigned in loop. so valueflow analysis bailout when start of loop is reached. " ) ;
break ;
}
}
// Global variable : stop when leaving the function scope
if ( var - > isGlobal ( ) ) {
if ( ! Token : : Match ( tok2 - > previous ( ) , " )|else { " ) )
break ;
if ( Token : : Match ( tok2 - > previous ( ) , " ) { " ) & &
! Token : : Match ( tok2 - > linkAt ( - 1 ) - > previous ( ) , " if|for|while ( " ) )
break ;
}
2014-01-11 21:21:00 +01:00
} else if ( tok2 - > str ( ) = = " break " ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " variable " + var - > nameToken ( ) - > str ( ) + " stopping on break " ) ;
break ;
2014-01-10 16:51:58 +01:00
}
2014-01-04 20:57:02 +01:00
}
}
}
2014-01-07 19:20:56 +01:00
static void valueFlowForLoop ( TokenList * tokenlist , ErrorLogger * errorLogger , const Settings * settings )
{
for ( Token * tok = tokenlist - > front ( ) ; tok ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " for ( " ) )
continue ;
tok = tok - > tokAt ( 2 ) ;
if ( ! Token : : Match ( tok , " %type%| %var% = %num% ; " ) ) { // TODO: don't use %num%
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok , " For loop not handled " ) ;
continue ;
}
Token * const vartok = tok - > tokAt ( Token : : Match ( tok , " %var% = " ) ? 0 : 1 ) ;
const MathLib : : bigint num1 = MathLib : : toLongNumber ( vartok - > strAt ( 2 ) ) ;
if ( vartok - > varId ( ) = = 0U )
continue ;
tok = vartok - > tokAt ( 4 ) ;
2014-01-10 18:18:49 +01:00
const Token * num2tok = 0 ;
if ( Token : : Match ( tok , " %varid% <|<=|!= " , vartok - > varId ( ) ) ) {
tok = tok - > next ( ) ;
num2tok = tok - > astOperand2 ( ) ;
if ( num2tok & & num2tok - > str ( ) = = " ( " & & ! num2tok - > astOperand2 ( ) )
num2tok = num2tok - > astOperand1 ( ) ;
if ( ! Token : : Match ( num2tok , " %num% " ) )
num2tok = 0 ;
}
if ( ! num2tok ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok , " For loop not handled " ) ;
continue ;
}
const MathLib : : bigint num2 = MathLib : : toLongNumber ( num2tok ? num2tok - > str ( ) : " 0 " ) - ( ( tok - > str ( ) = = " <= " ) ? 0 : 1 ) ;
while ( tok & & tok - > str ( ) ! = " ; " )
tok = tok - > next ( ) ;
if ( ! num2tok | | ! Token : : Match ( tok , " ; %varid% ++ ) { " , vartok - > varId ( ) ) ) {
2014-01-07 19:20:56 +01:00
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok , " For loop not handled " ) ;
continue ;
}
2014-01-10 18:18:49 +01:00
Token * const bodyStart = tok - > tokAt ( 4 ) ;
2014-01-07 19:20:56 +01:00
const Token * const bodyEnd = bodyStart - > link ( ) ;
// Is variable modified inside for loop
bool modified = false ;
for ( const Token * tok2 = bodyStart - > next ( ) ; tok2 ! = bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " %varid% = " , vartok - > varId ( ) ) ) {
modified = true ;
break ;
}
}
if ( modified )
continue ;
for ( Token * tok2 = bodyStart - > next ( ) ; tok2 ! = bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) = = vartok - > varId ( ) ) {
tok2 - > values . push_back ( ValueFlow : : Value ( num1 ) ) ;
tok2 - > values . push_back ( ValueFlow : : Value ( num2 ) ) ;
}
if ( tok2 - > str ( ) = = " { " ) {
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " For loop variable " + vartok - > str ( ) + " stopping on { " ) ;
break ;
}
}
}
}
2014-01-06 16:37:52 +01:00
static void valueFlowSubFunction ( TokenList * tokenlist , ErrorLogger * errorLogger , const Settings * settings )
{
std : : list < ValueFlow : : Value > argvalues ;
for ( Token * tok = tokenlist - > front ( ) ; tok ; tok = tok - > next ( ) ) {
2014-01-08 16:17:47 +01:00
if ( ! Token : : Match ( tok , " [(,] " ) )
continue ;
2014-01-08 06:04:51 +01:00
// passing value(s) to function
2014-01-06 16:37:52 +01:00
if ( Token : : Match ( tok , " [(,] %var% [,)] " ) & & ! tok - > next ( ) - > values . empty ( ) )
argvalues = tok - > next ( ) - > values ;
else if ( Token : : Match ( tok , " [(,] %num% [,) ] " )) {
argvalues . clear ( ) ;
2014-01-07 19:20:56 +01:00
argvalues . push_back ( ValueFlow : : Value ( MathLib : : toLongNumber ( tok - > next ( ) - > str ( ) ) ) ) ;
2014-01-06 16:37:52 +01:00
} else {
2014-01-08 16:17:47 +01:00
// bool operator => values 1/0 are passed to function..
const Token * op = tok - > next ( ) ;
while ( op & & op - > astParent ( ) & & ! Token : : Match ( op - > astParent ( ) , " [(,] " ) )
op = op - > astParent ( ) ;
2014-01-08 16:49:15 +01:00
if ( Token : : Match ( op , " %comp%|%oror%|&&|! " ) ) {
2014-01-08 16:17:47 +01:00
argvalues . clear ( ) ;
argvalues . push_back ( ValueFlow : : Value ( 0 ) ) ;
argvalues . push_back ( ValueFlow : : Value ( 1 ) ) ;
} else {
// possible values are unknown..
continue ;
}
2014-01-06 16:37:52 +01:00
}
const Token * const argumentToken = tok - > next ( ) ;
// is this a function call?
const Token * ftok = tok ;
while ( ftok & & ftok - > str ( ) ! = " ( " )
ftok = ftok - > astParent ( ) ;
if ( ! ftok | | ! ftok - > astOperand1 ( ) | | ! ftok - > astOperand2 ( ) | | ! ftok - > astOperand1 ( ) - > function ( ) )
continue ;
// Get argument nr
unsigned int argnr = 0 ;
for ( const Token * argtok = ftok - > next ( ) ; argtok & & argtok ! = argumentToken ; argtok = argtok - > nextArgument ( ) )
+ + argnr ;
// Get function argument, and check if parameter is passed by value
const Function * const function = ftok - > astOperand1 ( ) - > function ( ) ;
const Variable * const arg = function ? function - > getArgumentVar ( argnr ) : NULL ;
if ( ! Token : : Match ( arg ? arg - > typeStartToken ( ) : NULL , " const| %type% %var% ,|) " ) )
continue ;
// Function scope..
const Scope * const functionScope = function ? function - > functionScope : NULL ;
if ( ! functionScope )
continue ;
// Set value in function scope..
const unsigned int varid2 = arg - > nameToken ( ) - > varId ( ) ;
for ( const Token * tok2 = functionScope - > classStart - > next ( ) ; tok2 ! = functionScope - > classEnd ; tok2 = tok2 - > next ( ) ) {
2014-01-11 07:52:25 +01:00
if ( Token : : Match ( tok2 , " %varid% !!= " , varid2 ) ) {
2014-01-06 16:37:52 +01:00
std : : list < ValueFlow : : Value > & values = const_cast < Token * > ( tok2 ) - > values ;
values . insert ( values . begin ( ) , argvalues . begin ( ) , argvalues . end ( ) ) ;
2014-01-11 07:52:25 +01:00
} else if ( tok2 - > str ( ) = = " { " ) {
2014-01-06 16:37:52 +01:00
if ( settings - > debugwarnings )
bailout ( tokenlist , errorLogger , tok2 , " parameter " + arg - > nameToken ( ) - > str ( ) ) ;
2014-01-08 06:04:51 +01:00
break ;
2014-01-06 16:37:52 +01:00
}
}
}
}
2014-01-05 20:06:46 +01:00
void ValueFlow : : setValues ( TokenList * tokenlist , ErrorLogger * errorLogger , const Settings * settings )
2014-01-04 20:57:02 +01:00
{
2014-01-05 20:06:46 +01:00
for ( Token * tok = tokenlist - > front ( ) ; tok ; tok = tok - > next ( ) )
2014-01-04 20:57:02 +01:00
tok - > values . clear ( ) ;
2014-01-07 19:20:56 +01:00
valueFlowForLoop ( tokenlist , errorLogger , settings ) ;
2014-01-05 20:06:46 +01:00
valueFlowBeforeCondition ( tokenlist , errorLogger , settings ) ;
2014-01-06 16:37:52 +01:00
valueFlowSubFunction ( tokenlist , errorLogger , settings ) ;
2014-01-04 20:57:02 +01:00
}