2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2019-02-09 07:24:06 +01:00
* Copyright ( C ) 2007 - 2019 Cppcheck team .
2009-02-10 20:40:21 +01:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-02-10 20:40:21 +01:00
*/
# include "checkstl.h"
2017-05-27 04:33:47 +02:00
2014-04-12 23:39:52 +02:00
# include "checknullpointer.h"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# include "settings.h"
# include "standards.h"
# include "symboldatabase.h"
# include "token.h"
2015-11-29 10:49:10 +01:00
# include "utils.h"
2018-07-26 22:00:48 +02:00
# include "astutils.h"
2017-05-27 04:33:47 +02:00
# include <cstddef>
# include <list>
2018-10-17 06:36:51 +02:00
# include <map>
2017-05-27 04:33:47 +02:00
# include <set>
2010-10-10 10:52:41 +02:00
# include <sstream>
2017-05-27 04:33:47 +02:00
# include <utility>
2009-02-10 20:40:21 +01:00
2009-03-19 21:20:08 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckStl instance ;
2009-03-19 21:20:08 +01:00
}
2009-03-19 19:24:13 +01:00
2016-06-05 18:24:06 +02:00
// CWE IDs used:
2016-06-07 19:28:32 +02:00
static const struct CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
static const struct CWE CWE597 ( 597U ) ; // Use of Wrong Operator in String Comparison
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 CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
2016-06-07 19:28:32 +02:00
static const struct CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const struct CWE CWE704 ( 704U ) ; // Incorrect Type Conversion or Cast
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 CWE762 ( 762U ) ; // Mismatched Memory Management Routines
2017-08-22 11:04:02 +02:00
static const struct CWE CWE786 ( 786U ) ; // Access of Memory Location Before Start of Buffer
2016-06-07 19:28:32 +02:00
static const struct CWE CWE788 ( 788U ) ; // Access of Memory Location After End of Buffer
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 CWE825 ( 825U ) ; // Expired Pointer Dereference
2016-06-07 19:28:32 +02:00
static const struct CWE CWE834 ( 834U ) ; // Excessive Iteration
2016-01-25 20:01:48 +01:00
2018-08-11 11:40:48 +02:00
void CheckStl : : outOfBounds ( )
{
for ( const Scope * function : mTokenizer - > getSymbolDatabase ( ) - > functionScopes ) {
for ( const Token * tok = function - > bodyStart ; tok ! = function - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! tok - > isName ( ) | | ! tok - > valueType ( ) )
continue ;
const Library : : Container * container = tok - > valueType ( ) - > container ;
if ( ! container )
continue ;
for ( const ValueFlow : : Value & value : tok - > values ( ) ) {
if ( ! value . isContainerSizeValue ( ) )
continue ;
if ( value . isInconclusive ( ) & & ! mSettings - > inconclusive )
continue ;
if ( ! value . errorSeverity ( ) & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
continue ;
if ( value . intvalue = = 0 & & Token : : Match ( tok , " %name% . %name% ( " ) & & container - > getYield ( tok - > strAt ( 2 ) ) = = Library : : Container : : Yield : : ITEM ) {
outOfBoundsError ( tok , & value , nullptr ) ;
continue ;
}
2018-08-11 18:57:21 +02:00
if ( ! container - > arrayLike_indexOp & & ! container - > stdStringLike )
continue ;
2018-08-11 11:40:48 +02:00
if ( value . intvalue = = 0 & & Token : : Match ( tok , " %name% [ " ) ) {
outOfBoundsError ( tok , & value , nullptr ) ;
continue ;
}
if ( container - > arrayLike_indexOp & & Token : : Match ( tok , " %name% [ " ) ) {
const ValueFlow : : Value * indexValue = tok - > next ( ) - > astOperand2 ( ) ? tok - > next ( ) - > astOperand2 ( ) - > getMaxValue ( false ) : nullptr ;
if ( indexValue & & indexValue - > intvalue > = value . intvalue ) {
outOfBoundsError ( tok , & value , indexValue ) ;
continue ;
}
if ( mSettings - > isEnabled ( Settings : : WARNING ) ) {
indexValue = tok - > next ( ) - > astOperand2 ( ) ? tok - > next ( ) - > astOperand2 ( ) - > getMaxValue ( true ) : nullptr ;
if ( indexValue & & indexValue - > intvalue > = value . intvalue ) {
outOfBoundsError ( tok , & value , indexValue ) ;
continue ;
}
}
}
}
}
}
}
void CheckStl : : outOfBoundsError ( const Token * tok , const ValueFlow : : Value * containerSize , const ValueFlow : : Value * index )
{
// Do not warn if both the container size and index are possible
if ( containerSize & & index & & containerSize - > isPossible ( ) & & index - > isPossible ( ) )
return ;
const std : : string varname = tok ? tok - > str ( ) : std : : string ( " var " ) ;
std : : string errmsg ;
if ( ! containerSize )
errmsg = " Out of bounds access of item in container '$symbol' " ;
else if ( containerSize - > intvalue = = 0 ) {
if ( containerSize - > condition )
2018-11-28 13:58:01 +01:00
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( containerSize - > condition ) + " or $symbol is accessed out of bounds when $symbol is empty. " ;
2018-08-11 11:40:48 +02:00
else
2018-11-28 13:58:01 +01:00
errmsg = " Out of bounds access in $symbol because $symbol is empty. " ;
2018-08-11 14:45:12 +02:00
} else if ( index ) {
2018-11-28 07:03:56 +01:00
errmsg = " Accessing $symbol[ " + MathLib : : toString ( index - > intvalue ) + " ] is out of bounds when $symbol size is " + MathLib : : toString ( containerSize - > intvalue ) + " . " ;
if ( containerSize - > condition )
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( containerSize - > condition ) + " or $symbol size can be " + MathLib : : toString ( containerSize - > intvalue ) + " . " + errmsg ;
else if ( index - > condition )
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( index - > condition ) + " or $symbol item " + MathLib : : toString ( index - > intvalue ) + " can be accessed. " + errmsg ;
2018-08-11 14:45:12 +02:00
} else {
// should not happen
return ;
2018-08-11 11:40:48 +02:00
}
ErrorPath errorPath ;
if ( ! index )
errorPath = getErrorPath ( tok , containerSize , " Access out of bounds " ) ;
else {
ErrorPath errorPath1 = getErrorPath ( tok , containerSize , " Access out of bounds " ) ;
ErrorPath errorPath2 = getErrorPath ( tok , index , " Access out of bounds " ) ;
if ( errorPath1 . size ( ) < = 1 )
errorPath = errorPath2 ;
else if ( errorPath2 . size ( ) < = 1 )
errorPath = errorPath1 ;
else {
errorPath = errorPath1 ;
errorPath . splice ( errorPath . end ( ) , errorPath2 ) ;
}
}
reportError ( errorPath ,
( containerSize & & ! containerSize - > errorSeverity ( ) ) | | ( index & & ! index - > errorSeverity ( ) ) ? Severity : : warning : Severity : : error ,
" containerOutOfBounds " ,
" $symbol: " + varname + " \n " + errmsg ,
CWE398 ,
( containerSize & & containerSize - > isInconclusive ( ) ) | | ( index & & index - > isInconclusive ( ) ) ) ;
}
2018-11-28 19:27:28 +01:00
bool CheckStl : : isContainerSize ( const Token * containerToken , const Token * expr ) const
{
if ( ! Token : : simpleMatch ( expr , " ( ) " ) )
return false ;
if ( ! Token : : Match ( expr - > astOperand1 ( ) , " . %name% ( " ) )
return false ;
if ( ! isSameExpression ( mTokenizer - > isCPP ( ) , false , containerToken , expr - > astOperand1 ( ) - > astOperand1 ( ) , mSettings - > library , false , false ) )
return false ;
return containerToken - > valueType ( ) - > container - > getYield ( expr - > previous ( ) - > str ( ) ) = = Library : : Container : : Yield : : SIZE ;
}
bool CheckStl : : isContainerSizeGE ( const Token * containerToken , const Token * expr ) const
{
if ( ! expr )
return false ;
if ( isContainerSize ( containerToken , expr ) )
return true ;
if ( expr - > str ( ) = = " * " ) {
const Token * mul ;
if ( isContainerSize ( containerToken , expr - > astOperand1 ( ) ) )
mul = expr - > astOperand2 ( ) ;
else if ( isContainerSize ( containerToken , expr - > astOperand2 ( ) ) )
mul = expr - > astOperand1 ( ) ;
else
return false ;
return mul & & ( ! mul - > hasKnownIntValue ( ) | | mul - > values ( ) . front ( ) . intvalue ! = 0 ) ;
}
if ( expr - > str ( ) = = " + " ) {
const Token * op ;
if ( isContainerSize ( containerToken , expr - > astOperand1 ( ) ) )
op = expr - > astOperand2 ( ) ;
else if ( isContainerSize ( containerToken , expr - > astOperand2 ( ) ) )
op = expr - > astOperand1 ( ) ;
else
return false ;
return op & & op - > getValueGE ( 0 , mSettings ) ;
}
return false ;
}
void CheckStl : : outOfBoundsIndexExpression ( )
{
for ( const Scope * function : mTokenizer - > getSymbolDatabase ( ) - > functionScopes ) {
for ( const Token * tok = function - > bodyStart ; tok ! = function - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! tok - > isName ( ) | | ! tok - > valueType ( ) )
continue ;
const Library : : Container * container = tok - > valueType ( ) - > container ;
if ( ! container )
continue ;
if ( ! container - > arrayLike_indexOp & & ! container - > stdStringLike )
continue ;
if ( ! Token : : Match ( tok , " %name% [ " ) )
continue ;
if ( isContainerSizeGE ( tok , tok - > next ( ) - > astOperand2 ( ) ) )
outOfBoundsIndexExpressionError ( tok , tok - > next ( ) - > astOperand2 ( ) ) ;
}
}
}
void CheckStl : : outOfBoundsIndexExpressionError ( const Token * tok , const Token * index )
{
const std : : string varname = tok ? tok - > str ( ) : std : : string ( " var " ) ;
const std : : string i = index ? index - > expressionString ( ) : std : : string ( varname + " .size() " ) ;
std : : string errmsg = " Out of bounds access of $symbol, index ' " + i + " ' is out of bounds. " ;
reportError ( tok ,
Severity : : error ,
" containerOutOfBoundsIndexExpression " ,
" $symbol: " + varname + " \n " + errmsg ,
CWE398 ,
false ) ;
}
2018-08-11 11:40:48 +02:00
2009-03-20 20:09:44 +01:00
// Error message for bad iterator usage..
2010-04-17 13:37:04 +02:00
void CheckStl : : invalidIteratorError ( const Token * tok , const std : : string & iteratorName )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " invalidIterator1 " , " $symbol: " + iteratorName + " \n Invalid iterator: $symbol " , CWE664 , false ) ;
2010-04-17 13:37:04 +02:00
}
2018-10-17 06:36:51 +02:00
void CheckStl : : iteratorsError ( const Token * tok , const std : : string & containerName1 , const std : : string & containerName2 )
2009-03-20 20:09:44 +01:00
{
2018-10-17 06:36:51 +02:00
reportError ( tok , Severity : : error , " iterators1 " ,
" $symbol: " + containerName1 + " \n "
" $symbol: " + containerName2 + " \n "
" Same iterator is used with different containers ' " + containerName1 + " ' and ' " + containerName2 + " '. " , CWE664 , false ) ;
}
void CheckStl : : iteratorsError ( const Token * tok , const Token * containerTok , const std : : string & containerName1 , const std : : string & containerName2 )
{
std : : list < const Token * > callstack = { tok , containerTok } ;
reportError ( callstack , Severity : : error , " iterators2 " ,
" $symbol: " + containerName1 + " \n "
" $symbol: " + containerName2 + " \n "
" Same iterator is used with different containers ' " + containerName1 + " ' and ' " + containerName2 + " '. " , CWE664 , false ) ;
}
void CheckStl : : iteratorsError ( const Token * tok , const Token * containerTok , const std : : string & containerName )
{
std : : list < const Token * > callstack = { tok , containerTok } ;
reportError ( callstack , Severity : : error , " iterators3 " ,
" $symbol: " + containerName + " \n "
" Same iterator is used with containers ' " + containerName + " ' that are defined in different scopes. " , CWE664 , false ) ;
}
void CheckStl : : iteratorsCmpError ( const Token * cmpOperatorTok , const Token * containerTok1 , const Token * containerTok2 , const std : : string & containerName1 , const std : : string & containerName2 )
{
std : : list < const Token * > callstack = { cmpOperatorTok , containerTok1 , containerTok2 } ;
reportError ( callstack , Severity : : error , " iteratorsCmp1 " ,
" $symbol: " + containerName1 + " \n "
" $symbol: " + containerName2 + " \n "
" Comparison of iterators from containers ' " + containerName1 + " ' and ' " + containerName2 + " '. " , CWE664 , false ) ;
}
void CheckStl : : iteratorsCmpError ( const Token * cmpOperatorTok , const Token * containerTok1 , const Token * containerTok2 , const std : : string & containerName )
{
std : : list < const Token * > callstack = { cmpOperatorTok , containerTok1 , containerTok2 } ;
reportError ( callstack , Severity : : error , " iteratorsCmp2 " ,
" $symbol: " + containerName + " \n "
" Comparison of iterators from containers ' " + containerName + " ' that are defined in different scopes. " , CWE664 , false ) ;
2009-03-20 20:09:44 +01:00
}
2009-05-03 07:37:39 +02:00
// Error message used when dereferencing an iterator that has been erased..
2018-01-10 09:37:21 +01:00
void CheckStl : : dereferenceErasedError ( const Token * erased , const Token * deref , const std : : string & itername , bool inconclusive )
2009-05-03 07:37:39 +02:00
{
2012-10-17 00:29:06 +02:00
if ( erased ) {
2018-04-09 09:54:39 +02:00
std : : list < const Token * > callstack = { deref , erased } ;
2012-10-14 11:16:48 +02:00
reportError ( callstack , Severity : : error , " eraseDereference " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + itername + " \n "
" Iterator '$symbol' used after element has been erased. \n "
" The iterator '$symbol' is invalid after the element it pointed to has been erased. "
2018-01-10 09:37:21 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , inconclusive ) ;
2012-10-14 11:16:48 +02:00
} else {
reportError ( deref , Severity : : error , " eraseDereference " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + itername + " \n "
" Invalid iterator '$symbol' used. \n "
" The iterator '$symbol' is invalid before being assigned. "
2018-01-10 09:37:21 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , inconclusive ) ;
2012-10-14 11:16:48 +02:00
}
2009-05-03 07:37:39 +02:00
}
2009-03-20 20:09:44 +01:00
2013-10-26 17:48:20 +02:00
static const Token * skipMembers ( const Token * tok )
{
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( tok , " %name% . " ) )
2013-10-26 17:48:20 +02:00
tok = tok - > tokAt ( 2 ) ;
return tok ;
}
2018-01-10 09:37:21 +01:00
static bool isIterator ( const Variable * var , bool & inconclusiveType )
2016-08-14 10:49:48 +02:00
{
// Check that its an iterator
if ( ! var | | ! var - > isLocal ( ) | | ! Token : : Match ( var - > typeEndToken ( ) , " iterator|const_iterator|reverse_iterator|const_reverse_iterator|auto " ) )
return false ;
2018-01-12 09:36:14 +01:00
inconclusiveType = false ;
2017-07-26 20:19:36 +02:00
if ( var - > typeEndToken ( ) - > str ( ) = = " auto " )
return ( var - > nameToken ( ) - > valueType ( ) & & var - > nameToken ( ) - > valueType ( ) - > type = = ValueType : : Type : : ITERATOR ) ;
2016-08-14 10:49:48 +02:00
if ( var - > type ( ) ) { // If it is defined, ensure that it is defined like an iterator
// look for operator* and operator++
const Function * end = var - > type ( ) - > getFunction ( " operator* " ) ;
const Function * incOperator = var - > type ( ) - > getFunction ( " operator++ " ) ;
2018-01-10 09:37:21 +01:00
if ( ! end | | end - > argCount ( ) > 0 | | ! incOperator ) {
2016-08-14 10:49:48 +02:00
return false ;
2018-01-10 09:37:21 +01:00
} else {
inconclusiveType = true ; // heuristics only
}
2016-08-14 10:49:48 +02:00
}
return true ;
}
2018-01-06 16:08:12 +01:00
static std : : string getContainerName ( const Token * containerToken )
{
if ( ! containerToken )
return std : : string ( ) ;
std : : string ret ( containerToken - > str ( ) ) ;
for ( const Token * nametok = containerToken ; nametok ; nametok = nametok - > tokAt ( - 2 ) ) {
if ( ! Token : : Match ( nametok - > tokAt ( - 2 ) , " %name% . " ) )
break ;
ret = nametok - > strAt ( - 2 ) + ' . ' + ret ;
}
return ret ;
}
2018-10-18 20:08:32 +02:00
enum OperandPosition {
2018-10-17 06:36:51 +02:00
Left ,
Right
} ;
static const Token * findIteratorContainer ( const Token * start , const Token * end , unsigned int id )
{
const Token * containerToken = nullptr ;
2018-10-18 20:08:32 +02:00
for ( const Token * tok = start ; tok ! = end ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %varid% = %name% . %name% ( " , id ) ) {
2018-10-17 06:36:51 +02:00
// Iterator is assigned to value
2018-10-18 20:08:32 +02:00
if ( tok - > tokAt ( 5 ) - > valueType ( ) & & tok - > tokAt ( 5 ) - > valueType ( ) - > type = = ValueType : : Type : : ITERATOR ) {
2018-10-17 06:36:51 +02:00
containerToken = tok - > tokAt ( 2 ) ;
}
2018-10-18 20:08:32 +02:00
} else if ( Token : : Match ( tok , " %varid% = %name% ( " , id ) ) {
2018-10-17 06:36:51 +02:00
// Prevent FP: iterator is assigned to something
// TODO: Fix it in future
containerToken = nullptr ;
}
}
return containerToken ;
}
2009-02-10 20:40:21 +01:00
void CheckStl : : iterators ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2012-08-20 13:57:24 +02:00
2018-10-17 06:36:51 +02:00
// Filling map of iterators id and their scope begin
std : : map < unsigned int , const Token * > iteratorScopeBeginInfo ;
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
bool inconclusiveType = false ;
if ( ! isIterator ( var , inconclusiveType ) )
continue ;
const unsigned int iteratorId = var - > declarationId ( ) ;
if ( iteratorId ! = 0 )
iteratorScopeBeginInfo [ iteratorId ] = var - > nameToken ( ) ;
}
// Storage to save found comparison problems to avoid duplicate error messages
std : : set < const Token * > foundOperatorErrors ;
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2018-01-11 09:31:16 +01:00
bool inconclusiveType = false ;
2018-01-10 09:37:21 +01:00
if ( ! isIterator ( var , inconclusiveType ) )
2012-08-12 13:12:17 +02:00
continue ;
2018-10-09 20:10:43 +02:00
if ( inconclusiveType & & ! mSettings - > inconclusive )
continue ;
2011-12-27 11:02:43 +01:00
2018-04-28 09:38:33 +02:00
const unsigned int iteratorId = var - > declarationId ( ) ;
2010-12-30 22:36:25 +01:00
// the validIterator flag says if the iterator has a valid value or not
2018-04-26 08:57:25 +02:00
bool validIterator = Token : : Match ( var - > nameToken ( ) - > next ( ) , " [(=:{] " ) ;
2017-07-27 18:36:33 +02:00
const Scope * invalidationScope = nullptr ;
2012-08-20 13:57:24 +02:00
// The container this iterator can be used with
2018-01-06 16:08:12 +01:00
const Token * containerToken = nullptr ;
2017-07-27 18:36:33 +02:00
const Scope * containerAssignScope = nullptr ;
2012-04-09 07:19:39 +02:00
// When "validatingToken" is reached the validIterator is set to true
2017-07-27 18:36:33 +02:00
const Token * validatingToken = nullptr ;
2010-12-30 22:36:25 +01:00
2017-07-27 18:36:33 +02:00
const Token * eraseToken = nullptr ;
2012-10-14 11:16:48 +02:00
2010-12-30 22:36:25 +01:00
// Scan through the rest of the code and see if the iterator is
// used against other containers.
2018-04-27 22:36:30 +02:00
for ( const Token * tok2 = var - > nameToken ( ) ; tok2 & & tok2 ! = var - > scope ( ) - > bodyEnd ; tok2 = tok2 - > next ( ) ) {
if ( invalidationScope & & tok2 = = invalidationScope - > bodyEnd )
2012-08-20 13:57:24 +02:00
validIterator = true ; // Assume that the iterator becomes valid again
2018-04-27 22:36:30 +02:00
if ( containerAssignScope & & tok2 = = containerAssignScope - > bodyEnd )
2018-01-06 16:08:12 +01:00
containerToken = nullptr ; // We don't know which containers might be used with the iterator
2012-08-20 13:57:24 +02:00
2017-09-08 09:45:30 +02:00
if ( tok2 = = validatingToken ) {
2011-12-17 11:21:34 +01:00
validIterator = true ;
2017-09-08 09:45:30 +02:00
eraseToken = nullptr ;
invalidationScope = nullptr ;
}
2011-12-17 11:21:34 +01:00
2018-10-17 06:36:51 +02:00
// Is comparison expression?
// Check whether iterator compared against different container or iterator of different container?
if ( tok2 - > isComparisonOp ( ) & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand2 ( ) & &
( foundOperatorErrors . find ( tok2 ) = = foundOperatorErrors . end ( ) ) & &
compareIteratorAgainstDifferentContainer ( tok2 , containerToken , iteratorId , iteratorScopeBeginInfo ) ) {
foundOperatorErrors . insert ( tok2 ) ;
2009-07-28 20:05:00 +02:00
}
2010-12-30 22:36:25 +01:00
// Is the iterator used in a insert/erase operation?
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok2 , " %name% . insert|erase ( *| %varid% ) | , " , iteratorId)) {
2011-12-17 11:21:34 +01:00
const Token * itTok = tok2 - > tokAt ( 4 ) ;
if ( itTok - > str ( ) = = " * " ) {
2012-08-20 13:57:24 +02:00
if ( tok2 - > strAt ( 2 ) = = " insert " )
2011-12-17 11:21:34 +01:00
continue ;
itTok = itTok - > next ( ) ;
}
2010-12-30 22:36:25 +01:00
// It is bad to insert/erase an invalid iterator
2010-04-17 13:37:04 +02:00
if ( ! validIterator )
2011-12-17 11:21:34 +01:00
invalidIteratorError ( tok2 , itTok - > str ( ) ) ;
2010-04-17 13:37:04 +02:00
2010-12-30 22:36:25 +01:00
// If insert/erase is used on different container then
// report an error
2018-01-06 16:08:12 +01:00
if ( containerToken & & tok2 - > varId ( ) ! = containerToken - > varId ( ) ) {
2010-04-10 10:22:34 +02:00
// skip error message if container is a set..
2013-02-05 06:46:26 +01:00
const Variable * variableInfo = tok2 - > variable ( ) ;
2014-02-15 08:46:28 +01:00
const Token * decltok = variableInfo ? variableInfo - > typeStartToken ( ) : nullptr ;
2011-03-13 08:48:38 +01:00
2012-11-11 13:32:19 +01:00
if ( Token : : simpleMatch ( decltok , " std :: set " ) )
2012-08-20 17:27:43 +02:00
continue ; // No warning
2010-04-10 10:22:34 +02:00
2011-12-21 06:16:06 +01:00
// skip error message if the iterator is erased/inserted by value
if ( itTok - > previous ( ) - > str ( ) = = " * " )
continue ;
2016-08-04 09:35:16 +02:00
// inserting iterator range..
if ( tok2 - > strAt ( 2 ) = = " insert " ) {
const Token * par2 = itTok - > nextArgument ( ) ;
2016-08-06 18:07:41 +02:00
if ( ! par2 | | par2 - > nextArgument ( ) )
continue ;
while ( par2 - > str ( ) ! = " ) " ) {
2018-01-06 16:08:12 +01:00
if ( par2 - > varId ( ) = = containerToken - > varId ( ) )
2016-08-04 09:35:16 +02:00
break ;
2018-01-11 09:31:16 +01:00
bool inconclusiveType2 = false ;
2018-01-10 09:37:21 +01:00
if ( isIterator ( par2 - > variable ( ) , inconclusiveType2 ) )
2016-08-14 10:49:48 +02:00
break ; // TODO: check if iterator points at same container
2016-08-04 09:35:16 +02:00
if ( par2 - > str ( ) = = " ( " )
par2 = par2 - > link ( ) ;
par2 = par2 - > next ( ) ;
}
2016-08-14 10:49:48 +02:00
if ( par2 - > str ( ) ! = " ) " )
2016-08-04 09:35:16 +02:00
continue ;
}
2010-04-10 10:22:34 +02:00
// Show error message, mismatching iterator is used.
2018-01-06 16:08:12 +01:00
iteratorsError ( tok2 , getContainerName ( containerToken ) , getContainerName ( tok2 ) ) ;
2010-04-10 10:22:34 +02:00
}
2010-12-30 22:36:25 +01:00
// invalidate the iterator if it is erased
2018-01-06 16:08:12 +01:00
else if ( tok2 - > strAt ( 2 ) = = " erase " & & ( tok2 - > strAt ( 4 ) ! = " * " | | ( containerToken & & tok2 - > varId ( ) = = containerToken - > varId ( ) ) ) ) {
2009-07-28 20:05:00 +02:00
validIterator = false ;
2012-10-14 11:16:48 +02:00
eraseToken = tok2 ;
2012-08-20 13:57:24 +02:00
invalidationScope = tok2 - > scope ( ) ;
}
2009-07-30 18:57:58 +02:00
2010-12-30 22:36:25 +01:00
// skip the operation
2011-12-17 11:21:34 +01:00
tok2 = itTok - > next ( ) ;
2009-07-28 20:05:00 +02:00
}
2010-12-30 22:36:25 +01:00
// it = foo.erase(..
// taking the result of an erase is ok
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok2 , " %varid% = %name% . " , iteratorId ) & &
2013-10-26 17:48:20 +02:00
Token : : simpleMatch ( skipMembers ( tok2 - > tokAt ( 2 ) ) , " erase ( " ) ) {
2010-12-30 22:36:25 +01:00
// the returned iterator is valid
2018-01-06 16:08:12 +01:00
validatingToken = skipMembers ( tok2 - > tokAt ( 2 ) ) - > linkAt ( 1 ) ;
tok2 = validatingToken - > link ( ) ;
2009-11-10 17:20:20 +01:00
}
2010-12-30 22:36:25 +01:00
// Reassign the iterator
2018-01-06 16:08:12 +01:00
else if ( Token : : Match ( tok2 , " %varid% = %name% . " , iteratorId ) & &
Token : : Match ( skipMembers ( tok2 - > tokAt ( 2 ) ) , " begin|rbegin|cbegin|crbegin|find ( " ) ) {
validatingToken = skipMembers ( tok2 - > tokAt ( 2 ) ) - > linkAt ( 1 ) ;
containerToken = skipMembers ( tok2 - > tokAt ( 2 ) ) - > tokAt ( - 2 ) ;
2018-01-06 22:25:13 +01:00
if ( containerToken - > varId ( ) = = 0 | | Token : : simpleMatch ( validatingToken , " ) . " ) )
2018-01-06 16:08:12 +01:00
containerToken = nullptr ;
2012-08-20 13:57:24 +02:00
containerAssignScope = tok2 - > scope ( ) ;
// skip ahead
2018-01-06 16:08:12 +01:00
tok2 = validatingToken - > link ( ) ;
2012-08-20 13:57:24 +02:00
}
// Reassign the iterator
2015-12-24 14:40:48 +01:00
else if ( Token : : Match ( tok2 , " %varid% = " , iteratorId ) ) {
2018-09-02 21:04:45 +02:00
break ;
2010-08-17 18:56:11 +02:00
}
2010-12-30 22:36:25 +01:00
2012-09-06 16:30:10 +02:00
// Passing iterator to function. Iterator might be initialized
else if ( Token : : Match ( tok2 , " %varid% ,|) " , iteratorId)) {
validIterator = true ;
}
2010-12-30 22:36:25 +01:00
// Dereferencing invalid iterator?
2011-10-13 20:53:06 +02:00
else if ( ! validIterator & & Token : : Match ( tok2 , " * %varid% " , iteratorId ) ) {
2018-01-10 09:37:21 +01:00
dereferenceErasedError ( eraseToken , tok2 , tok2 - > strAt ( 1 ) , inconclusiveType ) ;
2009-08-01 16:37:24 +02:00
tok2 = tok2 - > next ( ) ;
2015-01-31 10:50:39 +01:00
} else if ( ! validIterator & & Token : : Match ( tok2 , " %varid% . %name% " , iteratorId ) ) {
2018-01-10 09:37:21 +01:00
dereferenceErasedError ( eraseToken , tok2 , tok2 - > str ( ) , inconclusiveType ) ;
2009-08-01 16:37:24 +02:00
tok2 = tok2 - > tokAt ( 2 ) ;
2010-05-15 19:46:42 +02:00
}
2010-12-30 22:36:25 +01:00
// bailout handling. Assume that the iterator becomes valid if we see return/break.
// TODO: better handling
2017-09-08 09:45:30 +02:00
else if ( tok2 - > scope ( ) = = invalidationScope & & Token : : Match ( tok2 , " return|break|continue " ) ) {
2012-04-09 07:19:39 +02:00
validatingToken = Token : : findsimplematch ( tok2 - > next ( ) , " ; " ) ;
2010-07-09 10:50:24 +02:00
}
2010-12-30 22:36:25 +01:00
// bailout handling. Assume that the iterator becomes valid if we see else.
// TODO: better handling
2017-11-03 10:39:57 +01:00
else if ( tok2 - > str ( ) = = " else " ) {
2010-09-18 16:46:38 +02:00
validIterator = true ;
}
2009-02-10 21:01:39 +01:00
}
2009-02-10 20:40:21 +01:00
}
}
2009-02-10 20:56:00 +01:00
2018-10-17 06:36:51 +02:00
bool CheckStl : : compareIteratorAgainstDifferentContainer ( const Token * operatorTok , const Token * containerTok , const unsigned int iteratorId , const std : : map < unsigned int , const Token * > & iteratorScopeBeginInfo )
{
if ( ! containerTok )
return false ;
const Token * otherOperand = nullptr ;
OperandPosition operandPosition ;
2018-10-18 20:08:32 +02:00
if ( operatorTok - > astOperand1 ( ) - > varId ( ) = = iteratorId ) {
2018-10-17 06:36:51 +02:00
otherOperand = operatorTok - > astOperand2 ( ) ;
operandPosition = OperandPosition : : Right ;
2018-10-18 20:08:32 +02:00
} else if ( operatorTok - > astOperand2 ( ) - > varId ( ) = = iteratorId ) {
2018-10-17 06:36:51 +02:00
otherOperand = operatorTok - > astOperand1 ( ) ;
operandPosition = OperandPosition : : Left ;
}
if ( ! otherOperand )
return false ;
const Token * const otherExprPart = otherOperand - > tokAt ( - 3 ) ;
2018-10-18 20:08:32 +02:00
if ( Token : : Match ( otherExprPart , " %name% . end|rend|cend|crend ( ) " ) & & otherExprPart - > varId ( ) ! = containerTok - > varId ( ) ) {
2018-10-17 06:36:51 +02:00
const std : : string & firstContainerName = getContainerName ( containerTok ) ;
const std : : string & secondContainerName = getContainerName ( otherExprPart ) ;
2018-10-18 20:08:32 +02:00
if ( firstContainerName ! = secondContainerName ) {
2018-10-17 06:36:51 +02:00
if ( operandPosition = = OperandPosition : : Right )
iteratorsError ( operatorTok , containerTok , firstContainerName , secondContainerName ) ;
else
iteratorsError ( operatorTok , containerTok , secondContainerName , firstContainerName ) ;
2018-10-18 20:08:32 +02:00
} else {
2018-10-17 06:36:51 +02:00
iteratorsError ( operatorTok , containerTok , firstContainerName ) ;
}
return true ;
2018-10-18 20:08:32 +02:00
} else {
2018-10-17 06:36:51 +02:00
const unsigned int otherId = otherOperand - > varId ( ) ;
auto it = iteratorScopeBeginInfo . find ( otherId ) ;
2018-10-18 20:08:32 +02:00
if ( it ! = iteratorScopeBeginInfo . end ( ) ) {
2018-10-17 06:36:51 +02:00
const Token * otherContainerToken = findIteratorContainer ( it - > second , operatorTok - > astOperand1 ( ) , otherId ) ;
2018-10-18 20:08:32 +02:00
if ( otherContainerToken & & otherContainerToken - > varId ( ) ! = containerTok - > varId ( ) ) {
2018-10-17 06:36:51 +02:00
const std : : string & firstContainerName = getContainerName ( containerTok ) ;
const std : : string & secondContainerName = getContainerName ( otherContainerToken ) ;
2018-10-18 20:08:32 +02:00
if ( firstContainerName ! = secondContainerName ) {
2018-10-17 06:36:51 +02:00
if ( operandPosition = = OperandPosition : : Right )
iteratorsCmpError ( operatorTok , containerTok , otherContainerToken , firstContainerName , secondContainerName ) ;
else
iteratorsCmpError ( operatorTok , containerTok , otherContainerToken , secondContainerName , firstContainerName ) ;
2018-10-18 20:08:32 +02:00
} else {
2018-10-17 06:36:51 +02:00
iteratorsCmpError ( operatorTok , containerTok , otherContainerToken , firstContainerName ) ;
}
return true ;
}
}
}
return false ;
}
2009-02-10 20:56:00 +01:00
2009-10-18 18:42:01 +02:00
// Error message for bad iterator usage..
void CheckStl : : mismatchingContainersError ( const Token * tok )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " mismatchingContainers " , " Iterators of different containers are used together. " , CWE664 , false ) ;
2009-10-18 18:42:01 +02:00
}
2018-07-26 22:00:48 +02:00
void CheckStl : : mismatchingContainerExpressionError ( const Token * tok1 , const Token * tok2 )
{
2018-07-26 22:08:05 +02:00
const std : : string expr1 ( tok1 ? tok1 - > expressionString ( ) : std : : string ( " v1 " ) ) ;
const std : : string expr2 ( tok2 ? tok2 - > expressionString ( ) : std : : string ( " v2 " ) ) ;
2018-07-26 22:03:49 +02:00
reportError ( tok1 , Severity : : warning , " mismatchingContainerExpression " ,
" Iterators to containers from different expressions ' " +
expr1 + " ' and ' " + expr2 + " ' are used together. " , CWE664 , false ) ;
2018-07-26 22:00:48 +02:00
}
2018-08-05 09:10:54 +02:00
void CheckStl : : sameIteratorExpressionError ( const Token * tok )
{
reportError ( tok , Severity : : style , " sameIteratorExpression " , " Same iterators expression are used for algorithm. " , CWE664 , false ) ;
}
2018-04-08 22:54:10 +02:00
static const std : : set < std : : string > algorithm2 = { // func(begin1, end1
" binary_search " , " copy " , " copy_if " , " equal_range "
, " generate " , " is_heap " , " is_heap_until " , " is_partitioned "
, " is_permutation " , " is_sorted " , " is_sorted_until " , " lower_bound " , " make_heap " , " max_element " , " minmax_element "
, " min_element " , " mismatch " , " move " , " move_backward " , " next_permutation " , " partition " , " partition_copy "
, " partition_point " , " pop_heap " , " prev_permutation " , " push_heap " , " random_shuffle " , " remove " , " remove_copy "
, " remove_copy_if " , " remove_if " , " replace " , " replace_copy " , " replace_copy_if " , " replace_if " , " reverse " , " reverse_copy "
, " shuffle " , " sort " , " sort_heap " , " stable_partition " , " stable_sort " , " swap_ranges " , " transform " , " unique "
, " unique_copy " , " upper_bound " , " string " , " wstring " , " u16string " , " u32string "
} ;
static const std : : set < std : : string > algorithm22 = { // func(begin1, end1, begin2, end2
" includes " , " lexicographical_compare " , " merge " , " partial_sort_copy "
, " set_difference " , " set_intersection " , " set_symmetric_difference " , " set_union "
} ;
static const std : : set < std : : string > algorithm1x1 = { // func(begin1, x, end1
" nth_element " , " partial_sort " , " rotate " , " rotate_copy "
} ;
2011-10-18 21:55:41 +02:00
2016-10-27 18:54:15 +02:00
static const std : : string iteratorBeginFuncPattern = " begin|cbegin|rbegin|crbegin " ;
static const std : : string iteratorEndFuncPattern = " end|cend|rend|crend " ;
2011-10-18 21:55:41 +02:00
2016-10-27 18:54:15 +02:00
static const std : : string pattern1x1_1 = " %name% . " + iteratorBeginFuncPattern + " ( ) , " ;
static const std : : string pattern1x1_2 = " %name% . " + iteratorEndFuncPattern + " ( ) ,|) " ;
static const std : : string pattern2 = pattern1x1_1 + pattern1x1_2 ;
2015-06-10 21:14:17 +02:00
2016-10-27 10:25:45 +02:00
static const Variable * getContainer ( const Token * argtok )
{
2016-11-01 14:08:42 +01:00
while ( argtok & & argtok - > astOperand1 ( ) )
argtok = argtok - > astOperand1 ( ) ;
2016-10-27 10:25:45 +02:00
if ( ! Token : : Match ( argtok , " %var% . begin|end|rbegin|rend ( ) " ) ) // TODO: use Library yield
return nullptr ;
const Variable * var = argtok - > variable ( ) ;
2016-10-27 11:53:37 +02:00
if ( var & & Token : : simpleMatch ( var - > typeStartToken ( ) , " std :: " ) )
2016-10-27 10:25:45 +02:00
return var ;
return nullptr ;
}
2018-07-26 22:23:37 +02:00
static const Token * getIteratorExpression ( const Token * tok )
2018-07-26 22:00:48 +02:00
{
2018-07-26 22:23:37 +02:00
if ( ! tok )
return nullptr ;
2018-09-12 17:33:53 +02:00
if ( tok - > isUnaryOp ( " * " ) )
return nullptr ;
2018-07-26 22:23:37 +02:00
if ( ! tok - > isName ( ) ) {
const Token * iter1 = getIteratorExpression ( tok - > astOperand1 ( ) ) ;
if ( iter1 )
return iter1 ;
2018-08-23 06:06:58 +02:00
if ( tok - > str ( ) = = " ( " )
2018-08-21 06:34:30 +02:00
return nullptr ;
2018-07-26 22:23:37 +02:00
const Token * iter2 = getIteratorExpression ( tok - > astOperand2 ( ) ) ;
if ( iter2 )
return iter2 ;
2018-07-26 22:24:00 +02:00
} else if ( Token : : Match ( tok , " begin|cbegin|rbegin|crbegin|end|cend|rend|crend ( " ) ) {
2018-09-07 07:08:02 +02:00
if ( Token : : Match ( tok - > previous ( ) , " . %name% ( ) !!. " ) )
2018-07-26 22:23:37 +02:00
return tok - > previous ( ) - > astOperand1 ( ) ;
2018-09-30 14:49:58 +02:00
if ( ! Token : : simpleMatch ( tok - > previous ( ) , " . " ) & & Token : : Match ( tok , " %name% ( !!) " ) & & ! Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ) . " ) )
2018-07-26 22:23:37 +02:00
return tok - > next ( ) - > astOperand2 ( ) ;
2018-07-26 22:00:48 +02:00
}
return nullptr ;
}
2015-06-10 21:14:17 +02:00
void CheckStl : : mismatchingContainers ( )
{
2010-12-30 22:36:25 +01:00
// Check if different containers are used in various calls of standard functions
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:46:29 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2018-08-23 06:06:58 +02:00
if ( Token : : Match ( tok , " %comp%|- " ) ) {
2018-08-21 06:34:30 +02:00
const Token * iter1 = getIteratorExpression ( tok - > astOperand1 ( ) ) ;
const Token * iter2 = getIteratorExpression ( tok - > astOperand2 ( ) ) ;
2018-09-28 08:38:24 +02:00
if ( iter1 & & iter2 & & ! isSameExpression ( true , false , iter1 , iter2 , mSettings - > library , false , false ) ) {
2018-08-21 06:34:30 +02:00
mismatchingContainerExpressionError ( iter1 , iter2 ) ;
continue ;
}
}
2016-10-27 10:25:45 +02:00
if ( ! Token : : Match ( tok , " %name% ( !!) " ) )
2012-11-16 06:50:49 +01:00
continue ;
2016-10-27 10:25:45 +02:00
const Token * const ftok = tok ;
2018-07-26 22:00:48 +02:00
const Token * firstArg = nullptr ;
2016-10-27 10:25:45 +02:00
2018-07-26 22:23:37 +02:00
const std : : vector < const Token * > args = getArguments ( ftok ) ;
if ( args . size ( ) < 2 )
continue ;
2016-10-27 10:25:45 +02:00
std : : map < const Variable * , unsigned int > containerNr ;
2018-07-26 22:23:37 +02:00
for ( unsigned int argnr = 1 ; argnr < = args . size ( ) ; + + argnr ) {
const Library : : ArgumentChecks : : IteratorInfo * i = mSettings - > library . getArgIteratorInfo ( ftok , argnr ) ;
2016-10-27 10:25:45 +02:00
if ( ! i )
continue ;
2018-07-26 22:23:37 +02:00
const Token * const argTok = args [ argnr - 1 ] ;
2018-08-05 09:10:54 +02:00
if ( i - > first ) {
firstArg = argTok ;
2018-08-05 10:48:28 +02:00
}
2018-09-28 08:38:24 +02:00
if ( i - > last & & firstArg & & argTok & & isSameExpression ( true , false , firstArg , argTok , mSettings - > library , false , false ) ) {
2018-08-05 10:48:28 +02:00
sameIteratorExpressionError ( firstArg ) ;
2018-08-05 09:10:54 +02:00
}
2016-10-27 10:25:45 +02:00
const Variable * c = getContainer ( argTok ) ;
2018-07-26 22:03:49 +02:00
if ( c ) {
2018-07-26 22:00:48 +02:00
std : : map < const Variable * , unsigned int > : : const_iterator it = containerNr . find ( c ) ;
if ( it = = containerNr . end ( ) ) {
for ( it = containerNr . begin ( ) ; it ! = containerNr . end ( ) ; + + it ) {
if ( it - > second = = i - > container ) {
mismatchingContainersError ( argTok ) ;
break ;
}
}
containerNr [ c ] = i - > container ;
} else if ( it - > second ! = i - > container ) {
mismatchingContainersError ( argTok ) ;
}
} else {
2018-08-05 09:10:54 +02:00
if ( i - > last & & firstArg & & argTok ) {
2018-07-26 22:23:37 +02:00
const Token * iter1 = getIteratorExpression ( firstArg ) ;
const Token * iter2 = getIteratorExpression ( argTok ) ;
2018-09-28 08:38:24 +02:00
if ( iter1 & & iter2 & & ! isSameExpression ( true , false , iter1 , iter2 , mSettings - > library , false , false ) ) {
2018-07-26 22:00:48 +02:00
mismatchingContainerExpressionError ( iter1 , iter2 ) ;
2016-10-27 10:25:45 +02:00
}
2012-11-16 06:50:49 +01:00
}
2011-10-18 21:55:41 +02:00
}
2012-04-17 12:21:41 +02:00
}
2018-06-16 16:10:28 +02:00
const int ret = mSettings - > library . returnValueContainer ( ftok ) ;
2016-11-01 14:08:42 +01:00
if ( ret ! = - 1 & & Token : : Match ( ftok - > next ( ) - > astParent ( ) , " ==|!= " ) ) {
const Token * parent = ftok - > next ( ) - > astParent ( ) ;
const Token * other = ( parent - > astOperand1 ( ) = = ftok - > next ( ) ) ? parent - > astOperand2 ( ) : parent - > astOperand1 ( ) ;
const Variable * c = getContainer ( other ) ;
if ( c ) {
2018-04-04 21:51:31 +02:00
const std : : map < const Variable * , unsigned int > : : const_iterator it = containerNr . find ( c ) ;
2016-11-01 14:08:42 +01:00
if ( it = = containerNr . end ( ) | | it - > second ! = ret )
mismatchingContainersError ( other ) ;
}
}
2009-10-18 18:42:01 +02:00
}
}
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2015-07-21 14:13:26 +02:00
if ( var & & var - > isStlStringType ( ) & & Token : : Match ( var - > nameToken ( ) , " %var% ( " ) & & Token : : Match ( var - > nameToken ( ) - > tokAt ( 2 ) , pattern2 . c_str ( ) ) ) {
if ( var - > nameToken ( ) - > strAt ( 2 ) ! = var - > nameToken ( ) - > strAt ( 8 ) ) {
mismatchingContainersError ( var - > nameToken ( ) ) ;
}
}
}
2009-10-18 18:42:01 +02:00
}
2009-02-10 20:56:00 +01:00
void CheckStl : : stlOutOfBounds ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-12-09 22:28:10 +01:00
2012-11-16 06:50:49 +01:00
// Scan through all scopes..
2018-07-13 16:46:29 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
const Token * tok = scope . classDef ;
2013-03-04 19:59:46 +01:00
// only interested in conditions
2018-07-13 16:46:29 +02:00
if ( ( scope . type ! = Scope : : eFor & & scope . type ! = Scope : : eWhile & & scope . type ! = Scope : : eIf & & scope . type ! = Scope : : eDo ) | | ! tok )
2009-02-10 20:56:00 +01:00
continue ;
2018-11-28 20:30:58 +01:00
const Token * condition = nullptr ;
if ( scope . type = = Scope : : eFor ) {
if ( Token : : simpleMatch ( tok - > next ( ) - > astOperand2 ( ) , " ; " ) & & Token : : simpleMatch ( tok - > next ( ) - > astOperand2 ( ) - > astOperand2 ( ) , " ; " ) )
condition = tok - > next ( ) - > astOperand2 ( ) - > astOperand2 ( ) - > astOperand1 ( ) ;
} else if ( Token : : simpleMatch ( tok , " do { " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " } while ( " ) )
condition = tok - > linkAt ( 1 ) - > tokAt ( 2 ) - > astOperand2 ( ) ;
else
condition = tok - > next ( ) - > astOperand2 ( ) ;
2013-03-04 19:59:46 +01:00
2018-11-28 20:30:58 +01:00
if ( ! condition )
2017-07-26 20:29:13 +02:00
continue ;
2013-03-04 19:59:46 +01:00
2018-11-28 20:30:58 +01:00
std : : vector < const Token * > conds ;
visitAstNodes ( condition ,
[ & ] ( const Token * cond ) {
if ( Token : : Match ( cond , " %oror%|&& " ) )
return ChildrenToVisit : : op1_and_op2 ;
if ( cond - > isComparisonOp ( ) )
conds . emplace_back ( cond ) ;
return ChildrenToVisit : : none ;
} ) ;
for ( const Token * cond : conds ) {
const Token * vartok ;
const Token * containerToken ;
if ( Token : : Match ( cond , " <= %var% . %name% ( ) " ) & & Token : : Match ( cond - > astOperand1 ( ) , " %var% " ) ) {
vartok = cond - > astOperand1 ( ) ;
containerToken = cond - > next ( ) ;
} else {
continue ;
}
2013-03-04 19:59:46 +01:00
2018-11-28 20:30:58 +01:00
// Is it a array like container?
const Library : : Container * container = containerToken - > valueType ( ) ? containerToken - > valueType ( ) - > container : nullptr ;
if ( ! container )
continue ;
if ( container - > getYield ( containerToken - > strAt ( 2 ) ) ! = Library : : Container : : SIZE )
continue ;
2013-03-04 19:59:46 +01:00
2018-11-28 20:30:58 +01:00
// variable id for loop variable.
const unsigned int numId = vartok - > varId ( ) ;
// variable id for the container variable
const unsigned int declarationId = containerToken - > varId ( ) ;
const std : : string & containerName = containerToken - > str ( ) ;
for ( const Token * tok3 = scope . bodyStart ; tok3 & & tok3 ! = scope . bodyEnd ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > varId ( ) = = declarationId ) {
tok3 = tok3 - > next ( ) ;
if ( Token : : Match ( tok3 , " . %name% ( ) " ) ) {
if ( container - > getYield ( tok3 - > strAt ( 1 ) ) = = Library : : Container : : SIZE )
break ;
} else if ( container - > arrayLike_indexOp & & Token : : Match ( tok3 , " [ %varid% ] " , numId ) )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 1 ) , containerName , false ) ;
else if ( Token : : Match ( tok3 , " . %name% ( %varid% ) " , numId)) {
const Library : : Container : : Yield yield = container - > getYield ( tok3 - > strAt ( 1 ) ) ;
if ( yield = = Library : : Container : : AT_INDEX )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 3 ) , containerName , true ) ;
}
2009-05-06 21:55:04 +02:00
}
2009-02-10 20:56:00 +01:00
}
}
}
2009-03-21 14:20:10 +01:00
}
2009-02-10 20:56:00 +01:00
2012-02-25 12:43:27 +01:00
void CheckStl : : stlOutOfBoundsError ( const Token * tok , const std : : string & num , const std : : string & var , bool at )
2009-03-21 14:20:10 +01:00
{
2012-02-25 12:43:27 +01:00
if ( at )
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " $symbol: " + var + " \n When " + num + " ==$symbol.size(), $symbol.at( " + num + " ) is out of bounds. " , CWE788 , false ) ;
2012-02-25 12:43:27 +01:00
else
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " $symbol: " + var + " \n When " + num + " ==$symbol.size(), $symbol[ " + num + " ] is out of bounds. " , CWE788 , false ) ;
2009-02-10 20:56:00 +01:00
}
2009-02-11 06:08:29 +01:00
2017-08-22 11:04:02 +02:00
void CheckStl : : negativeIndex ( )
{
// Negative index is out of bounds..
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-08-10 06:47:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-08-22 12:30:43 +02:00
if ( ! Token : : Match ( tok , " %var% [ " ) | | WRONG_DATA ( ! tok - > next ( ) - > astOperand2 ( ) , tok ) )
2017-08-22 11:04:02 +02:00
continue ;
const Variable * const var = tok - > variable ( ) ;
if ( ! var | | tok = = var - > nameToken ( ) )
continue ;
2018-06-16 16:10:28 +02:00
const Library : : Container * const container = mSettings - > library . detectContainer ( var - > typeStartToken ( ) ) ;
2017-08-22 11:04:02 +02:00
if ( ! container | | ! container - > arrayLike_indexOp )
continue ;
2018-06-16 16:10:28 +02:00
const ValueFlow : : Value * index = tok - > next ( ) - > astOperand2 ( ) - > getValueLE ( - 1 , mSettings ) ;
2017-08-22 11:04:02 +02:00
if ( ! index )
continue ;
negativeIndexError ( tok , * index ) ;
}
}
}
void CheckStl : : negativeIndexError ( const Token * tok , const ValueFlow : : Value & index )
{
const ErrorPath errorPath = getErrorPath ( tok , & index , " Negative array index " ) ;
std : : ostringstream errmsg ;
if ( index . condition )
errmsg < < ValueFlow : : eitherTheConditionIsRedundant ( index . condition )
< < " , otherwise there is negative array index " < < index . intvalue < < " . " ;
else
errmsg < < " Array index " < < index . intvalue < < " is out of bounds. " ;
2017-09-20 22:41:36 +02:00
reportError ( errorPath , index . errorSeverity ( ) ? Severity : : error : Severity : : warning , " negativeContainerIndex " , errmsg . str ( ) , CWE786 , index . isInconclusive ( ) ) ;
2017-08-22 11:04:02 +02:00
}
2009-02-11 06:08:29 +01:00
void CheckStl : : erase ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-12-09 22:28:10 +01:00
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . type = = Scope : : eFor & & Token : : simpleMatch ( scope . classDef , " for ( " ) ) {
const Token * tok = scope . classDef - > linkAt ( 1 ) ;
2015-07-23 18:53:31 +02:00
if ( ! Token : : Match ( tok - > tokAt ( - 3 ) , " ; ++| %var% ++| ) { " ) )
continue ;
tok = tok - > previous ( ) ;
if ( ! tok - > isName ( ) )
tok = tok - > previous ( ) ;
2018-08-10 06:47:18 +02:00
eraseCheckLoopVar ( scope , tok - > variable ( ) ) ;
} else if ( scope . type = = Scope : : eWhile & & Token : : Match ( scope . classDef , " while ( %var% != " ) ) {
eraseCheckLoopVar ( scope , scope . classDef - > tokAt ( 2 ) - > variable ( ) ) ;
2015-07-23 18:53:31 +02:00
}
}
}
2010-05-08 20:11:15 +02:00
2015-07-23 18:53:31 +02:00
void CheckStl : : eraseCheckLoopVar ( const Scope & scope , const Variable * var )
{
2018-01-10 09:37:21 +01:00
bool inconclusiveType = false ;
if ( ! isIterator ( var , inconclusiveType ) )
2015-07-23 18:53:31 +02:00
return ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope . bodyStart ; tok ! = scope . bodyEnd ; tok = tok - > next ( ) ) {
2015-07-23 18:53:31 +02:00
if ( tok - > str ( ) ! = " ( " )
continue ;
2015-07-30 10:13:49 +02:00
if ( ! Token : : Match ( tok - > tokAt ( - 2 ) , " . erase ( ++| %varid% ) " , var - > declarationId ( ) ) )
continue ;
if ( Token : : simpleMatch ( tok - > astParent ( ) , " = " ) )
2015-07-23 18:53:31 +02:00
continue ;
// Iterator is invalid..
unsigned int indentlevel = 0U ;
const Token * tok2 = tok - > link ( ) ;
2018-04-27 22:36:30 +02:00
for ( ; tok2 ! = scope . bodyEnd ; tok2 = tok2 - > next ( ) ) {
2015-07-23 18:53:31 +02:00
if ( tok2 - > str ( ) = = " { " ) {
+ + indentlevel ;
continue ;
}
if ( tok2 - > str ( ) = = " } " ) {
if ( indentlevel > 0U )
- - indentlevel ;
2015-07-23 19:13:50 +02:00
else if ( Token : : simpleMatch ( tok2 , " } else { " ) )
2015-07-23 18:53:31 +02:00
tok2 = tok2 - > linkAt ( 2 ) ;
continue ;
}
if ( tok2 - > varId ( ) = = var - > declarationId ( ) ) {
if ( Token : : simpleMatch ( tok2 - > next ( ) , " = " ) )
2009-02-11 06:08:29 +01:00
break ;
2018-01-10 09:37:21 +01:00
dereferenceErasedError ( tok , tok2 , tok2 - > str ( ) , inconclusiveType ) ;
2015-07-23 18:53:31 +02:00
break ;
2009-02-11 06:08:29 +01:00
}
2015-07-23 18:53:31 +02:00
if ( indentlevel = = 0U & & Token : : Match ( tok2 , " break|return|goto " ) )
break ;
2009-02-11 06:08:29 +01:00
}
2018-04-27 22:36:30 +02:00
if ( tok2 = = scope . bodyEnd )
2018-01-10 09:37:21 +01:00
dereferenceErasedError ( tok , scope . classDef , var - > nameToken ( ) - > str ( ) , inconclusiveType ) ;
2009-02-11 06:08:29 +01:00
}
}
2009-02-18 20:57:43 +01:00
void CheckStl : : pushback ( )
{
2012-02-25 12:43:27 +01:00
// Pointer can become invalid after push_back, push_front, reserve or resize..
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-08-10 06:47:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2012-11-16 06:50:49 +01:00
if ( Token : : Match ( tok , " %var% = & %var% [ " ) ) {
2015-11-13 13:22:47 +01:00
// Skip it directly if it is a pointer or an array
2015-11-15 14:55:10 +01:00
const Token * containerTok = tok - > tokAt ( 3 ) ;
if ( containerTok - > variable ( ) & & containerTok - > variable ( ) - > isArrayOrPointer ( ) )
2015-11-13 13:22:47 +01:00
continue ;
2012-11-16 06:50:49 +01:00
// Variable id for pointer
const unsigned int pointerId ( tok - > varId ( ) ) ;
bool invalidPointer = false ;
2014-04-12 23:39:52 +02:00
const Token * function = nullptr ;
2018-04-27 22:36:30 +02:00
const Token * end2 = tok - > scope ( ) - > bodyEnd ;
2014-04-12 23:39:52 +02:00
for ( const Token * tok2 = tok ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
2012-11-16 06:50:49 +01:00
// push_back on vector..
2015-11-15 14:55:10 +01:00
if ( Token : : Match ( tok2 , " %varid% . push_front|push_back|insert|reserve|resize|clear " , containerTok - > varId ( ) ) ) {
2012-11-16 06:50:49 +01:00
invalidPointer = true ;
2014-04-12 23:39:52 +02:00
function = tok2 - > tokAt ( 2 ) ;
2012-11-16 06:50:49 +01:00
}
2009-04-28 21:18:02 +02:00
2012-11-16 06:50:49 +01:00
// Using invalid pointer..
if ( invalidPointer & & tok2 - > varId ( ) = = pointerId ) {
2014-04-12 23:39:52 +02:00
bool unknown = false ;
2018-12-29 09:26:57 +01:00
if ( CheckNullPointer : : isPointerDeRef ( tok2 , unknown , mSettings ) )
2014-04-12 23:39:52 +02:00
invalidPointerError ( tok2 , function - > str ( ) , tok2 - > str ( ) ) ;
2012-11-16 06:50:49 +01:00
break ;
}
2009-04-28 21:18:02 +02:00
}
}
}
}
2012-02-25 12:43:27 +01:00
// Iterator becomes invalid after reserve, resize, insert, push_back or push_front..
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2014-04-12 19:44:37 +02:00
// Check that its an iterator
if ( ! var | | ! var - > isLocal ( ) | | ! Token : : Match ( var - > typeEndToken ( ) , " iterator|const_iterator|reverse_iterator|const_reverse_iterator " ) )
continue ;
2009-10-31 15:27:33 +01:00
2018-04-28 09:38:33 +02:00
const unsigned int iteratorId = var - > declarationId ( ) ;
2014-04-12 19:44:37 +02:00
// ... on std::vector
if ( ! Token : : Match ( var - > typeStartToken ( ) , " std| ::| vector < " ) )
continue ;
2011-01-06 12:20:54 +01:00
2014-04-12 19:44:37 +02:00
// the variable id for the vector
unsigned int vectorid = 0 ;
2011-01-06 12:20:54 +01:00
2017-07-27 18:36:33 +02:00
const Token * validatingToken = nullptr ;
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
std : : string invalidIterator ;
2018-04-27 22:36:30 +02:00
const Token * end2 = var - > scope ( ) - > bodyEnd ;
2014-04-12 19:44:37 +02:00
for ( const Token * tok2 = var - > nameToken ( ) ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
2009-09-27 09:59:19 +02:00
2014-04-12 19:44:37 +02:00
if ( validatingToken = = tok2 ) {
invalidIterator . clear ( ) ;
2017-07-27 18:36:33 +02:00
validatingToken = nullptr ;
2014-04-12 19:44:37 +02:00
}
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
// Using push_back or push_front inside a loop..
if ( Token : : simpleMatch ( tok2 , " for ( " ) ) {
tok2 = tok2 - > tokAt ( 2 ) ;
}
2009-04-25 17:14:02 +02:00
2014-04-12 19:44:37 +02:00
if ( Token : : Match ( tok2 , " %varid% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %varid% != %var% . end|rend|cend|crend ( ) ; ++| %varid% ++| ) { " , iteratorId ) ) {
// variable id for the loop iterator
const unsigned int varId ( tok2 - > tokAt ( 2 ) - > varId ( ) ) ;
2009-09-27 09:59:19 +02:00
2014-04-12 19:44:37 +02:00
const Token * pushbackTok = nullptr ;
2011-01-06 12:20:54 +01:00
2014-04-12 19:44:37 +02:00
// Count { and } for tok3
const Token * tok3 = tok2 - > tokAt ( 20 ) ;
for ( const Token * const end3 = tok3 - > linkAt ( - 1 ) ; tok3 ! = end3 ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > str ( ) = = " break " | | tok3 - > str ( ) = = " return " ) {
2017-07-27 18:36:33 +02:00
pushbackTok = nullptr ;
2014-04-12 19:44:37 +02:00
break ;
} else if ( Token : : Match ( tok3 , " %varid% . push_front|push_back|insert|reserve|resize|clear|erase ( " , varId ) & & ! tok3 - > previous ( ) - > isAssignmentOp ( ) ) {
if ( tok3 - > strAt ( 2 ) ! = " erase " | | ( tok3 - > tokAt ( 4 ) - > varId ( ) ! = iteratorId & & tok3 - > tokAt ( 5 ) - > varId ( ) ! = iteratorId ) ) // This case is handled in: CheckStl::iterators()
2012-11-16 06:50:49 +01:00
pushbackTok = tok3 - > tokAt ( 2 ) ;
2009-07-14 12:40:47 +02:00
}
2009-11-10 19:07:04 +01:00
}
2009-02-18 20:57:43 +01:00
2014-04-12 19:44:37 +02:00
if ( pushbackTok )
invalidIteratorError ( pushbackTok , pushbackTok - > str ( ) , tok2 - > str ( ) ) ;
}
2009-11-02 21:53:01 +01:00
2014-04-12 19:44:37 +02:00
// Assigning iterator..
if ( Token : : Match ( tok2 , " %varid% = " , iteratorId ) ) {
if ( Token : : Match ( tok2 - > tokAt ( 2 ) , " %var% . begin|end|rbegin|rend|cbegin|cend|crbegin|crend|insert|erase|find ( " ) ) {
if ( ! invalidIterator . empty ( ) & & Token : : Match ( tok2 - > tokAt ( 4 ) , " insert|erase ( *| %varid% )|, " , iteratorId ) ) {
invalidIteratorError ( tok2 , invalidIterator , var - > name ( ) ) ;
2012-11-16 06:50:49 +01:00
break ;
}
2014-04-12 19:44:37 +02:00
vectorid = tok2 - > tokAt ( 2 ) - > varId ( ) ;
tok2 = tok2 - > linkAt ( 5 ) ;
} else {
vectorid = 0 ;
}
2017-06-02 20:38:00 +02:00
invalidIterator . clear ( ) ;
2014-04-12 19:44:37 +02:00
}
2009-10-31 15:27:33 +01:00
2014-04-12 19:44:37 +02:00
// push_back on vector..
if ( vectorid > 0 & & Token : : Match ( tok2 , " %varid% . push_front|push_back|insert|reserve|resize|clear|erase ( " , vectorid ) ) {
if ( ! invalidIterator . empty ( ) & & Token : : Match ( tok2 - > tokAt ( 2 ) , " insert|erase ( *| %varid% ,|) " , iteratorId ) ) {
invalidIteratorError ( tok2 , invalidIterator , var - > name ( ) ) ;
break ;
2012-11-16 06:50:49 +01:00
}
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
if ( tok2 - > strAt ( 2 ) ! = " erase " | | ( tok2 - > tokAt ( 4 ) - > varId ( ) ! = iteratorId & & tok2 - > tokAt ( 5 ) - > varId ( ) ! = iteratorId ) ) // This case is handled in: CheckStl::iterators()
invalidIterator = tok2 - > strAt ( 2 ) ;
tok2 = tok2 - > linkAt ( 3 ) ;
}
else if ( tok2 - > str ( ) = = " return " | | tok2 - > str ( ) = = " throw " )
validatingToken = Token : : findsimplematch ( tok2 - > next ( ) , " ; " ) ;
2010-04-16 16:56:55 +02:00
2014-04-12 19:44:37 +02:00
// TODO: instead of bail out for 'else' try to check all execution paths.
else if ( tok2 - > str ( ) = = " break " | | tok2 - > str ( ) = = " else " )
invalidIterator . clear ( ) ;
2012-11-16 06:50:49 +01:00
2014-04-12 19:44:37 +02:00
// Using invalid iterator..
if ( ! invalidIterator . empty ( ) ) {
if ( Token : : Match ( tok2 , " ++|--|*|+|-|(|,|=|!= %varid% " , iteratorId ) )
invalidIteratorError ( tok2 , invalidIterator , tok2 - > strAt ( 1 ) ) ;
if ( Token : : Match ( tok2 , " %varid% ++|--|+|-|. " , iteratorId ) )
invalidIteratorError ( tok2 , invalidIterator , tok2 - > str ( ) ) ;
2009-10-31 15:27:33 +01:00
}
2009-02-18 20:57:43 +01:00
}
}
}
2009-03-21 17:58:13 +01:00
// Error message for bad iterator usage..
2009-10-21 20:15:11 +02:00
void CheckStl : : invalidIteratorError ( const Token * tok , const std : : string & func , const std : : string & iterator_name )
2009-03-21 17:58:13 +01:00
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " invalidIterator2 " ,
" $symbol: " + func + " \n "
" $symbol: " + iterator_name + " \n "
" After " + func + " (), the iterator ' " + iterator_name + " ' may be invalid. " , CWE664 , false ) ;
2009-03-21 17:58:13 +01:00
}
2009-03-18 22:40:38 +01:00
2009-04-28 21:18:02 +02:00
// Error message for bad iterator usage..
2012-10-14 11:16:48 +02:00
void CheckStl : : invalidPointerError ( const Token * tok , const std : : string & func , const std : : string & pointer_name )
2009-04-28 21:18:02 +02:00
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : error , " invalidPointer " ,
" $symbol: " + func + " \n "
" $symbol: " + pointer_name + " \n "
" Invalid pointer ' " + pointer_name + " ' after " + func + " (). " , CWE664 , false ) ;
2009-04-28 21:18:02 +02:00
}
2013-02-10 07:43:09 +01:00
void CheckStl : : stlBoundaries ( )
2009-04-13 17:48:13 +02:00
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2015-11-20 12:46:59 +01:00
if ( ! var | | ! var - > scope ( ) | | ! var - > scope ( ) - > isExecutable ( ) )
continue ;
2018-06-16 16:10:28 +02:00
const Library : : Container * container = mSettings - > library . detectContainer ( var - > typeStartToken ( ) , true ) ;
2015-11-20 12:46:59 +01:00
if ( ! container | | container - > opLessAllowed )
continue ;
2018-04-27 22:36:30 +02:00
const Token * const end = var - > scope ( ) - > bodyEnd ;
2015-11-20 12:46:59 +01:00
for ( const Token * tok = var - > nameToken ( ) ; tok ! = end ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " !!* %varid% < " , var - > declarationId ( ) ) ) {
stlBoundariesError ( tok ) ;
} else if ( Token : : Match ( tok , " > %varid% !!. " , var - > declarationId ( ) ) ) {
stlBoundariesError ( tok ) ;
2009-04-13 17:48:13 +02:00
}
}
}
}
2012-05-05 15:21:27 +02:00
// Error message for bad boundary usage..
2015-11-20 12:46:59 +01:00
void CheckStl : : stlBoundariesError ( const Token * tok )
2009-04-13 17:48:13 +02:00
{
2013-02-10 07:43:09 +01:00
reportError ( tok , Severity : : error , " stlBoundaries " ,
2015-11-20 12:46:59 +01:00
" Dangerous comparison using operator< on iterator. \n "
" Iterator compared with operator<. This is dangerous since the order of items in the "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" container is not guaranteed. One should use operator!= instead to compare iterators. " , CWE664 , false ) ;
2009-04-13 17:48:13 +02:00
}
2009-11-02 20:24:38 +01:00
2015-01-03 11:06:44 +01:00
static bool if_findCompare ( const Token * const tokBack )
2012-06-07 19:33:18 +02:00
{
2015-01-03 11:06:44 +01:00
const Token * tok = tokBack - > astParent ( ) ;
if ( ! tok )
2015-01-03 11:29:13 +01:00
return true ;
2015-01-03 11:06:44 +01:00
if ( tok - > isComparisonOp ( ) )
2015-07-21 20:56:47 +02:00
return ( ! tok - > astOperand1 ( ) - > isNumber ( ) & & ! tok - > astOperand2 ( ) - > isNumber ( ) ) ;
2015-01-03 11:06:44 +01:00
if ( tok - > isArithmeticalOp ( ) ) // result is used in some calculation
return true ; // TODO: check if there is a comparison of the result somewhere
2015-01-04 12:43:31 +01:00
if ( tok - > str ( ) = = " . " )
return true ; // Dereferencing is OK, the programmer might know that the element exists - TODO: An inconclusive warning might be appropriate
2015-01-03 11:29:13 +01:00
if ( tok - > isAssignmentOp ( ) )
return if_findCompare ( tok ) ; // Go one step upwards in the AST
2012-06-07 19:33:18 +02:00
return false ;
}
2010-02-27 21:26:11 +01:00
void CheckStl : : if_find ( )
{
2018-06-16 16:10:28 +02:00
const bool printWarning = mSettings - > isEnabled ( Settings : : WARNING ) ;
const bool printPerformance = mSettings - > isEnabled ( Settings : : PERFORMANCE ) ;
2015-04-10 14:18:52 +02:00
if ( ! printWarning & & ! printPerformance )
2010-02-28 06:56:47 +01:00
return ;
2011-08-14 16:46:35 +02:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-08-14 16:46:35 +02:00
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( ( scope . type ! = Scope : : eIf & & scope . type ! = Scope : : eWhile ) | | ! scope . classDef )
2011-12-09 22:28:10 +01:00
continue ;
2018-08-10 06:47:18 +02:00
for ( const Token * tok = scope . classDef - > next ( ) ; tok - > str ( ) ! = " { " ; tok = tok - > next ( ) ) {
2015-01-04 12:43:31 +01:00
const Token * funcTok = nullptr ;
const Library : : Container * container = nullptr ;
2014-11-18 14:35:36 +01:00
2015-11-20 20:08:35 +01:00
if ( Token : : Match ( tok , " %name% ( " ) )
tok = tok - > linkAt ( 1 ) ;
else if ( tok - > variable ( ) & & Token : : Match ( tok , " %var% . %name% ( " ) ) {
2018-06-16 16:10:28 +02:00
container = mSettings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ;
2015-01-04 12:43:31 +01:00
funcTok = tok - > tokAt ( 2 ) ;
2012-04-03 20:59:45 +02:00
}
2012-01-02 22:52:50 +01:00
2015-01-04 12:43:31 +01:00
// check also for vector-like or pointer containers
2015-01-03 11:06:44 +01:00
else if ( tok - > variable ( ) & & tok - > astParent ( ) & & ( tok - > astParent ( ) - > str ( ) = = " * " | | tok - > astParent ( ) - > str ( ) = = " [ " ) ) {
const Token * tok2 = tok - > astParent ( ) ;
2012-01-02 22:52:50 +01:00
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( tok2 - > astParent ( ) , " . %name% ( " ) )
2012-01-02 22:52:50 +01:00
continue ;
2015-01-04 12:43:31 +01:00
funcTok = tok2 - > astParent ( ) - > next ( ) ;
if ( tok - > variable ( ) - > isArrayOrPointer ( ) )
2018-06-16 16:10:28 +02:00
container = mSettings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ;
2015-01-04 12:43:31 +01:00
else { // Container of container - find the inner container
2018-06-16 16:10:28 +02:00
container = mSettings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ; // outer container
2015-01-04 12:43:31 +01:00
tok2 = Token : : findsimplematch ( tok - > variable ( ) - > typeStartToken ( ) , " < " , tok - > variable ( ) - > typeEndToken ( ) ) ;
if ( container & & container - > type_templateArgNo > = 0 & & tok2 ) {
tok2 = tok2 - > next ( ) ;
2015-01-04 14:31:58 +01:00
for ( int j = 0 ; j < container - > type_templateArgNo ; j + + )
2015-01-04 12:43:31 +01:00
tok2 = tok2 - > nextTemplateArgument ( ) ;
2018-06-16 16:10:28 +02:00
container = mSettings - > library . detectContainer ( tok2 ) ; // innner container
2015-01-04 12:43:31 +01:00
} else
container = nullptr ;
2012-01-02 22:52:50 +01:00
}
}
2015-01-04 12:43:31 +01:00
if ( container & & container - > getAction ( funcTok - > str ( ) ) = = Library : : Container : : FIND ) {
if ( if_findCompare ( funcTok - > next ( ) ) )
continue ;
2015-11-20 18:22:15 +01:00
if ( printWarning & & container - > getYield ( funcTok - > str ( ) ) = = Library : : Container : : ITERATOR )
2015-01-04 12:43:31 +01:00
if_findError ( tok , false ) ;
2016-01-29 08:55:46 +01:00
else if ( printPerformance & & container - > stdStringLike & & funcTok - > str ( ) = = " find " )
2015-01-04 12:43:31 +01:00
if_findError ( tok , true ) ;
2015-04-10 14:18:52 +02:00
} else if ( printWarning & & Token : : Match ( tok , " std :: find|find_if ( " ) ) {
2012-04-03 20:59:45 +02:00
// check that result is checked properly
2015-01-03 11:06:44 +01:00
if ( ! if_findCompare ( tok - > tokAt ( 3 ) ) ) {
2012-04-03 20:59:45 +02:00
if_findError ( tok , false ) ;
}
2010-02-28 07:04:58 +01:00
}
}
2010-02-27 21:26:11 +01:00
}
}
void CheckStl : : if_findError ( const Token * tok , bool str )
{
2010-04-02 07:30:58 +02:00
if ( str )
2012-04-04 19:40:28 +02:00
reportError ( tok , Severity : : performance , " stlIfStrFind " ,
2012-10-14 11:16:48 +02:00
" Inefficient usage of string::find() in condition; string::compare() would be faster. \n "
2013-02-10 07:43:09 +01:00
" Either inefficient or wrong usage of string::find(). string::compare() will be faster if "
2012-04-04 19:40:28 +02:00
" string::find's result is compared with 0, because it will not scan the whole "
" string. If your intention is to check that there are no findings in the string, "
2016-06-05 18:24:06 +02:00
" you should compare with std::string::npos. " , CWE597 , false ) ;
2010-02-27 21:26:11 +01:00
else
CWE mapping of signedCharArrayIndex, unknownSignCharArrayIndex, suspiciousCase, suspiciousEqualityComparison, duplicateBranch, duplicateExpressionTernary, suspiciousSemicolon, incompleteArrayFill, redundantPointerOp, unusedLabelSwitch, unusedLabel, unknownEvaluationOrder, stlIfFind, useAutoPointerCopy
2016-08-25 16:40:23 +02:00
reportError ( tok , Severity : : warning , " stlIfFind " , " Suspicious condition. The result of find() is an iterator, but it is not properly checked. " , CWE398 , false ) ;
2010-02-27 21:26:11 +01:00
}
2013-02-14 16:59:58 +01:00
/**
* Is container . size ( ) slow ?
*/
2015-05-17 20:02:41 +02:00
static bool isCpp03ContainerSizeSlow ( const Token * tok )
2009-12-19 15:24:59 +01:00
{
2014-01-28 17:31:23 +01:00
if ( ! tok )
2013-02-05 06:46:26 +01:00
return false ;
2014-01-28 17:31:23 +01:00
const Variable * var = tok - > variable ( ) ;
2015-05-17 20:02:41 +02:00
return var & & var - > isStlType ( " list " ) ;
2009-12-19 15:24:59 +01:00
}
void CheckStl : : size ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : PERFORMANCE ) )
2010-04-21 08:38:25 +02:00
return ;
2018-06-16 16:10:28 +02:00
if ( mSettings - > standards . cpp = = Standards : : CPP11 )
2015-05-17 20:02:41 +02:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-08-10 06:47:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2012-11-16 06:50:49 +01:00
if ( Token : : Match ( tok , " %var% . size ( ) " ) | |
2015-01-31 10:50:39 +01:00
Token : : Match ( tok , " %name% . %var% . size ( ) " ) ) {
2013-02-05 06:46:26 +01:00
// get the variable
2015-10-14 14:06:04 +02:00
const Token * varTok = tok ;
2013-02-05 06:46:26 +01:00
if ( tok - > strAt ( 2 ) ! = " size " )
2015-10-14 14:06:04 +02:00
varTok = varTok - > tokAt ( 2 ) ;
2012-11-16 06:50:49 +01:00
2015-10-14 14:06:04 +02:00
const Token * const end = varTok - > tokAt ( 5 ) ;
2012-04-04 19:44:57 +02:00
2015-01-31 10:50:39 +01:00
// check for comparison to zero
if ( ( tok - > previous ( ) & & ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " ==|<=|!=|> 0 " ) ) | |
( end - > next ( ) & & ! end - > next ( ) - > isArithmeticalOp ( ) & & Token : : Match ( tok - > tokAt ( - 2 ) , " 0 ==|>=|!=|< " ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) ) {
sizeError ( varTok ) ;
continue ;
}
2015-01-31 10:50:39 +01:00
}
2011-03-29 01:31:23 +02:00
2015-01-31 10:50:39 +01:00
// check for comparison to one
if ( ( tok - > previous ( ) & & ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " >=|< 1 " ) & & ! end - > tokAt ( 2 ) - > isArithmeticalOp ( ) ) | |
( end - > next ( ) & & ! end - > next ( ) - > isArithmeticalOp ( ) & & Token : : Match ( tok - > tokAt ( - 2 ) , " 1 <=|> " ) & & ! tok - > tokAt ( - 3 ) - > isArithmeticalOp ( ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) )
sizeError ( varTok ) ;
2015-01-31 10:50:39 +01:00
}
2012-11-04 16:15:26 +01:00
2015-01-31 10:50:39 +01:00
// check for using as boolean expression
else if ( ( Token : : Match ( tok - > tokAt ( - 2 ) , " if|while ( " ) & & end - > str ( ) = = " ) " ) | |
2015-08-14 20:46:13 +02:00
( tok - > previous ( ) - > tokType ( ) = = Token : : eLogicalOp & & Token : : Match ( end , " &&|)|,|;|%oror% " ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) )
sizeError ( varTok ) ;
2011-03-29 01:31:23 +02:00
}
2009-12-19 15:24:59 +01:00
}
}
}
}
void CheckStl : : sizeError ( const Token * tok )
{
2011-12-24 08:10:16 +01:00
const std : : string varname = tok ? tok - > str ( ) : std : : string ( " list " ) ;
2010-12-04 09:15:48 +01:00
reportError ( tok , Severity : : performance , " stlSize " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + varname + " \n "
" Possible inefficient checking for '$symbol' emptiness. \n "
" Checking for '$symbol' emptiness might be inefficient. "
" Using $symbol.empty() instead of $symbol.size() can be faster. "
" $symbol.size() can take linear time but $symbol.empty() is "
2016-06-05 18:24:06 +02:00
" guaranteed to take constant time. " , CWE398 , false ) ;
2009-12-19 15:24:59 +01:00
}
2010-09-16 18:49:23 +02:00
void CheckStl : : redundantCondition ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : STYLE ) )
2015-04-06 14:10:27 +02:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2014-04-12 22:24:31 +02:00
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . type ! = Scope : : eIf )
2014-04-12 22:24:31 +02:00
continue ;
2018-08-10 06:47:18 +02:00
const Token * tok = scope . classDef - > tokAt ( 2 ) ;
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( tok , " %name% . find ( %any% ) != %name% . end|rend|cend|crend ( ) ) { %name% . remove|erase ( %any% ) ; " ) )
2014-04-12 22:24:31 +02:00
continue ;
2015-01-31 10:50:39 +01:00
// Get tokens for the fields %name% and %any%
2014-04-12 22:24:31 +02:00
const Token * var1 = tok ;
2012-04-04 19:44:57 +02:00
const Token * any1 = var1 - > tokAt ( 4 ) ;
const Token * var2 = any1 - > tokAt ( 3 ) ;
const Token * var3 = var2 - > tokAt ( 7 ) ;
const Token * any2 = var3 - > tokAt ( 4 ) ;
2010-09-16 18:49:23 +02:00
2015-01-31 10:50:39 +01:00
// Check if all the "%name%" fields are the same and if all the "%any%" are the same..
2010-09-16 18:49:23 +02:00
if ( var1 - > str ( ) = = var2 - > str ( ) & &
var2 - > str ( ) = = var3 - > str ( ) & &
2011-10-13 20:53:06 +02:00
any1 - > str ( ) = = any2 - > str ( ) ) {
2010-09-16 18:49:23 +02:00
redundantIfRemoveError ( tok ) ;
}
}
}
void CheckStl : : redundantIfRemoveError ( const Token * tok )
{
2011-02-04 10:10:24 +01:00
reportError ( tok , Severity : : style , " redundantIfRemove " ,
2012-10-14 11:16:48 +02:00
" Redundant checking of STL container element existence before removing it. \n "
2011-02-04 10:10:24 +01:00
" Redundant checking of STL container element existence before removing it. "
2016-06-05 18:24:06 +02:00
" It is safe to call the remove method on a non-existing element. " , CWE398 , false ) ;
2010-09-16 18:49:23 +02:00
}
2010-10-10 10:52:41 +02:00
void CheckStl : : missingComparison ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2013-03-03 11:41:59 +01:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2010-10-10 10:52:41 +02:00
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . type ! = Scope : : eFor | | ! scope . classDef )
2012-02-25 12:43:27 +01:00
continue ;
2010-10-10 10:52:41 +02:00
2018-08-10 06:47:18 +02:00
for ( const Token * tok2 = scope . classDef - > tokAt ( 2 ) ; tok2 ! = scope . bodyStart ; tok2 = tok2 - > next ( ) ) {
2012-02-25 12:43:27 +01:00
if ( tok2 - > str ( ) = = " ; " )
break ;
2010-10-10 10:52:41 +02:00
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( tok2 , " %var% = %name% . begin|rbegin|cbegin|crbegin ( ) ; %name% != %name% . end|rend|cend|crend ( ) ; ++| %name% ++| ) { " ) )
2012-02-25 12:43:27 +01:00
continue ;
2010-10-10 10:52:41 +02:00
2012-02-25 12:43:27 +01:00
// same container
if ( tok2 - > strAt ( 2 ) ! = tok2 - > strAt ( 10 ) )
2012-04-04 19:44:57 +02:00
break ;
const unsigned int iteratorId ( tok2 - > varId ( ) ) ;
// same iterator
if ( iteratorId = = tok2 - > tokAt ( 10 ) - > varId ( ) )
break ;
2010-10-30 11:22:30 +02:00
2012-02-25 12:43:27 +01:00
// increment iterator
2012-04-04 19:44:57 +02:00
if ( ! Token : : Match ( tok2 - > tokAt ( 16 ) , " ++ %varid% ) " , iteratorId ) & &
! Token : : Match ( tok2 - > tokAt ( 16 ) , " %varid% ++ ) " , iteratorId ) ) {
break ;
2012-02-25 12:43:27 +01:00
}
2011-01-06 12:20:54 +01:00
2014-02-16 11:47:52 +01:00
const Token * incrementToken = nullptr ;
2012-02-25 12:43:27 +01:00
// Parse loop..
2018-08-10 06:47:18 +02:00
for ( const Token * tok3 = scope . bodyStart ; tok3 ! = scope . bodyEnd ; tok3 = tok3 - > next ( ) ) {
2012-04-04 19:44:57 +02:00
if ( Token : : Match ( tok3 , " %varid% ++ " , iteratorId ) )
2012-02-25 12:43:27 +01:00
incrementToken = tok3 ;
else if ( Token : : Match ( tok3 - > previous ( ) , " ++ %varid% !!. " , iteratorId ) )
incrementToken = tok3 ;
else if ( Token : : Match ( tok3 , " %varid% !=|== " , iteratorId ) )
2017-07-27 18:36:33 +02:00
incrementToken = nullptr ;
2012-02-25 12:43:27 +01:00
else if ( tok3 - > str ( ) = = " break " | | tok3 - > str ( ) = = " return " )
2017-07-27 18:36:33 +02:00
incrementToken = nullptr ;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok3 , " %varid% = %name% . insert ( ++| %varid% ++| , " , iteratorId ) ) {
2012-02-25 12:43:27 +01:00
// skip insertion..
tok3 = tok3 - > linkAt ( 6 ) ;
if ( ! tok3 )
break ;
2010-10-10 10:52:41 +02:00
}
}
2012-02-25 12:43:27 +01:00
if ( incrementToken )
missingComparisonError ( incrementToken , tok2 - > tokAt ( 16 ) ) ;
2010-10-10 10:52:41 +02:00
}
}
}
void CheckStl : : missingComparisonError ( const Token * incrementToken1 , const Token * incrementToken2 )
{
2018-04-09 09:54:39 +02:00
std : : list < const Token * > callstack = { incrementToken1 , incrementToken2 } ;
2012-10-14 11:16:48 +02:00
2010-10-10 10:52:41 +02:00
std : : ostringstream errmsg ;
2010-11-29 19:27:31 +01:00
errmsg < < " Missing bounds check for extra iterator increment in loop. \n "
2012-10-14 11:16:48 +02:00
< < " The iterator incrementing is suspicious - it is incremented at line " ;
if ( incrementToken1 )
errmsg < < incrementToken1 - > linenr ( ) ;
errmsg < < " and then at line " ;
if ( incrementToken2 )
errmsg < < incrementToken2 - > linenr ( ) ;
errmsg < < " . The loop might unintentionally skip an element in the container. "
2010-11-27 09:57:26 +01:00
< < " There is no comparison between these increments to prevent that the iterator is "
< < " incremented beyond the end. " ;
2010-10-10 10:52:41 +02:00
2016-06-05 18:24:06 +02:00
reportError ( callstack , Severity : : warning , " StlMissingComparison " , errmsg . str ( ) , CWE834 , false ) ;
2010-10-10 10:52:41 +02:00
}
2013-02-05 06:46:26 +01:00
static bool isLocal ( const Token * tok )
2011-12-17 11:21:34 +01:00
{
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > variable ( ) ;
2011-12-18 19:44:38 +01:00
return var & & ! var - > isStatic ( ) & & var - > isLocal ( ) ;
2011-12-17 11:21:34 +01:00
}
2015-06-10 21:14:17 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > stl_string_stream = {
" istringstream " , " ostringstream " , " stringstream " , " wstringstream "
} ;
2015-06-10 21:14:17 +02:00
}
void CheckStl : : string_c_str ( )
{
2018-06-16 16:10:28 +02:00
const bool printInconclusive = mSettings - > inconclusive ;
const bool printPerformance = mSettings - > isEnabled ( Settings : : PERFORMANCE ) ;
2014-01-30 22:09:24 +01:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2012-02-25 12:43:27 +01:00
// Find all functions that take std::string as argument
std : : multimap < std : : string , unsigned int > c_strFuncParam ;
2015-04-10 14:18:52 +02:00
if ( printPerformance ) {
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
for ( const Function & func : scope . functionList ) {
if ( c_strFuncParam . erase ( func . tokenDef - > str ( ) ) ! = 0 ) { // Check if function with this name was already found
c_strFuncParam . insert ( std : : make_pair ( func . tokenDef - > str ( ) , 0 ) ) ; // Disable, because there are overloads. TODO: Handle overloads
2012-02-25 12:43:27 +01:00
continue ;
}
unsigned int numpar = 0 ;
2018-08-10 06:47:18 +02:00
c_strFuncParam . insert ( std : : make_pair ( func . tokenDef - > str ( ) , numpar ) ) ; // Insert function as dummy, to indicate that there is at least one function with that name
for ( const Variable & var : func . argumentList ) {
2012-02-25 12:43:27 +01:00
numpar + + ;
2018-08-10 06:47:18 +02:00
if ( var . isStlStringType ( ) & & ( ! var . isReference ( ) | | var . isConst ( ) ) )
c_strFuncParam . insert ( std : : make_pair ( func . tokenDef - > str ( ) , numpar ) ) ;
2012-02-25 12:43:27 +01:00
}
}
}
}
2011-12-17 11:21:34 +01:00
// Try to detect common problems when using string::c_str()
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . type ! = Scope : : eFunction | | ! scope . function )
2012-02-25 12:43:27 +01:00
continue ;
2011-11-04 19:21:19 +01:00
2012-02-25 12:43:27 +01:00
enum { charPtr , stdString , stdStringConstRef , Other } returnType = Other ;
2018-08-10 06:47:18 +02:00
if ( Token : : Match ( scope . function - > tokenDef - > tokAt ( - 2 ) , " char|wchar_t * " ) )
2012-02-25 12:43:27 +01:00
returnType = charPtr ;
2018-08-10 06:47:18 +02:00
else if ( Token : : Match ( scope . function - > tokenDef - > tokAt ( - 5 ) , " const std :: string|wstring & " ) )
2012-02-25 12:43:27 +01:00
returnType = stdStringConstRef ;
2018-08-10 06:47:18 +02:00
else if ( Token : : Match ( scope . function - > tokenDef - > tokAt ( - 3 ) , " std :: string|wstring !!& " ) )
2012-02-25 12:43:27 +01:00
returnType = stdString ;
2018-08-10 06:47:18 +02:00
for ( const Token * tok = scope . bodyStart ; tok & & tok ! = scope . bodyEnd ; tok = tok - > next ( ) ) {
2012-02-25 12:43:27 +01:00
// Invalid usage..
2014-03-16 19:04:44 +01:00
if ( Token : : Match ( tok , " throw %var% . c_str|data ( ) ; " ) & & isLocal ( tok - > next ( ) ) & &
2015-11-20 11:20:23 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlStringType ( ) ) {
2012-02-25 12:43:27 +01:00
string_c_strThrowError ( tok ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " [;{}] %name% = %var% . str ( ) . c_str|data ( ) ; " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2014-01-30 22:09:24 +01:00
const Variable * var2 = tok - > tokAt ( 3 ) - > variable ( ) ;
if ( var & & var - > isPointer ( ) & & var2 & & var2 - > isStlType ( stl_string_stream ) )
2012-02-25 12:43:27 +01:00
string_c_strError ( tok ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " [;{}] %var% = %name% ( " ) & &
2014-03-16 19:04:44 +01:00
Token : : Match ( tok - > linkAt ( 4 ) , " ) . c_str|data ( ) ; " ) & &
2015-01-31 10:50:39 +01:00
tok - > tokAt ( 3 ) - > function ( ) & & Token : : Match ( tok - > tokAt ( 3 ) - > function ( ) - > retDef , " std :: string|wstring %name% " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2012-02-25 12:43:27 +01:00
if ( var & & var - > isPointer ( ) )
string_c_strError ( tok ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %name% ( !!) " ) & & c_strFuncParam . find ( tok - > str ( ) ) ! = c_strFuncParam . end ( ) & &
2018-08-10 06:47:18 +02:00
! Token : : Match ( tok - > previous ( ) , " ::|. " ) & & tok - > varId ( ) = = 0 & & tok - > str ( ) ! = scope . className ) { // calling function. TODO: Add support for member functions
2018-04-04 21:51:31 +02:00
const std : : pair < std : : multimap < std : : string , unsigned int > : : const_iterator , std : : multimap < std : : string , unsigned int > : : const_iterator > range = c_strFuncParam . equal_range ( tok - > str ( ) ) ;
2012-02-25 12:43:27 +01:00
for ( std : : multimap < std : : string , unsigned int > : : const_iterator i = range . first ; i ! = range . second ; + + i ) {
if ( i - > second = = 0 )
continue ;
const Token * tok2 = tok - > tokAt ( 2 ) ;
unsigned int j ;
for ( j = 0 ; tok2 & & j < i - > second - 1 ; j + + )
tok2 = tok2 - > nextArgument ( ) ;
if ( tok2 )
tok2 = tok2 - > nextArgument ( ) ;
else
break ;
if ( ! tok2 & & j = = i - > second - 1 )
tok2 = tok - > next ( ) - > link ( ) ;
2017-08-02 08:24:16 +02:00
else if ( tok2 )
2012-02-25 12:43:27 +01:00
tok2 = tok2 - > previous ( ) ;
2017-08-02 08:24:16 +02:00
else
break ;
2014-03-16 19:04:44 +01:00
if ( tok2 & & Token : : Match ( tok2 - > tokAt ( - 4 ) , " . c_str|data ( ) " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok2 - > tokAt ( - 5 ) - > variable ( ) ;
2015-11-20 11:20:23 +01:00
if ( var & & var - > isStlStringType ( ) ) {
2012-11-28 08:48:48 +01:00
string_c_strParam ( tok , i - > second ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok2 - > tokAt ( - 9 ) , " %name% . str ( ) " ) ) { // Check ss.str().c_str() as parameter
2014-01-30 22:09:24 +01:00
const Variable * ssVar = tok2 - > tokAt ( - 9 ) - > variable ( ) ;
if ( ssVar & & ssVar - > isStlType ( stl_string_stream ) )
string_c_strParam ( tok , i - > second ) ;
}
2012-11-28 08:48:48 +01:00
}
2012-02-25 12:43:27 +01:00
}
}
2011-11-04 19:21:19 +01:00
2012-02-25 12:43:27 +01:00
// Using c_str() to get the return value is only dangerous if the function returns a char*
2016-05-04 11:10:12 +02:00
if ( ( returnType = = charPtr | | ( printPerformance & & ( returnType = = stdString | | returnType = = stdStringConstRef ) ) ) & & tok - > str ( ) = = " return " ) {
bool err = false ;
const Token * tok2 = tok - > next ( ) ;
if ( Token : : Match ( tok2 , " std :: string|wstring ( " ) & &
Token : : Match ( tok2 - > linkAt ( 3 ) , " ) . c_str|data ( ) ; " ) ) {
err = true ;
} else if ( Token : : simpleMatch ( tok2 , " ( " ) & &
Token : : Match ( tok2 - > link ( ) , " ) . c_str|data ( ) ; " ) ) {
2011-10-26 22:14:47 +02:00
// Check for "+ localvar" or "+ std::string(" inside the bracket
2015-04-10 14:18:52 +02:00
bool is_implicit_std_string = printInconclusive ;
2016-05-04 11:10:12 +02:00
const Token * search_end = tok2 - > link ( ) ;
for ( const Token * search_tok = tok2 - > next ( ) ; search_tok ! = search_end ; search_tok = search_tok - > next ( ) ) {
2014-01-30 22:09:24 +01:00
if ( Token : : Match ( search_tok , " + %var% " ) & & isLocal ( search_tok - > next ( ) ) & &
2015-11-20 11:20:23 +01:00
search_tok - > next ( ) - > variable ( ) & & search_tok - > next ( ) - > variable ( ) - > isStlStringType ( ) ) {
2011-10-26 22:14:47 +02:00
is_implicit_std_string = true ;
break ;
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( search_tok , " + std :: string|wstring ( " ) ) {
2011-10-26 22:14:47 +02:00
is_implicit_std_string = true ;
break ;
}
}
if ( is_implicit_std_string )
2016-05-04 11:10:12 +02:00
err = true ;
2010-10-19 20:21:58 +02:00
}
2016-05-04 11:10:12 +02:00
bool local = false ;
2017-02-26 17:25:32 +01:00
bool ptrOrRef = false ;
2016-05-04 11:10:12 +02:00
const Variable * lastVar = nullptr ;
const Function * lastFunc = nullptr ;
bool funcStr = false ;
if ( Token : : Match ( tok2 , " %var% . " ) ) {
local = isLocal ( tok2 ) ;
2017-03-01 02:03:08 +01:00
bool refToNonLocal = false ;
if ( tok2 - > variable ( ) & & tok2 - > variable ( ) - > isReference ( ) ) {
const Token * refTok = tok2 - > variable ( ) - > nameToken ( ) ;
refToNonLocal = true ; // safe assumption is default to avoid FPs
if ( Token : : Match ( refTok , " %var% = %var% .|;|[ " ) )
refToNonLocal = ! isLocal ( refTok - > tokAt ( 2 ) ) ;
}
ptrOrRef = refToNonLocal | | ( tok2 - > variable ( ) & & tok2 - > variable ( ) - > isPointer ( ) ) ;
2016-05-04 11:10:12 +02:00
}
while ( tok2 ) {
if ( Token : : Match ( tok2 , " %var% .|:: " ) ) {
2017-02-26 17:25:32 +01:00
if ( ptrOrRef )
2016-05-06 17:24:44 +02:00
local = false ;
2016-05-04 11:10:12 +02:00
lastVar = tok2 - > variable ( ) ;
tok2 = tok2 - > tokAt ( 2 ) ;
} else if ( Token : : Match ( tok2 , " %name% ( " ) & & Token : : simpleMatch ( tok2 - > linkAt ( 1 ) , " ) . " ) ) {
lastFunc = tok2 - > function ( ) ;
local = false ;
funcStr = tok2 - > str ( ) = = " str " ;
tok2 = tok2 - > linkAt ( 1 ) - > tokAt ( 2 ) ;
} else
break ;
}
if ( Token : : Match ( tok2 , " c_str|data ( ) ; " ) ) {
2016-05-06 17:24:44 +02:00
if ( ( local | | returnType ! = charPtr ) & & lastVar & & lastVar - > isStlStringType ( ) )
2016-05-04 11:10:12 +02:00
err = true ;
else if ( funcStr & & lastVar & & lastVar - > isStlType ( stl_string_stream ) )
err = true ;
else if ( lastFunc & & Token : : Match ( lastFunc - > tokenDef - > tokAt ( - 3 ) , " std :: string|wstring " ) )
err = true ;
}
if ( err ) {
if ( returnType = = charPtr )
string_c_strError ( tok ) ;
else
string_c_strReturn ( tok ) ;
2012-02-25 12:43:27 +01:00
}
}
2010-10-17 19:18:46 +02:00
}
}
}
2011-11-20 19:26:07 +01:00
void CheckStl : : string_c_strThrowError ( const Token * tok )
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlcstrthrow " , " Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception. \n "
2011-11-20 19:26:07 +01:00
" Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid. " ) ;
}
2011-12-17 11:21:34 +01:00
void CheckStl : : string_c_strError ( const Token * tok )
2010-10-17 19:18:46 +02:00
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlcstr " , " Dangerous usage of c_str(). The value returned by c_str() is invalid after this call. \n "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted. " , CWE664 , false ) ;
2010-10-17 19:18:46 +02:00
}
2012-02-25 12:43:27 +01:00
void CheckStl : : string_c_strReturn ( const Token * tok )
{
reportError ( tok , Severity : : performance , " stlcstrReturn " , " Returning the result of c_str() in a function that returns std::string is slow and redundant. \n "
2016-06-05 18:24:06 +02:00
" The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string. " , CWE704 , false ) ;
2012-02-25 12:43:27 +01:00
}
void CheckStl : : string_c_strParam ( const Token * tok , unsigned int number )
{
std : : ostringstream oss ;
2012-10-14 11:16:48 +02:00
oss < < " Passing the result of c_str() to a function that takes std::string as argument no. " < < number < < " is slow and redundant. \n "
" The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string. " ;
2016-06-05 18:24:06 +02:00
reportError ( tok , Severity : : performance , " stlcstrParam " , oss . str ( ) , CWE704 , false ) ;
2012-02-25 12:43:27 +01:00
}
2011-06-16 20:26:00 +02:00
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
2015-03-19 06:41:54 +01:00
2015-06-10 21:14:17 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > stl_containers_with_empty_and_clear = {
" deque " , " forward_list " , " list " ,
" map " , " multimap " , " multiset " , " set " , " string " ,
" unordered_map " , " unordered_multimap " , " unordered_multiset " ,
" unordered_set " , " vector " , " wstring "
} ;
2015-06-10 21:14:17 +02:00
}
2011-10-24 23:25:23 +02:00
void CheckStl : : uselessCalls ( )
{
2018-06-16 16:10:28 +02:00
const bool printPerformance = mSettings - > isEnabled ( Settings : : PERFORMANCE ) ;
const bool printWarning = mSettings - > isEnabled ( Settings : : WARNING ) ;
2015-04-10 14:18:52 +02:00
if ( ! printPerformance & & ! printWarning )
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-08-10 06:47:18 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2015-04-10 14:18:52 +02:00
if ( printWarning & & Token : : Match ( tok , " %var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %name% [,)] " ) & &
2014-07-23 15:06:27 +02:00
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) ) {
2016-02-02 20:26:02 +01:00
const Variable * var = tok - > variable ( ) ;
if ( ! var | | ! var - > isStlType ( ) )
continue ;
2012-11-16 06:50:49 +01:00
uselessCallsReturnValueError ( tok - > tokAt ( 4 ) , tok - > str ( ) , tok - > strAt ( 2 ) ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% . swap ( %name% ) " ) & &
2014-07-23 15:06:27 +02:00
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) ) {
2016-02-02 20:26:02 +01:00
const Variable * var = tok - > variable ( ) ;
if ( ! var | | ! var - > isStlType ( ) )
continue ;
2012-11-16 06:50:49 +01:00
uselessCallsSwapError ( tok , tok - > str ( ) ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% . substr ( " ) & &
2015-11-20 11:20:23 +01:00
tok - > variable ( ) & & tok - > variable ( ) - > isStlStringType ( ) ) {
2016-02-02 20:26:02 +01:00
if ( Token : : Match ( tok - > tokAt ( 4 ) , " 0| ) " ) ) {
2012-08-10 15:26:07 +02:00
uselessCallsSubstrError ( tok , false ) ;
2016-02-02 20:26:02 +01:00
} else if ( tok - > strAt ( 4 ) = = " 0 " & & tok - > linkAt ( 3 ) - > strAt ( - 1 ) = = " npos " ) {
2014-04-13 19:04:35 +02:00
if ( ! tok - > linkAt ( 3 ) - > previous ( ) - > variable ( ) ) // Make sure that its no variable
2012-11-16 06:50:49 +01:00
uselessCallsSubstrError ( tok , false ) ;
2014-04-13 19:04:35 +02:00
} else if ( Token : : simpleMatch ( tok - > linkAt ( 3 ) - > tokAt ( - 2 ) , " , 0 ) " ) )
2012-11-16 06:50:49 +01:00
uselessCallsSubstrError ( tok , true ) ;
2015-04-10 14:18:52 +02:00
} else if ( printWarning & & Token : : Match ( tok , " [{};] %var% . empty ( ) ; " ) & &
2018-07-10 22:57:59 +02:00
! tok - > tokAt ( 4 ) - > astParent ( ) & &
2014-01-30 22:09:24 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlType ( stl_containers_with_empty_and_clear ) )
2012-11-16 06:50:49 +01:00
uselessCallsEmptyError ( tok - > next ( ) ) ;
else if ( Token : : Match ( tok , " [{};] std :: remove|remove_if|unique ( " ) & & tok - > tokAt ( 5 ) - > nextArgument ( ) )
uselessCallsRemoveError ( tok - > next ( ) , tok - > strAt ( 3 ) ) ;
}
2011-10-24 23:25:23 +02:00
}
}
2011-10-31 21:32:30 +01:00
void CheckStl : : uselessCallsReturnValueError ( const Token * tok , const std : : string & varname , const std : : string & function )
2011-10-24 23:25:23 +02:00
{
std : : ostringstream errmsg ;
2018-04-09 06:43:48 +02:00
errmsg < < " $symbol: " < < varname < < ' \n ' ;
errmsg < < " $symbol: " < < function < < ' \n ' ;
2011-10-31 21:32:30 +01:00
errmsg < < " It is inefficient to call ' " < < varname < < " . " < < function < < " ( " < < varname < < " )' as it always returns 0. \n "
2012-10-14 11:16:48 +02:00
< < " 'std::string:: " < < function < < " ()' returns zero when given itself as parameter "
2011-10-31 21:32:30 +01:00
< < " ( " < < varname < < " . " < < function < < " ( " < < varname < < " )). As it is currently the "
2012-10-14 11:16:48 +02:00
< < " code is inefficient. It is possible either the string searched (' "
< < varname < < " ') or searched for (' " < < varname < < " ') is wrong. " ;
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
reportError ( tok , Severity : : warning , " uselessCallsCompare " , errmsg . str ( ) , CWE628 , false ) ;
2011-10-24 23:25:23 +02:00
}
2011-10-31 21:32:30 +01:00
void CheckStl : : uselessCallsSwapError ( const Token * tok , const std : : string & varname )
2011-10-24 23:25:23 +02:00
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : performance , " uselessCallsSwap " ,
" $symbol: " + varname + " \n "
" It is inefficient to swap a object with itself by calling '$symbol.swap($symbol)' \n "
" The 'swap()' function has no logical effect when given itself as parameter "
" ($symbol.swap($symbol)). As it is currently the "
" code is inefficient. Is the object or the parameter wrong here? " , CWE628 , false ) ;
2011-10-24 23:25:23 +02:00
}
2012-04-04 19:40:28 +02:00
void CheckStl : : uselessCallsSubstrError ( const Token * tok , bool empty )
2011-10-24 23:25:23 +02:00
{
2012-04-04 19:40:28 +02:00
if ( empty )
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
reportError ( tok , Severity : : performance , " uselessCallsSubstr " , " Ineffective call of function 'substr' because it returns an empty string. " , CWE398 , false ) ;
2012-04-04 19:40:28 +02:00
else
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
reportError ( tok , Severity : : performance , " uselessCallsSubstr " , " Ineffective call of function 'substr' because it returns a copy of the object. Use operator= instead. " , CWE398 , false ) ;
2011-10-24 23:25:23 +02:00
}
2012-07-12 12:23:52 +02:00
void CheckStl : : uselessCallsEmptyError ( const Token * tok )
{
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
reportError ( tok , Severity : : warning , " uselessCallsEmpty " , " Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead? " , CWE398 , false ) ;
2012-07-12 12:23:52 +02:00
}
2012-08-21 11:30:27 +02:00
2012-09-07 14:23:32 +02:00
void CheckStl : : uselessCallsRemoveError ( const Token * tok , const std : : string & function )
2012-08-21 11:30:27 +02:00
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : warning , " uselessCallsRemove " ,
" $symbol: " + function + " \n "
" Return value of std::$symbol() ignored. Elements remain in container. \n "
" The return value of std::$symbol() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. "
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
" Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them. " , CWE762 , false ) ;
2012-08-21 11:30:27 +02:00
}
2013-04-05 06:14:59 +02:00
// Check for iterators being dereferenced before being checked for validity.
// E.g. if (*i && i != str.end()) { }
void CheckStl : : checkDereferenceInvalidIterator ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2013-04-05 06:14:59 +02:00
return ;
// Iterate over "if", "while", and "for" conditions where there may
// be an iterator that is dereferenced before being checked for validity.
2018-08-10 06:47:18 +02:00
for ( const Scope & scope : mTokenizer - > getSymbolDatabase ( ) - > scopeList ) {
if ( ! ( scope . type = = Scope : : eIf | | scope . type = = Scope : : eDo | | scope . type = = Scope : : eWhile | | scope . type = = Scope : : eFor ) )
2017-07-26 20:32:14 +02:00
continue ;
2018-08-10 06:47:18 +02:00
const Token * const tok = scope . classDef ;
2017-07-26 20:32:14 +02:00
const Token * startOfCondition = tok - > next ( ) ;
2018-08-10 06:47:18 +02:00
if ( scope . type = = Scope : : eDo )
2017-07-26 20:32:14 +02:00
startOfCondition = startOfCondition - > link ( ) - > tokAt ( 2 ) ;
if ( ! startOfCondition ) // ticket #6626 invalid code
continue ;
const Token * endOfCondition = startOfCondition - > link ( ) ;
if ( ! endOfCondition )
continue ;
2013-04-05 06:14:59 +02:00
2017-07-26 20:32:14 +02:00
// For "for" loops, only search between the two semicolons
2018-08-10 06:47:18 +02:00
if ( scope . type = = Scope : : eFor ) {
2017-07-26 20:32:14 +02:00
startOfCondition = Token : : findsimplematch ( tok - > tokAt ( 2 ) , " ; " , endOfCondition ) ;
if ( ! startOfCondition )
2015-04-06 17:23:48 +02:00
continue ;
2017-07-26 20:32:14 +02:00
endOfCondition = Token : : findsimplematch ( startOfCondition - > next ( ) , " ; " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
if ( ! endOfCondition )
continue ;
2017-07-26 20:32:14 +02:00
}
2013-04-05 06:14:59 +02:00
2017-07-26 20:32:14 +02:00
// Only consider conditions composed of all "&&" terms and
// conditions composed of all "||" terms
const bool isOrExpression =
2017-07-27 18:36:33 +02:00
Token : : findsimplematch ( startOfCondition , " || " , endOfCondition ) ! = nullptr ;
2017-07-26 20:32:14 +02:00
const bool isAndExpression =
2017-07-27 18:36:33 +02:00
Token : : findsimplematch ( startOfCondition , " && " , endOfCondition ) ! = nullptr ;
2017-07-26 20:32:14 +02:00
// Look for a check of the validity of an iterator
2017-07-27 18:36:33 +02:00
const Token * validityCheckTok = nullptr ;
2017-07-26 20:32:14 +02:00
if ( ! isOrExpression & & isAndExpression ) {
validityCheckTok =
Token : : findmatch ( startOfCondition , " && %var% != %name% . end|rend|cend|crend ( ) " , endOfCondition ) ;
} else if ( isOrExpression & & ! isAndExpression ) {
validityCheckTok =
Token : : findmatch ( startOfCondition , " %oror% %var% == %name% . end|rend|cend|crend ( ) " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
}
2017-07-26 20:32:14 +02:00
if ( ! validityCheckTok )
continue ;
const unsigned int iteratorVarId = validityCheckTok - > next ( ) - > varId ( ) ;
// If the iterator dereference is to the left of the check for
// the iterator's validity, report an error.
const Token * const dereferenceTok =
Token : : findmatch ( startOfCondition , " * %varid% " , validityCheckTok , iteratorVarId ) ;
if ( dereferenceTok )
dereferenceInvalidIteratorError ( dereferenceTok , dereferenceTok - > strAt ( 1 ) ) ;
2013-04-05 06:14:59 +02:00
}
}
void CheckStl : : dereferenceInvalidIteratorError ( const Token * deref , const std : : string & iterName )
{
reportError ( deref , Severity : : warning ,
2018-04-09 06:43:48 +02:00
" derefInvalidIterator " ,
" $symbol: " + iterName + " \n "
" Possible dereference of an invalid iterator: $symbol \n "
" Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after. " , CWE825 , false ) ;
2013-04-05 06:14:59 +02:00
}
2014-03-03 18:27:45 +01:00
2018-08-10 11:29:16 +02:00
void CheckStl : : readingEmptyStlContainer2 ( )
{
for ( const Scope * function : mTokenizer - > getSymbolDatabase ( ) - > functionScopes ) {
for ( const Token * tok = function - > bodyStart ; tok ! = function - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! tok - > isName ( ) | | ! tok - > valueType ( ) )
continue ;
const Library : : Container * container = tok - > valueType ( ) - > container ;
if ( ! container )
continue ;
const ValueFlow : : Value * value = tok - > getContainerSizeValue ( 0 ) ;
if ( ! value )
continue ;
if ( value - > isInconclusive ( ) & & ! mSettings - > inconclusive )
continue ;
if ( ! value - > errorSeverity ( ) & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
continue ;
if ( Token : : Match ( tok , " %name% . %name% ( " ) ) {
if ( container - > getYield ( tok - > strAt ( 2 ) ) = = Library : : Container : : Yield : : ITEM )
readingEmptyStlContainerError ( tok , value ) ;
}
}
}
}
void CheckStl : : readingEmptyStlContainerError ( const Token * tok , const ValueFlow : : Value * value )
2014-03-03 18:27:45 +01:00
{
2018-04-09 06:43:48 +02:00
const std : : string varname = tok ? tok - > str ( ) : std : : string ( " var " ) ;
2018-08-10 11:29:16 +02:00
std : : string errmsg ;
if ( value & & value - > condition )
errmsg = " Reading from container '$symbol'. " + ValueFlow : : eitherTheConditionIsRedundant ( value - > condition ) + " or '$symbol' can be empty. " ;
else
errmsg = " Reading from empty STL container '$symbol' " ;
const ErrorPath errorPath = getErrorPath ( tok , value , " Reading from empty container " ) ;
reportError ( errorPath , value ? ( value - > errorSeverity ( ) ? Severity : : error : Severity : : warning ) : Severity : : style , " reademptycontainer " , " $symbol: " + varname + " \n " + errmsg , CWE398 , ! value ) ;
2014-03-03 18:27:45 +01:00
}
2018-09-19 18:58:59 +02:00
void CheckStl : : useStlAlgorithmError ( const Token * tok , const std : : string & algoName )
{
reportError ( tok , Severity : : style , " useStlAlgorithm " ,
" Consider using " + algoName + " algorithm instead of a raw loop. " , CWE398 , false ) ;
}
static bool isEarlyExit ( const Token * start )
{
if ( start - > str ( ) ! = " { " )
return false ;
const Token * endToken = start - > link ( ) ;
const Token * tok = Token : : findmatch ( start , " return|throw|break " , endToken ) ;
if ( ! tok )
return false ;
const Token * endStatement = Token : : findsimplematch ( tok , " ; } " , endToken ) ;
if ( ! endStatement )
return false ;
if ( endStatement - > next ( ) ! = endToken )
return false ;
return true ;
}
static const Token * singleStatement ( const Token * start )
{
if ( start - > str ( ) ! = " { " )
return nullptr ;
const Token * endToken = start - > link ( ) ;
const Token * endStatement = Token : : findsimplematch ( start - > next ( ) , " ; " ) ;
if ( ! Token : : simpleMatch ( endStatement , " ; } " ) )
return nullptr ;
if ( endStatement - > next ( ) ! = endToken )
return nullptr ;
return endStatement ;
}
static const Token * singleAssignInScope ( const Token * start , unsigned int varid , bool & input )
{
const Token * endStatement = singleStatement ( start ) ;
if ( ! endStatement )
return nullptr ;
if ( ! Token : : Match ( start - > next ( ) , " %var% %assign% " ) )
return nullptr ;
const Token * assignTok = start - > tokAt ( 2 ) ;
if ( isVariableChanged ( assignTok - > next ( ) , endStatement , assignTok - > astOperand1 ( ) - > varId ( ) , false , nullptr , true ) )
return nullptr ;
if ( isVariableChanged ( assignTok - > next ( ) , endStatement , varid , false , nullptr , true ) )
return nullptr ;
input = Token : : findmatch ( assignTok - > next ( ) , " %varid% " , endStatement , varid ) ;
return assignTok ;
}
static const Token * singleMemberCallInScope ( const Token * start , unsigned int varid , bool & input )
{
if ( start - > str ( ) ! = " { " )
return nullptr ;
const Token * endToken = start - > link ( ) ;
if ( ! Token : : Match ( start - > next ( ) , " %var% . %name% ( " ) )
return nullptr ;
if ( ! Token : : simpleMatch ( start - > linkAt ( 4 ) , " ) ; } " ) )
return nullptr ;
const Token * endStatement = start - > linkAt ( 4 ) - > next ( ) ;
if ( endStatement - > next ( ) ! = endToken )
return nullptr ;
const Token * dotTok = start - > tokAt ( 2 ) ;
if ( ! Token : : findmatch ( dotTok - > tokAt ( 2 ) , " %varid% " , endStatement , varid ) )
return nullptr ;
input = Token : : Match ( start - > next ( ) , " %var% . %name% ( %varid% ) " , varid ) ;
if ( isVariableChanged ( dotTok - > next ( ) , endStatement , dotTok - > astOperand1 ( ) - > varId ( ) , false , nullptr , true ) )
return nullptr ;
return dotTok ;
}
static const Token * singleIncrementInScope ( const Token * start , unsigned int varid , bool & input )
{
if ( start - > str ( ) ! = " { " )
return nullptr ;
const Token * varTok = nullptr ;
if ( Token : : Match ( start - > next ( ) , " ++ %var% ; } " ) )
varTok = start - > tokAt ( 2 ) ;
else if ( Token : : Match ( start - > next ( ) , " %var% ++ ; } " ))
varTok = start - > tokAt ( 1 ) ;
if ( ! varTok )
return nullptr ;
input = varTok - > varId ( ) = = varid ;
return varTok ;
}
static const Token * singleConditionalInScope ( const Token * start , unsigned int varid )
{
if ( start - > str ( ) ! = " { " )
return nullptr ;
const Token * endToken = start - > link ( ) ;
if ( ! Token : : simpleMatch ( start - > next ( ) , " if ( " ) )
return nullptr ;
if ( ! Token : : simpleMatch ( start - > linkAt ( 2 ) , " ) { " ) )
return nullptr ;
const Token * bodyTok = start - > linkAt ( 2 ) - > next ( ) ;
const Token * endBodyTok = bodyTok - > link ( ) ;
if ( ! Token : : simpleMatch ( endBodyTok , " } } " ) )
return nullptr ;
if ( endBodyTok - > next ( ) ! = endToken )
return nullptr ;
if ( ! Token : : findmatch ( start , " %varid% " , bodyTok , varid ) )
return nullptr ;
if ( isVariableChanged ( start , bodyTok , varid , false , nullptr , true ) )
return nullptr ;
return bodyTok ;
}
static bool addByOne ( const Token * tok , unsigned int varid )
{
if ( Token : : Match ( tok , " += %any% ; " ) & &
tok - > tokAt ( 1 ) - > hasKnownIntValue ( ) & &
tok - > tokAt ( 1 ) - > getValue ( 1 ) ) {
return true ;
}
if ( Token : : Match ( tok , " = %varid% + %any% ; " , varid ) & &
tok - > tokAt ( 3 ) - > hasKnownIntValue ( ) & &
tok - > tokAt ( 3 ) - > getValue ( 1 ) ) {
return true ;
}
return false ;
}
static bool accumulateBoolLiteral ( const Token * tok , unsigned int varid )
{
// TODO: Missing %oreq%
if ( Token : : Match ( tok , " =|&= %bool% ; " ) & &
tok - > tokAt ( 1 ) - > hasKnownIntValue ( ) ) {
return true ;
}
if ( Token : : Match ( tok , " = %varid% %oror%|%or%|&&|& %bool% ; " , varid ) & &
tok - > tokAt ( 3 ) - > hasKnownIntValue ( ) ) {
return true ;
}
return false ;
}
static bool accumulateBool ( const Token * tok , unsigned int varid )
{
// TODO: Missing %oreq%
if ( Token : : simpleMatch ( tok , " &= " ) ) {
return true ;
}
if ( Token : : Match ( tok , " = %varid% %oror%|%or%|&&|& " , varid ) ) {
return true ;
}
return false ;
}
static bool hasVarIds ( const Token * tok , unsigned int var1 , unsigned int var2 )
{
if ( tok - > astOperand1 ( ) - > varId ( ) = = tok - > astOperand2 ( ) - > varId ( ) )
return false ;
if ( tok - > astOperand1 ( ) - > varId ( ) = = var1 | | tok - > astOperand1 ( ) - > varId ( ) = = var2 ) {
if ( tok - > astOperand2 ( ) - > varId ( ) = = var1 | | tok - > astOperand2 ( ) - > varId ( ) = = var2 ) {
return true ;
}
}
return false ;
}
static std : : string flipMinMax ( const std : : string & algo )
{
if ( algo = = " std::max_element " )
return " std::min_element " ;
if ( algo = = " std::min_element " )
return " std::max_element " ;
return algo ;
}
static std : : string minmaxCompare ( const Token * condTok , unsigned int loopVar , unsigned int assignVar , bool invert = false )
{
if ( ! Token : : Match ( condTok , " <|<=|>=|> " ) )
return " std::accumulate " ;
if ( ! hasVarIds ( condTok , loopVar , assignVar ) )
return " std::accumulate " ;
std : : string algo = " std::max_element " ;
if ( Token : : Match ( condTok , " <|<= " ) )
algo = " std::min_element " ;
if ( condTok - > astOperand1 ( ) - > varId ( ) = = assignVar )
algo = flipMinMax ( algo ) ;
if ( invert )
algo = flipMinMax ( algo ) ;
return algo ;
}
void CheckStl : : useStlAlgorithm ( )
{
if ( ! mSettings - > isEnabled ( Settings : : STYLE ) )
return ;
for ( const Scope * function : mTokenizer - > getSymbolDatabase ( ) - > functionScopes ) {
for ( const Token * tok = function - > bodyStart ; tok ! = function - > bodyEnd ; tok = tok - > next ( ) ) {
// Parse range-based for loop
if ( ! Token : : simpleMatch ( tok , " for ( " ) )
continue ;
if ( ! Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) { " ) )
continue ;
const Token * bodyTok = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * splitTok = tok - > next ( ) - > astOperand2 ( ) ;
if ( ! Token : : simpleMatch ( splitTok , " : " ) )
continue ;
const Token * loopVar = splitTok - > previous ( ) ;
if ( ! Token : : Match ( loopVar , " %var% " ) )
continue ;
// Check for single assignment
bool useLoopVarInAssign ;
const Token * assignTok = singleAssignInScope ( bodyTok , loopVar - > varId ( ) , useLoopVarInAssign ) ;
if ( assignTok ) {
unsigned int assignVarId = assignTok - > astOperand1 ( ) - > varId ( ) ;
std : : string algo ;
if ( assignVarId = = loopVar - > varId ( ) ) {
if ( useLoopVarInAssign )
algo = " std::transform " ;
else if ( Token : : Match ( assignTok - > next ( ) , " %var%|%bool%|%num%|%char% ; " ))
algo = " std::fill " ;
else if ( Token : : Match ( assignTok - > next ( ) , " %name% ( ) " ) )
algo = " std::generate " ;
else
algo = " std::fill or std::generate " ;
} else {
if ( addByOne ( assignTok , assignVarId ) )
algo = " std::distance " ;
else if ( accumulateBool ( assignTok , assignVarId ) )
algo = " std::any_of, std::all_of, std::none_of, or std::accumulate " ;
else if ( Token : : Match ( assignTok , " = %var% <|<=|>=|> %var% ? %var% : %var% " ) & & hasVarIds ( assignTok - > tokAt ( 6 ) , loopVar - > varId ( ) , assignVarId ) )
algo = minmaxCompare ( assignTok - > tokAt ( 2 ) , loopVar - > varId ( ) , assignVarId , assignTok - > tokAt ( 5 ) - > varId ( ) = = assignVarId ) ;
else
algo = " std::accumulate " ;
}
useStlAlgorithmError ( assignTok , algo ) ;
continue ;
}
// Check for container calls
bool useLoopVarInMemCall ;
const Token * memberAccessTok = singleMemberCallInScope ( bodyTok , loopVar - > varId ( ) , useLoopVarInMemCall ) ;
if ( memberAccessTok ) {
const Token * memberCallTok = memberAccessTok - > astOperand2 ( ) ;
2018-09-23 20:24:51 +02:00
const unsigned int contVarId = memberAccessTok - > astOperand1 ( ) - > varId ( ) ;
2018-09-19 18:58:59 +02:00
if ( contVarId = = loopVar - > varId ( ) )
continue ;
if ( memberCallTok - > str ( ) = = " push_back " | |
memberCallTok - > str ( ) = = " push_front " | |
memberCallTok - > str ( ) = = " emplace_back " ) {
std : : string algo ;
if ( useLoopVarInMemCall )
algo = " std::copy " ;
else
algo = " std::transform " ;
2018-09-21 10:38:30 +02:00
useStlAlgorithmError ( memberCallTok , algo ) ;
2018-09-19 18:58:59 +02:00
}
continue ;
}
// Check for increment in loop
bool useLoopVarInIncrement ;
const Token * incrementTok = singleIncrementInScope ( bodyTok , loopVar - > varId ( ) , useLoopVarInIncrement ) ;
if ( incrementTok ) {
std : : string algo ;
if ( useLoopVarInIncrement )
algo = " std::transform " ;
else
algo = " std::distance " ;
useStlAlgorithmError ( incrementTok , algo ) ;
continue ;
}
// Check for conditionals
const Token * condBodyTok = singleConditionalInScope ( bodyTok , loopVar - > varId ( ) ) ;
if ( condBodyTok ) {
// Check for single assign
assignTok = singleAssignInScope ( condBodyTok , loopVar - > varId ( ) , useLoopVarInAssign ) ;
if ( assignTok ) {
2018-09-23 20:24:51 +02:00
const unsigned int assignVarId = assignTok - > astOperand1 ( ) - > varId ( ) ;
2018-09-19 18:58:59 +02:00
std : : string algo ;
if ( assignVarId = = loopVar - > varId ( ) ) {
if ( useLoopVarInAssign )
algo = " std::transform " ;
else
algo = " std::replace_if " ;
} else {
if ( addByOne ( assignTok , assignVarId ) )
algo = " std::count_if " ;
else if ( accumulateBoolLiteral ( assignTok , assignVarId ) )
algo = " std::any_of, std::all_of, std::none_of, or std::accumulate " ;
else
algo = " std::accumulate " ;
}
useStlAlgorithmError ( assignTok , algo ) ;
continue ;
}
// Check for container call
memberAccessTok = singleMemberCallInScope ( condBodyTok , loopVar - > varId ( ) , useLoopVarInMemCall ) ;
if ( memberAccessTok ) {
const Token * memberCallTok = memberAccessTok - > astOperand2 ( ) ;
2018-09-23 20:24:51 +02:00
const unsigned int contVarId = memberAccessTok - > astOperand1 ( ) - > varId ( ) ;
2018-09-19 18:58:59 +02:00
if ( contVarId = = loopVar - > varId ( ) )
continue ;
if ( memberCallTok - > str ( ) = = " push_back " | |
memberCallTok - > str ( ) = = " push_front " | |
memberCallTok - > str ( ) = = " emplace_back " ) {
if ( useLoopVarInMemCall )
useStlAlgorithmError ( memberAccessTok , " std::copy_if " ) ;
// There is no transform_if to suggest
}
continue ;
}
// Check for increment in loop
incrementTok = singleIncrementInScope ( condBodyTok , loopVar - > varId ( ) , useLoopVarInIncrement ) ;
if ( incrementTok ) {
std : : string algo ;
if ( useLoopVarInIncrement )
algo = " std::transform " ;
else
algo = " std::count_if " ;
useStlAlgorithmError ( incrementTok , algo ) ;
continue ;
}
// Check early return
if ( isEarlyExit ( condBodyTok ) ) {
const Token * loopVar2 = Token : : findmatch ( condBodyTok , " %varid% " , condBodyTok - > link ( ) , loopVar - > varId ( ) ) ;
std : : string algo ;
if ( loopVar2 )
algo = " std::find_if " ;
else
algo = " std::any_of " ;
useStlAlgorithmError ( condBodyTok , algo ) ;
continue ;
}
}
}
}
}