2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2020-05-10 11:16:32 +02:00
* Copyright ( C ) 2007 - 2020 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
2020-08-17 23:36:45 +02:00
# include "check.h"
# include "checknullpointer.h"
2020-04-13 13:44:48 +02:00
# include "library.h"
# include "mathlib.h"
2017-05-27 04:33:47 +02:00
# 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"
2019-10-30 17:57:46 +01:00
# include "pathanalysis.h"
2020-06-06 17:54:56 +02:00
# include "valueflow.h"
2017-05-27 04:33:47 +02:00
2020-08-26 21:05:17 +02:00
# include <algorithm>
2017-05-27 04:33:47 +02:00
# include <cstddef>
2020-08-26 21:05:17 +02:00
# include <iterator>
2017-05-27 04:33:47 +02:00
# 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
2020-06-16 02:40:54 +02:00
static const struct CWE CWE667 ( 667U ) ; // Improper Locking
2016-06-07 19:28:32 +02:00
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
2020-06-16 02:40:54 +02:00
static const struct CWE CWE833 ( 833U ) ; // Deadlock
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 ( ) ) {
2019-08-12 12:58:53 +02:00
const Library : : Container * container = getLibraryContainer ( tok ) ;
2018-08-11 11:40:48 +02:00
if ( ! container )
continue ;
2019-08-15 21:14:27 +02:00
const Token * parent = astParentSkipParens ( tok ) ;
2018-08-11 11:40:48 +02:00
for ( const ValueFlow : : Value & value : tok - > values ( ) ) {
if ( ! value . isContainerSizeValue ( ) )
continue ;
2019-09-20 15:06:37 +02:00
if ( value . isImpossible ( ) )
continue ;
2018-08-11 11:40:48 +02:00
if ( value . isInconclusive ( ) & & ! mSettings - > inconclusive )
continue ;
if ( ! value . errorSeverity ( ) & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
continue ;
2019-08-12 12:58:53 +02:00
if ( value . intvalue = = 0 & & Token : : Match ( parent , " . %name% ( " ) & & container - > getYield ( parent - > strAt ( 1 ) ) = = Library : : Container : : Yield : : ITEM ) {
outOfBoundsError ( parent - > tokAt ( 2 ) , tok - > expressionString ( ) , & value , parent - > strAt ( 1 ) , nullptr ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
2019-03-28 12:49:52 +01:00
if ( Token : : Match ( tok , " %name% . %name% ( " ) & & container - > getYield ( tok - > strAt ( 2 ) ) = = Library : : Container : : Yield : : START_ITERATOR ) {
2019-08-12 12:58:53 +02:00
const Token * fparent = tok - > tokAt ( 3 ) - > astParent ( ) ;
2019-03-28 12:49:52 +01:00
const Token * other = nullptr ;
2019-08-12 12:58:53 +02:00
if ( Token : : simpleMatch ( fparent , " + " ) & & fparent - > astOperand1 ( ) = = tok - > tokAt ( 3 ) )
other = fparent - > astOperand2 ( ) ;
else if ( Token : : simpleMatch ( fparent , " + " ) & & fparent - > astOperand2 ( ) = = tok - > tokAt ( 3 ) )
other = fparent - > astOperand1 ( ) ;
2019-03-29 15:20:17 +01:00
if ( other & & other - > hasKnownIntValue ( ) & & other - > getKnownIntValue ( ) > value . intvalue ) {
2019-08-12 12:58:53 +02:00
outOfBoundsError ( fparent , tok - > expressionString ( ) , & value , other - > expressionString ( ) , & other - > values ( ) . back ( ) ) ;
2019-03-28 12:49:52 +01:00
continue ;
2019-03-29 15:20:17 +01:00
} else if ( other & & ! other - > hasKnownIntValue ( ) & & value . isKnown ( ) & & value . intvalue = = 0 ) {
2019-08-12 12:58:53 +02:00
outOfBoundsError ( fparent , tok - > expressionString ( ) , & value , other - > expressionString ( ) , nullptr ) ;
2019-03-29 15:20:17 +01:00
continue ;
2019-03-28 12:49:52 +01:00
}
}
2018-08-11 18:57:21 +02:00
if ( ! container - > arrayLike_indexOp & & ! container - > stdStringLike )
continue ;
2019-08-24 15:40:29 +02:00
if ( value . intvalue = = 0 & & Token : : Match ( parent , " [ " ) & & tok = = parent - > astOperand1 ( ) ) {
2019-08-12 12:58:53 +02:00
outOfBoundsError ( parent , tok - > expressionString ( ) , & value , " " , nullptr ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
2019-08-12 12:58:53 +02:00
if ( container - > arrayLike_indexOp & & Token : : Match ( parent , " [ " ) ) {
const ValueFlow : : Value * indexValue = parent - > astOperand2 ( ) ? parent - > astOperand2 ( ) - > getMaxValue ( false ) : nullptr ;
2018-08-11 11:40:48 +02:00
if ( indexValue & & indexValue - > intvalue > = value . intvalue ) {
2019-08-12 12:58:53 +02:00
outOfBoundsError ( parent , tok - > expressionString ( ) , & value , parent - > astOperand2 ( ) - > expressionString ( ) , indexValue ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
if ( mSettings - > isEnabled ( Settings : : WARNING ) ) {
2019-08-12 12:58:53 +02:00
indexValue = parent - > astOperand2 ( ) ? parent - > astOperand2 ( ) - > getMaxValue ( true ) : nullptr ;
2018-08-11 11:40:48 +02:00
if ( indexValue & & indexValue - > intvalue > = value . intvalue ) {
2019-08-12 12:58:53 +02:00
outOfBoundsError ( parent , tok - > expressionString ( ) , & value , parent - > astOperand2 ( ) - > expressionString ( ) , indexValue ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
}
}
}
}
}
}
2020-08-26 21:05:17 +02:00
static std : : string indexValueString ( const ValueFlow : : Value & indexValue )
{
if ( indexValue . isIteratorStartValue ( ) )
return " at position " + MathLib : : toString ( indexValue . intvalue ) + " from the beginning " ;
if ( indexValue . isIteratorEndValue ( ) )
return " at position " + MathLib : : toString ( - indexValue . intvalue ) + " from the end " ;
return MathLib : : toString ( indexValue . intvalue ) ;
}
2019-03-29 11:13:25 +01:00
void CheckStl : : outOfBoundsError ( const Token * tok , const std : : string & containerName , const ValueFlow : : Value * containerSize , const std : : string & index , const ValueFlow : : Value * indexValue )
2018-08-11 11:40:48 +02:00
{
2019-03-29 11:13:25 +01:00
// Do not warn if both the container size and index value are possible
if ( containerSize & & indexValue & & containerSize - > isPossible ( ) & & indexValue - > isPossible ( ) )
2018-08-11 11:40:48 +02:00
return ;
2019-03-29 11:13:25 +01:00
const std : : string expression = tok ? tok - > expressionString ( ) : ( containerName + " [x] " ) ;
2018-08-11 11:40:48 +02:00
std : : string errmsg ;
if ( ! containerSize )
2019-03-29 11:13:25 +01:00
errmsg = " Out of bounds access in expression ' " + expression + " ' " ;
2018-08-11 11:40:48 +02:00
else if ( containerSize - > intvalue = = 0 ) {
if ( containerSize - > condition )
2019-03-29 11:13:25 +01:00
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( containerSize - > condition ) + " or expression ' " + expression + " ' cause access out of bounds. " ;
2019-03-29 15:20:17 +01:00
else if ( indexValue = = nullptr & & ! index . empty ( ) )
errmsg = " Out of bounds access in expression ' " + expression + " ' because '$symbol' is empty and ' " + index + " ' may be non-zero. " ;
2018-08-11 11:40:48 +02:00
else
2019-03-29 11:13:25 +01:00
errmsg = " Out of bounds access in expression ' " + expression + " ' because '$symbol' is empty. " ;
} else if ( indexValue ) {
2018-11-28 07:03:56 +01:00
if ( containerSize - > condition )
2019-03-29 11:13:25 +01:00
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( containerSize - > condition ) + " or $symbol size can be " + MathLib : : toString ( containerSize - > intvalue ) + " . Expression ' " + expression + " ' cause access out of bounds. " ;
else if ( indexValue - > condition )
2020-08-26 21:05:17 +02:00
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( indexValue - > condition ) + " or ' " + index + " ' can have the value " + indexValueString ( * indexValue ) + " . Expression ' " + expression + " ' cause access out of bounds. " ;
2019-03-29 11:13:25 +01:00
else
2020-08-26 21:05:17 +02:00
errmsg = " Out of bounds access in ' " + expression + " ', if '$symbol' size is " + MathLib : : toString ( containerSize - > intvalue ) + " and ' " + index + " ' is " + indexValueString ( * indexValue ) ;
2018-08-11 14:45:12 +02:00
} else {
// should not happen
return ;
2018-08-11 11:40:48 +02:00
}
ErrorPath errorPath ;
2019-03-29 11:13:25 +01:00
if ( ! indexValue )
2018-08-11 11:40:48 +02:00
errorPath = getErrorPath ( tok , containerSize , " Access out of bounds " ) ;
else {
ErrorPath errorPath1 = getErrorPath ( tok , containerSize , " Access out of bounds " ) ;
2019-03-29 11:13:25 +01:00
ErrorPath errorPath2 = getErrorPath ( tok , indexValue , " Access out of bounds " ) ;
2018-08-11 11:40:48 +02:00
if ( errorPath1 . size ( ) < = 1 )
errorPath = errorPath2 ;
else if ( errorPath2 . size ( ) < = 1 )
errorPath = errorPath1 ;
else {
errorPath = errorPath1 ;
errorPath . splice ( errorPath . end ( ) , errorPath2 ) ;
}
}
reportError ( errorPath ,
2019-03-29 11:13:25 +01:00
( containerSize & & ! containerSize - > errorSeverity ( ) ) | | ( indexValue & & ! indexValue - > errorSeverity ( ) ) ? Severity : : warning : Severity : : error ,
2018-08-11 11:40:48 +02:00
" containerOutOfBounds " ,
2019-03-29 11:13:25 +01:00
" $symbol: " + containerName + " \n " + errmsg ,
2018-08-11 11:40:48 +02:00
CWE398 ,
2019-03-29 11:13:25 +01:00
( containerSize & & containerSize - > isInconclusive ( ) ) | | ( indexValue & & indexValue - > isInconclusive ( ) ) ) ;
2018-08-11 11:40:48 +02:00
}
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 ) ;
}
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
} ;
2019-07-18 10:56:44 +02:00
static bool isVector ( const Token * tok )
{
if ( ! tok )
return false ;
const Variable * var = tok - > variable ( ) ;
const Token * decltok = var ? var - > typeStartToken ( ) : nullptr ;
return Token : : simpleMatch ( decltok , " std :: vector " ) ;
}
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
2019-07-15 14:05:23 +02:00
std : : map < int , const Token * > iteratorScopeBeginInfo ;
2018-10-17 06:36:51 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
bool inconclusiveType = false ;
if ( ! isIterator ( var , inconclusiveType ) )
continue ;
2019-07-15 14:05:23 +02:00
const int iteratorId = var - > declarationId ( ) ;
2018-10-17 06:36:51 +02:00
if ( iteratorId ! = 0 )
iteratorScopeBeginInfo [ iteratorId ] = var - > nameToken ( ) ;
}
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
2019-07-15 14:05:23 +02:00
const int iteratorId = var - > declarationId ( ) ;
2018-04-28 09:38:33 +02:00
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
2010-12-30 22:36:25 +01:00
// Is the iterator used in a insert/erase operation?
2019-12-25 09:32:50 +01:00
if ( Token : : Match ( tok2 , " %name% . insert|erase ( *| %varid% )|, " , iteratorId ) & & ! isVector ( tok2 ) ) {
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 ;
}
2019-12-20 12:13:15 +01:00
// Not different containers if a reference is used..
if ( containerToken & & containerToken - > variable ( ) & & containerToken - > variable ( ) - > isReference ( ) ) {
const Token * nameToken = containerToken - > variable ( ) - > nameToken ( ) ;
if ( Token : : Match ( nameToken , " %name% = " ) ) {
const Token * name1 = nameToken - > tokAt ( 2 ) ;
const Token * name2 = tok2 ;
while ( Token : : Match ( name1 , " %name%|.|:: " ) & & name2 & & name1 - > str ( ) = = name2 - > str ( ) ) {
name1 = name1 - > next ( ) ;
name2 = name2 - > next ( ) ;
}
if ( ! Token : : simpleMatch ( name1 , " ; " ) | | ! Token : : Match ( name2 , " [;,()=] " ) )
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
2020-06-06 17:54:56 +02:00
void CheckStl : : mismatchingContainerIteratorError ( const Token * tok , const Token * iterTok )
{
const std : : string container ( tok ? tok - > expressionString ( ) : std : : string ( " v1 " ) ) ;
const std : : string iter ( iterTok ? iterTok - > expressionString ( ) : std : : string ( " it " ) ) ;
reportError ( tok ,
Severity : : error ,
" mismatchingContainerIterator " ,
" Iterator ' " + iter + " ' from different container ' " + container + " ' are used together. " ,
CWE664 ,
false ) ;
}
2009-10-18 18:42:01 +02:00
// Error message for bad iterator usage..
2019-12-25 09:32:50 +01:00
void CheckStl : : mismatchingContainersError ( const Token * tok1 , const Token * tok2 )
2009-10-18 18:42:01 +02:00
{
2019-12-25 09:32:50 +01:00
const std : : string expr1 ( tok1 ? tok1 - > expressionString ( ) : std : : string ( " v1 " ) ) ;
const std : : string expr2 ( tok2 ? tok2 - > expressionString ( ) : std : : string ( " v2 " ) ) ;
reportError ( tok1 ,
Severity : : error ,
" mismatchingContainers " ,
" Iterators of different containers ' " + expr1 + " ' and ' " + expr2 + " ' 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
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 ;
}
2019-12-25 09:32:50 +01:00
bool CheckStl : : checkIteratorPair ( const Token * tok1 , const Token * tok2 )
{
if ( ! tok1 )
return false ;
if ( ! tok2 )
return false ;
ValueFlow : : Value val1 = getLifetimeObjValue ( tok1 ) ;
ValueFlow : : Value val2 = getLifetimeObjValue ( tok2 ) ;
if ( val1 . tokvalue & & val2 . tokvalue & & val1 . lifetimeKind = = val2 . lifetimeKind ) {
if ( val1 . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : Lambda )
return false ;
2020-01-01 12:02:21 +01:00
if ( tok1 - > astParent ( ) = = tok2 - > astParent ( ) & & Token : : Match ( tok1 - > astParent ( ) , " %comp%|- " ) ) {
if ( val1 . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : Address )
return false ;
if ( val1 . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : Object & &
( ! astIsContainer ( val1 . tokvalue ) | | ! astIsContainer ( val2 . tokvalue ) ) )
return false ;
}
2019-12-25 09:32:50 +01:00
if ( isSameExpression ( true , false , val1 . tokvalue , val2 . tokvalue , mSettings - > library , false , false ) )
return false ;
if ( val1 . tokvalue - > expressionString ( ) = = val2 . tokvalue - > expressionString ( ) )
iteratorsError ( tok1 , val1 . tokvalue , val1 . tokvalue - > expressionString ( ) ) ;
else
mismatchingContainersError ( val1 . tokvalue , val2 . tokvalue ) ;
return true ;
}
const Token * iter1 = getIteratorExpression ( tok1 ) ;
const Token * iter2 = getIteratorExpression ( tok2 ) ;
if ( iter1 & & iter2 & & ! isSameExpression ( true , false , iter1 , iter2 , mSettings - > library , false , false ) ) {
mismatchingContainerExpressionError ( iter1 , iter2 ) ;
return true ;
}
return false ;
}
struct ArgIteratorInfo {
const Token * tok ;
const Library : : ArgumentChecks : : IteratorInfo * info ;
} ;
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%|- " ) ) {
2019-12-25 09:32:50 +01:00
if ( checkIteratorPair ( tok - > astOperand1 ( ) , tok - > astOperand2 ( ) ) )
2018-08-21 06:34:30 +02:00
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:23:37 +02:00
const std : : vector < const Token * > args = getArguments ( ftok ) ;
if ( args . size ( ) < 2 )
continue ;
2019-12-25 09:32:50 +01:00
// Group args together by container
std : : map < int , std : : vector < ArgIteratorInfo > > containers ;
2019-07-15 14:05:23 +02:00
for ( int argnr = 1 ; argnr < = args . size ( ) ; + + argnr ) {
2018-07-26 22:23:37 +02:00
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 ] ;
2019-12-25 09:32:50 +01:00
containers [ i - > container ] . push_back ( { argTok , i } ) ;
}
// Lambda is used to escape the nested loops
[ & ] {
2019-12-26 15:48:17 +01:00
for ( const auto & p : containers )
{
2019-12-25 09:32:50 +01:00
const std : : vector < ArgIteratorInfo > & cargs = p . second ;
for ( ArgIteratorInfo iter1 : cargs ) {
for ( ArgIteratorInfo iter2 : cargs ) {
if ( iter1 . tok = = iter2 . tok )
continue ;
if ( iter1 . info - > first & & iter2 . info - > last & &
isSameExpression ( true , false , iter1 . tok , iter2 . tok , mSettings - > library , false , false ) )
sameIteratorExpressionError ( iter1 . tok ) ;
if ( checkIteratorPair ( iter1 . tok , iter2 . tok ) )
return ;
2016-10-27 10:25:45 +02:00
}
2012-11-16 06:50:49 +01:00
}
2011-10-18 21:55:41 +02:00
}
2019-12-25 09:32:50 +01:00
} ( ) ;
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 ) ) {
2019-12-25 09:32:50 +01:00
mismatchingContainersError ( var - > nameToken ( ) , var - > nameToken ( ) - > tokAt ( 2 ) ) ;
2015-07-21 14:13:26 +02:00
}
}
}
2009-10-18 18:42:01 +02:00
}
2020-06-06 17:54:56 +02:00
void CheckStl : : mismatchingContainerIterator ( )
{
// Check if different containers are used in various calls of standard functions
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! astIsContainer ( tok ) )
continue ;
if ( ! Token : : Match ( tok , " %var% . %name% ( !!) " ) )
continue ;
const Token * const ftok = tok - > tokAt ( 2 ) ;
const std : : vector < const Token * > args = getArguments ( ftok ) ;
const Library : : Container * c = tok - > valueType ( ) - > container ;
Library : : Container : : Action action = c - > getAction ( tok - > strAt ( 2 ) ) ;
const Token * iterTok = nullptr ;
if ( action = = Library : : Container : : Action : : INSERT & & args . size ( ) = = 2 ) {
// Skip if iterator pair
if ( astIsIterator ( args . back ( ) ) )
continue ;
if ( ! astIsIterator ( args . front ( ) ) )
continue ;
iterTok = args . front ( ) ;
} else if ( action = = Library : : Container : : Action : : ERASE ) {
if ( ! astIsIterator ( args . front ( ) ) )
continue ;
iterTok = args . front ( ) ;
} else {
continue ;
}
ValueFlow : : Value val = getLifetimeObjValue ( iterTok ) ;
if ( ! val . tokvalue )
continue ;
if ( val . lifetimeKind ! = ValueFlow : : Value : : LifetimeKind : : Iterator )
continue ;
if ( isSameExpression ( true , false , tok , val . tokvalue , mSettings - > library , false , false ) )
continue ;
mismatchingContainerIteratorError ( tok , iterTok ) ;
}
}
}
2019-07-18 10:56:44 +02:00
static bool isInvalidMethod ( const Token * tok )
{
2020-04-03 13:16:57 +02:00
if ( Token : : Match ( tok - > next ( ) , " . assign|clear|swap " ) )
2019-07-18 10:56:44 +02:00
return true ;
if ( Token : : Match ( tok - > next ( ) , " %assign% " ) )
return true ;
2020-04-03 13:16:57 +02:00
if ( const Library : : Container * c = tok - > valueType ( ) - > container ) {
Library : : Container : : Action action = c - > getAction ( tok - > strAt ( 2 ) ) ;
if ( c - > unstableErase ) {
if ( action = = Library : : Container : : Action : : ERASE )
return true ;
}
if ( c - > unstableInsert ) {
if ( action = = Library : : Container : : Action : : RESIZE )
return true ;
if ( action = = Library : : Container : : Action : : CLEAR )
return true ;
if ( action = = Library : : Container : : Action : : PUSH )
return true ;
if ( action = = Library : : Container : : Action : : POP )
return true ;
if ( action = = Library : : Container : : Action : : INSERT )
return true ;
if ( action = = Library : : Container : : Action : : CHANGE )
return true ;
if ( action = = Library : : Container : : Action : : CHANGE_INTERNAL )
return true ;
if ( Token : : Match ( tok - > next ( ) , " . insert|emplace " ) )
return true ;
}
}
2019-07-18 10:56:44 +02:00
return false ;
}
2019-09-02 06:58:09 +02:00
static bool isVariableDecl ( const Token * tok )
{
if ( ! tok )
return false ;
const Variable * var = tok - > variable ( ) ;
if ( ! var )
return false ;
if ( var - > nameToken ( ) = = tok )
return true ;
if ( Token : : Match ( var - > declEndToken ( ) , " ; %var% " ) & & var - > declEndToken ( ) - > next ( ) = = tok )
return true ;
return false ;
}
2019-07-18 10:56:44 +02:00
void CheckStl : : invalidContainer ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
const Library & library = mSettings - > library ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " %var% " ) )
continue ;
if ( tok - > varId ( ) = = 0 )
continue ;
if ( ! astIsContainer ( tok ) )
continue ;
if ( ! isInvalidMethod ( tok ) )
continue ;
2020-02-11 11:45:10 +01:00
std : : set < nonneg int > skipVarIds ;
2019-07-18 10:56:44 +02:00
// Skip if the variable is assigned to
2020-02-16 15:56:52 +01:00
const Token * assignExpr = tok ;
while ( assignExpr - > astParent ( ) ) {
bool isRHS = astIsRHS ( assignExpr ) ;
assignExpr = assignExpr - > astParent ( ) ;
if ( Token : : Match ( assignExpr , " %assign% " ) ) {
if ( ! isRHS )
assignExpr = nullptr ;
break ;
}
}
if ( Token : : Match ( assignExpr , " %assign% " ) & & Token : : Match ( assignExpr - > astOperand1 ( ) , " %var% " ) )
skipVarIds . insert ( assignExpr - > astOperand1 ( ) - > varId ( ) ) ;
2019-07-18 10:56:44 +02:00
const Token * endToken = nextAfterAstRightmostLeaf ( tok - > next ( ) - > astParent ( ) ) ;
if ( ! endToken )
endToken = tok - > next ( ) ;
const ValueFlow : : Value * v = nullptr ;
ErrorPath errorPath ;
PathAnalysis : : Info info = PathAnalysis { endToken , library } . forwardFind ( [ & ] ( const PathAnalysis : : Info & info ) {
if ( ! info . tok - > variable ( ) )
return false ;
2020-02-11 11:45:10 +01:00
if ( info . tok - > varId ( ) = = 0 )
2019-07-18 10:56:44 +02:00
return false ;
2020-02-11 11:45:10 +01:00
if ( skipVarIds . count ( info . tok - > varId ( ) ) > 0 )
return false ;
2020-08-07 04:08:30 +02:00
// if (Token::simpleMatch(info.tok->next(), "."))
2020-08-23 17:17:33 +02:00
// return false;
2020-02-11 11:45:10 +01:00
if ( Token : : Match ( info . tok - > astParent ( ) , " %assign% " ) & & astIsLHS ( info . tok ) )
skipVarIds . insert ( info . tok - > varId ( ) ) ;
2019-09-02 06:59:07 +02:00
if ( info . tok - > variable ( ) - > isReference ( ) & &
! isVariableDecl ( info . tok ) & &
2019-09-02 06:58:09 +02:00
reaches ( info . tok - > variable ( ) - > nameToken ( ) , tok , library , nullptr ) ) {
2019-09-02 06:59:07 +02:00
2019-09-02 06:58:09 +02:00
ErrorPath ep ;
bool addressOf = false ;
const Variable * var = getLifetimeVariable ( info . tok , ep , & addressOf ) ;
// Check the reference is created before the change
2019-09-03 06:41:35 +02:00
if ( var & & var - > declarationId ( ) = = tok - > varId ( ) & & ! addressOf ) {
2019-09-02 06:58:09 +02:00
// An argument always reaches
if ( var - > isArgument ( ) | | ( ! var - > isReference ( ) & & ! var - > isRValueReference ( ) & &
! isVariableDecl ( tok ) & & reaches ( var - > nameToken ( ) , tok , library , & ep ) ) ) {
errorPath = ep ;
return true ;
}
}
}
2019-07-18 10:56:44 +02:00
for ( const ValueFlow : : Value & val : info . tok - > values ( ) ) {
if ( ! val . isLocalLifetimeValue ( ) )
continue ;
2019-07-24 00:04:49 +02:00
if ( val . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : Address )
continue ;
2020-08-07 04:08:30 +02:00
if ( val . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : SubObject )
continue ;
2019-07-18 10:56:44 +02:00
if ( ! val . tokvalue - > variable ( ) )
continue ;
if ( val . tokvalue - > varId ( ) ! = tok - > varId ( ) )
continue ;
ErrorPath ep ;
// Check the iterator is created before the change
2020-05-23 07:13:55 +02:00
if ( val . tokvalue ! = tok & & reaches ( val . tokvalue , tok , library , & ep ) ) {
2019-07-18 10:56:44 +02:00
v = & val ;
errorPath = ep ;
return true ;
}
}
return false ;
} ) ;
2019-09-02 06:58:09 +02:00
if ( ! info . tok )
2019-07-18 10:56:44 +02:00
continue ;
errorPath . insert ( errorPath . end ( ) , info . errorPath . begin ( ) , info . errorPath . end ( ) ) ;
2019-09-02 06:58:09 +02:00
if ( v ) {
invalidContainerError ( info . tok , tok , v , errorPath ) ;
} else {
invalidContainerReferenceError ( info . tok , tok , errorPath ) ;
}
2019-07-18 10:56:44 +02:00
}
}
}
2020-04-08 14:56:12 +02:00
static const Token * getLoopContainer ( const Token * tok )
2020-04-04 11:47:02 +02:00
{
if ( ! Token : : simpleMatch ( tok , " for ( " ) )
return nullptr ;
const Token * sepTok = tok - > next ( ) - > astOperand2 ( ) ;
if ( ! Token : : simpleMatch ( sepTok , " : " ) )
return nullptr ;
return sepTok - > astOperand2 ( ) ;
}
void CheckStl : : invalidContainerLoop ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
const Token * contTok = getLoopContainer ( tok ) ;
if ( ! contTok )
continue ;
const Token * blockStart = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * blockEnd = blockStart - > link ( ) ;
if ( ! Token : : Match ( contTok , " %var% " ) )
continue ;
if ( contTok - > varId ( ) = = 0 )
continue ;
if ( ! astIsContainer ( contTok ) )
continue ;
nonneg int varid = contTok - > varId ( ) ;
for ( const Token * tok2 = blockStart ; tok2 ! = blockEnd ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) ! = varid )
continue ;
if ( ! Token : : Match ( tok2 - > next ( ) , " . %name% ( " ) )
continue ;
if ( ! isInvalidMethod ( tok2 ) )
continue ;
2020-05-23 07:14:45 +02:00
const Scope * s = tok2 - > scope ( ) ;
if ( ! s )
continue ;
if ( isReturnScope ( s - > bodyEnd , & mSettings - > library ) )
continue ;
2020-04-04 11:47:02 +02:00
invalidContainerLoopError ( tok2 , tok ) ;
break ;
}
}
}
}
void CheckStl : : invalidContainerLoopError ( const Token * tok , const Token * loopTok )
{
ErrorPath errorPath ;
const std : : string method = tok ? tok - > strAt ( 2 ) : " erase " ;
errorPath . emplace_back ( loopTok , " Iterating container here. " ) ;
const std : : string msg = " Calling ' " + method + " ' while iterating the container is invalid. " ;
errorPath . emplace_back ( tok , " " ) ;
reportError ( errorPath , Severity : : error , " invalidContainerLoop " , msg , CWE664 , false ) ;
}
2019-07-18 10:56:44 +02:00
void CheckStl : : invalidContainerError ( const Token * tok , const Token * contTok , const ValueFlow : : Value * val , ErrorPath errorPath )
{
2019-09-11 19:25:09 +02:00
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
2019-07-18 10:56:44 +02:00
std : : string method = contTok ? contTok - > strAt ( 2 ) : " erase " ;
errorPath . emplace_back ( contTok , " After calling ' " + method + " ', iterators or references to the container's data may be invalid . " ) ;
if ( val )
errorPath . insert ( errorPath . begin ( ) , val - > errorPath . begin ( ) , val - > errorPath . end ( ) ) ;
std : : string msg = " Using " + lifetimeMessage ( tok , val , errorPath ) ;
errorPath . emplace_back ( tok , " " ) ;
2019-09-11 19:25:09 +02:00
reportError ( errorPath , Severity : : error , " invalidContainer " , msg + " that may be invalid. " , CWE664 , inconclusive ) ;
2019-07-18 10:56:44 +02:00
}
2009-10-18 18:42:01 +02:00
2019-09-02 06:58:09 +02:00
void CheckStl : : invalidContainerReferenceError ( const Token * tok , const Token * contTok , ErrorPath errorPath )
{
std : : string method = contTok ? contTok - > strAt ( 2 ) : " erase " ;
std : : string name = contTok ? contTok - > expressionString ( ) : " x " ;
errorPath . emplace_back (
contTok , " After calling ' " + method + " ', iterators or references to the container's data may be invalid . " ) ;
std : : string msg = " Reference to " + name ;
errorPath . emplace_back ( tok , " " ) ;
reportError ( errorPath , Severity : : error , " invalidContainerReference " , msg + " that may be invalid. " , CWE664 , false ) ;
}
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 ;
2020-01-05 16:24:25 +01:00
// check in the ast that cond is of the form "%var% <= %var% . %name% ( )"
if ( cond - > str ( ) = = " <= " & & Token : : Match ( cond - > astOperand1 ( ) , " %var% " ) & &
cond - > astOperand2 ( ) - > str ( ) = = " ( " & & cond - > astOperand2 ( ) - > astOperand1 ( ) - > str ( ) = = " . " & &
Token : : Match ( cond - > astOperand2 ( ) - > astOperand1 ( ) - > astOperand1 ( ) , " %var% " ) & &
Token : : Match ( cond - > astOperand2 ( ) - > astOperand1 ( ) - > astOperand2 ( ) , " %name% " ) ) {
2018-11-28 20:30:58 +01:00
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 ;
2019-07-17 11:39:30 +02:00
if ( container - > getYield ( containerToken - > strAt ( 2 ) ) ! = Library : : Container : : Yield : : SIZE )
2018-11-28 20:30:58 +01:00
continue ;
2013-03-04 19:59:46 +01:00
2018-11-28 20:30:58 +01:00
// variable id for loop variable.
2019-07-15 14:05:23 +02:00
const int numId = vartok - > varId ( ) ;
2018-11-28 20:30:58 +01:00
// variable id for the container variable
2019-07-15 14:05:23 +02:00
const int declarationId = containerToken - > varId ( ) ;
2018-11-28 20:30:58 +01:00
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% ( ) " ) ) {
2019-07-17 11:39:30 +02:00
if ( container - > getYield ( tok3 - > strAt ( 1 ) ) = = Library : : Container : : Yield : : SIZE )
2018-11-28 20:30:58 +01:00
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 ) ) ;
2019-07-17 11:39:30 +02:00
if ( yield = = Library : : Container : : Yield : : AT_INDEX )
2018-11-28 20:30:58 +01:00
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 ;
2019-07-18 10:56:44 +02:00
// Vector erases are handled by invalidContainer check
if ( isVector ( tok - > tokAt ( - 3 ) ) )
continue ;
2015-07-30 10:13:49 +02:00
if ( Token : : simpleMatch ( tok - > astParent ( ) , " = " ) )
2015-07-23 18:53:31 +02:00
continue ;
// Iterator is invalid..
2019-07-15 14:05:23 +02:00
int indentlevel = 0U ;
2015-07-23 18:53:31 +02:00
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
}
}
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 ;
2020-02-18 19:02:54 +01:00
const Token * conditionStart = scope . classDef - > next ( ) ;
if ( conditionStart & & Token : : simpleMatch ( conditionStart - > astOperand2 ( ) , " ; " ) )
conditionStart = conditionStart - > astOperand2 ( ) ;
for ( const Token * tok = conditionStart ; 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
}
}
2019-07-17 11:39:30 +02:00
if ( container & & container - > getAction ( funcTok - > str ( ) ) = = Library : : Container : : Action : : FIND ) {
2015-01-04 12:43:31 +01:00
if ( if_findCompare ( funcTok - > next ( ) ) )
continue ;
2019-07-17 11:39:30 +02:00
if ( printWarning & & container - > getYield ( funcTok - > str ( ) ) = = Library : : Container : : Yield : : 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 )
{
2019-05-05 10:35:22 +02:00
if ( str & & mSettings - > standards . cpp > = Standards : : CPP20 )
2012-04-04 19:40:28 +02:00
reportError ( tok , Severity : : performance , " stlIfStrFind " ,
2019-07-17 16:06:10 +02:00
" Inefficient usage of string::find() in condition; string::starts_with() could be faster. \n "
2019-05-05 10:35:22 +02:00
" Either inefficient or wrong usage of string::find(). string::starts_with() 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 ) ;
2019-05-05 10:35:22 +02:00
if ( ! str )
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
}
2019-05-02 11:04:23 +02:00
static std : : pair < const Token * , const Token * > isMapFind ( const Token * tok )
{
if ( ! Token : : simpleMatch ( tok , " ( " ) )
return { } ;
if ( ! Token : : simpleMatch ( tok - > astOperand1 ( ) , " . " ) )
return { } ;
if ( ! astIsContainer ( tok - > astOperand1 ( ) - > astOperand1 ( ) ) )
return { } ;
const Token * contTok = tok - > astOperand1 ( ) - > astOperand1 ( ) ;
const Library : : Container * container = contTok - > valueType ( ) - > container ;
if ( ! container )
return { } ;
if ( ! container - > stdAssociativeLike )
return { } ;
if ( ! Token : : Match ( tok - > astOperand1 ( ) , " . find|count ( " ) )
return { } ;
if ( ! tok - > astOperand2 ( ) )
return { } ;
return { contTok , tok - > astOperand2 ( ) } ;
}
static const Token * skipLocalVars ( const Token * tok )
{
if ( ! tok )
return tok ;
if ( Token : : simpleMatch ( tok , " { " ) )
return skipLocalVars ( tok - > next ( ) ) ;
const Scope * scope = tok - > scope ( ) ;
const Token * top = tok - > astTop ( ) ;
if ( ! top ) {
const Token * semi = Token : : findsimplematch ( tok , " ; " ) ;
if ( ! semi )
return tok ;
if ( ! Token : : Match ( semi - > previous ( ) , " %var% ; " ) )
return tok ;
const Token * varTok = semi - > previous ( ) ;
const Variable * var = varTok - > variable ( ) ;
if ( ! var )
return tok ;
if ( var - > nameToken ( ) ! = varTok )
return tok ;
return skipLocalVars ( semi - > next ( ) ) ;
}
if ( Token : : Match ( top , " %assign% " ) ) {
const Token * varTok = top - > astOperand1 ( ) ;
if ( ! Token : : Match ( varTok , " %var% " ) )
return tok ;
const Variable * var = varTok - > variable ( ) ;
if ( ! var )
return tok ;
if ( var - > scope ( ) ! = scope )
return tok ;
const Token * endTok = nextAfterAstRightmostLeaf ( top ) ;
if ( ! endTok )
return tok ;
return skipLocalVars ( endTok - > next ( ) ) ;
}
return tok ;
}
static const Token * findInsertValue ( const Token * tok , const Token * containerTok , const Token * keyTok , const Library & library )
{
const Token * startTok = skipLocalVars ( tok ) ;
const Token * top = startTok - > astTop ( ) ;
const Token * icontainerTok = nullptr ;
const Token * ikeyTok = nullptr ;
const Token * ivalueTok = nullptr ;
if ( Token : : simpleMatch ( top , " = " ) & & Token : : simpleMatch ( top - > astOperand1 ( ) , " [ " ) ) {
icontainerTok = top - > astOperand1 ( ) - > astOperand1 ( ) ;
ikeyTok = top - > astOperand1 ( ) - > astOperand2 ( ) ;
ivalueTok = top - > astOperand2 ( ) ;
}
if ( Token : : simpleMatch ( top , " ( " ) & & Token : : Match ( top - > astOperand1 ( ) , " . insert|emplace ( " ) & & ! astIsIterator ( top - > astOperand1 ( ) - > tokAt ( 2 ) ) ) {
icontainerTok = top - > astOperand1 ( ) - > astOperand1 ( ) ;
const Token * itok = top - > astOperand1 ( ) - > tokAt ( 2 ) - > astOperand2 ( ) ;
if ( Token : : simpleMatch ( itok , " , " ) ) {
ikeyTok = itok - > astOperand1 ( ) ;
ivalueTok = itok - > astOperand2 ( ) ;
} else {
ikeyTok = itok ;
}
}
if ( ! ikeyTok | | ! icontainerTok )
return nullptr ;
if ( isSameExpression ( true , true , containerTok , icontainerTok , library , true , false ) & &
isSameExpression ( true , true , keyTok , ikeyTok , library , true , true ) ) {
if ( ivalueTok )
return ivalueTok ;
else
return ikeyTok ;
}
return nullptr ;
}
void CheckStl : : checkFindInsert ( )
{
if ( ! mSettings - > isEnabled ( Settings : : PERFORMANCE ) )
return ;
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : simpleMatch ( tok , " if ( " ) )
continue ;
if ( ! Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) { " ) )
continue ;
if ( ! Token : : Match ( tok - > next ( ) - > astOperand2 ( ) , " %comp% " ) )
continue ;
const Token * condTok = tok - > next ( ) - > astOperand2 ( ) ;
const Token * containerTok ;
const Token * keyTok ;
std : : tie ( containerTok , keyTok ) = isMapFind ( condTok - > astOperand1 ( ) ) ;
if ( ! containerTok )
continue ;
const Token * thenTok = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * valueTok = findInsertValue ( thenTok , containerTok , keyTok , mSettings - > library ) ;
if ( ! valueTok )
continue ;
if ( Token : : simpleMatch ( thenTok - > link ( ) , " } else { " ) ) {
const Token * valueTok2 =
findInsertValue ( thenTok - > link ( ) - > tokAt ( 2 ) , containerTok , keyTok , mSettings - > library ) ;
if ( ! valueTok2 )
continue ;
if ( isSameExpression ( true , true , valueTok , valueTok2 , mSettings - > library , true , true ) ) {
checkFindInsertError ( valueTok ) ;
}
} else {
checkFindInsertError ( valueTok ) ;
}
}
}
}
void CheckStl : : checkFindInsertError ( const Token * tok )
{
reportError (
tok , Severity : : performance , " stlFindInsert " , " Searching before insertion is not necessary. " , 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 ;
2019-05-05 10:35:22 +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 ;
2019-07-15 14:05:23 +02:00
const int iteratorId ( tok2 - > varId ( ) ) ;
2012-04-04 19:44:57 +02:00
// 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
2019-07-15 14:05:23 +02:00
std : : multimap < std : : string , 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 ;
}
2019-07-15 14:05:23 +02:00
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
2019-07-15 14:05:23 +02:00
const std : : pair < std : : multimap < std : : string , int > : : const_iterator , std : : multimap < std : : string , int > : : const_iterator > range = c_strFuncParam . equal_range ( tok - > str ( ) ) ;
for ( std : : multimap < std : : string , int > : : const_iterator i = range . first ; i ! = range . second ; + + i ) {
2012-02-25 12:43:27 +01:00
if ( i - > second = = 0 )
continue ;
const Token * tok2 = tok - > tokAt ( 2 ) ;
2019-07-15 14:05:23 +02:00
int j ;
2012-02-25 12:43:27 +01:00
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
}
2019-07-15 14:05:23 +02:00
void CheckStl : : string_c_strParam ( const Token * tok , nonneg int number )
2012-02-25 12:43:27 +01:00
{
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 ;
2019-07-15 14:05:23 +02:00
const int iteratorVarId = validityCheckTok - > next ( ) - > varId ( ) ;
2017-07-26 20:32:14 +02:00
// 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
}
}
2020-08-26 21:05:17 +02:00
2020-08-17 23:36:45 +02:00
void CheckStl : : checkDereferenceInvalidIterator2 ( )
{
const bool printInconclusive = ( mSettings - > inconclusive ) ;
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " sizeof|decltype|typeid|typeof ( " ) ) {
tok = tok - > next ( ) - > link ( ) ;
continue ;
}
2020-08-26 21:05:17 +02:00
std : : vector < ValueFlow : : Value > contValues ;
std : : copy_if ( tok - > values ( ) . begin ( ) , tok - > values ( ) . end ( ) , std : : back_inserter ( contValues ) , [ & ] ( const ValueFlow : : Value & value ) {
if ( value . isImpossible ( ) )
return false ;
if ( ! printInconclusive & & value . isInconclusive ( ) )
return false ;
return value . isContainerSizeValue ( ) ;
} ) ;
2020-08-17 23:36:45 +02:00
// Can iterator point to END or before START?
2020-08-23 17:17:33 +02:00
for ( const ValueFlow : : Value & value : tok - > values ( ) ) {
2020-08-26 06:58:53 +02:00
if ( value . isImpossible ( ) )
continue ;
2020-08-17 23:36:45 +02:00
if ( ! printInconclusive & & value . isInconclusive ( ) )
continue ;
if ( ! value . isIteratorValue ( ) )
continue ;
2020-08-26 21:05:17 +02:00
const bool isInvalidIterator = ( value . isIteratorEndValue ( ) & & value . intvalue > = 0 ) | | ( value . isIteratorStartValue ( ) & & value . intvalue < 0 ) ;
const ValueFlow : : Value * cValue = nullptr ;
if ( ! isInvalidIterator ) {
auto it = std : : find_if ( contValues . begin ( ) , contValues . end ( ) , [ & ] ( const ValueFlow : : Value & c ) {
if ( value . isIteratorStartValue ( ) & & value . intvalue > = c . intvalue )
return true ;
if ( value . isIteratorEndValue ( ) & & - value . intvalue > c . intvalue )
return true ;
return false ;
} ) ;
if ( it = = contValues . end ( ) )
continue ;
cValue = & * it ;
}
bool inconclusive = false ;
2020-08-17 23:36:45 +02:00
bool unknown = false ;
if ( ! CheckNullPointer : : isPointerDeRef ( tok , unknown , mSettings ) ) {
2020-08-26 21:05:17 +02:00
if ( ! unknown )
continue ;
inconclusive = true ;
}
if ( cValue ) {
const ValueFlow : : Value & lValue = getLifetimeObjValue ( tok , true ) ;
outOfBoundsError ( tok , lValue . tokvalue - > expressionString ( ) , cValue , tok - > expressionString ( ) , & value ) ;
} else {
dereferenceInvalidIteratorError ( tok , & value , inconclusive ) ;
2020-08-17 23:36:45 +02:00
}
}
}
}
void CheckStl : : dereferenceInvalidIteratorError ( const Token * tok , const ValueFlow : : Value * value , bool inconclusive )
{
const std : : string & varname = tok ? tok - > expressionString ( ) : " var " ;
const std : : string errmsgcond ( " $symbol: " + varname + ' \n ' + ValueFlow : : eitherTheConditionIsRedundant ( value ? value - > condition : nullptr ) + " or there is possible dereference of an invalid iterator: $symbol. " ) ;
if ( ! tok | | ! value ) {
reportError ( tok , Severity : : error , " derefInvalidIterator " , " Dereference of an invalid iterator " , CWE825 , false ) ;
reportError ( tok , Severity : : warning , " derefInvalidIteratorRedundantCheck " , errmsgcond , CWE825 , false ) ;
return ;
}
if ( ! mSettings - > isEnabled ( value , inconclusive ) )
return ;
const ErrorPath errorPath = getErrorPath ( tok , value , " Dereference of an invalid iterator " ) ;
if ( value - > condition ) {
reportError ( errorPath , Severity : : warning , " derefInvalidIteratorRedundantCheck " , errmsgcond , CWE825 , inconclusive | | value - > isInconclusive ( ) ) ;
} else {
std : : string errmsg ;
errmsg = std : : string ( value - > isKnown ( ) ? " Dereference " : " Possible dereference " ) + " of an invalid iterator " ;
if ( ! varname . empty ( ) )
errmsg = " $symbol: " + varname + ' \n ' + errmsg + " : $symbol " ;
reportError ( errorPath ,
value - > isKnown ( ) ? Severity : : error : Severity : : warning ,
" derefInvalidIterator " ,
errmsg ,
CWE825 , inconclusive | | value - > isInconclusive ( ) ) ;
}
}
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 ;
}
2019-07-15 14:05:23 +02:00
static const Token * singleAssignInScope ( const Token * start , nonneg int varid , bool & input )
2018-09-19 18:58:59 +02:00
{
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 ;
2019-03-15 06:15:56 +01:00
input = Token : : findmatch ( assignTok - > next ( ) , " %varid% " , endStatement , varid ) | | ! Token : : Match ( start - > next ( ) , " %var% = " ) ;
2018-09-19 18:58:59 +02:00
return assignTok ;
}
2019-07-15 14:05:23 +02:00
static const Token * singleMemberCallInScope ( const Token * start , nonneg int varid , bool & input )
2018-09-19 18:58:59 +02:00
{
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 ;
}
2019-07-15 14:05:23 +02:00
static const Token * singleIncrementInScope ( const Token * start , nonneg int varid , bool & input )
2018-09-19 18:58:59 +02:00
{
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 ;
}
2019-07-15 14:05:23 +02:00
static const Token * singleConditionalInScope ( const Token * start , nonneg int varid )
2018-09-19 18:58:59 +02:00
{
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 ;
}
2019-07-15 14:05:23 +02:00
static bool addByOne ( const Token * tok , nonneg int varid )
2018-09-19 18:58:59 +02:00
{
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 ;
}
2019-07-15 14:05:23 +02:00
static bool accumulateBoolLiteral ( const Token * tok , nonneg int varid )
2018-09-19 18:58:59 +02:00
{
2019-03-15 06:15:56 +01:00
if ( Token : : Match ( tok , " %assign% %bool% ; " ) & &
2018-09-19 18:58:59 +02:00
tok - > tokAt ( 1 ) - > hasKnownIntValue ( ) ) {
return true ;
}
if ( Token : : Match ( tok , " = %varid% %oror%|%or%|&&|& %bool% ; " , varid ) & &
tok - > tokAt ( 3 ) - > hasKnownIntValue ( ) ) {
return true ;
}
return false ;
}
2019-07-15 14:05:23 +02:00
static bool accumulateBool ( const Token * tok , nonneg int varid )
2018-09-19 18:58:59 +02:00
{
2019-03-15 06:15:56 +01:00
// Missing %oreq% so we have to check both manually
if ( Token : : simpleMatch ( tok , " &= " ) | | Token : : simpleMatch ( tok , " |= " ) ) {
2018-09-19 18:58:59 +02:00
return true ;
}
if ( Token : : Match ( tok , " = %varid% %oror%|%or%|&&|& " , varid ) ) {
return true ;
}
return false ;
}
2019-07-15 14:05:23 +02:00
static bool hasVarIds ( const Token * tok , nonneg int var1 , nonneg int var2 )
2018-09-19 18:58:59 +02:00
{
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 ;
}
2019-07-15 14:05:23 +02:00
static std : : string minmaxCompare ( const Token * condTok , nonneg int loopVar , nonneg int assignVar , bool invert = false )
2018-09-19 18:58:59 +02:00
{
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 ) {
2019-07-15 14:05:23 +02:00
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 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 ( ) ;
2019-07-15 14:05:23 +02:00
const 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 ) {
2019-07-15 14:05:23 +02:00
const 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 ( ) ;
2019-07-15 14:05:23 +02:00
const 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 ;
}
}
}
}
}
2020-06-16 02:40:54 +02:00
2020-09-02 07:11:23 +02:00
void CheckStl : : knownEmptyContainerError ( const Token * tok , const std : : string & algo )
2020-08-22 09:16:26 +02:00
{
2020-09-02 07:11:23 +02:00
const std : : string var = tok ? tok - > expressionString ( ) : std : : string ( " var " ) ;
std : : string msg ;
if ( astIsIterator ( tok ) ) {
msg = " Using " + algo + " with iterator ' " + var + " ' that is always empty. " ;
} else {
msg = " Iterating over container ' " + var + " ' that is always empty. " ;
}
2020-08-22 09:16:26 +02:00
reportError ( tok , Severity : : style ,
2020-09-02 07:11:23 +02:00
" knownEmptyContainer " ,
msg , CWE398 , false ) ;
2020-08-22 09:16:26 +02:00
}
2020-09-02 07:11:23 +02:00
static bool isKnownEmptyContainer ( const Token * tok )
{
if ( ! tok )
return false ;
for ( const ValueFlow : : Value & v : tok - > values ( ) ) {
if ( ! v . isKnown ( ) )
continue ;
if ( ! v . isContainerSizeValue ( ) )
continue ;
if ( v . intvalue ! = 0 )
continue ;
return true ;
}
return false ;
}
void CheckStl : : knownEmptyContainer ( )
2020-08-22 09:16:26 +02:00
{
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 ( ) ) {
2020-09-02 07:11:23 +02:00
if ( ! Token : : Match ( tok , " %name% ( !!) " ) )
2020-08-22 09:16:26 +02:00
continue ;
2020-09-02 07:11:23 +02:00
// Parse range-based for loop
if ( tok - > str ( ) = = " for " ) {
if ( ! Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) { " ) )
2020-08-22 09:16:26 +02:00
continue ;
2020-09-02 07:11:23 +02:00
const Token * splitTok = tok - > next ( ) - > astOperand2 ( ) ;
if ( ! Token : : simpleMatch ( splitTok , " : " ) )
2020-08-22 09:16:26 +02:00
continue ;
2020-09-02 07:11:23 +02:00
const Token * contTok = splitTok - > astOperand2 ( ) ;
if ( ! isKnownEmptyContainer ( contTok ) )
continue ;
knownEmptyContainerError ( contTok , " " ) ;
} else {
const std : : vector < const Token * > args = getArguments ( tok ) ;
if ( args . empty ( ) )
2020-08-22 09:16:26 +02:00
continue ;
2020-09-02 07:11:23 +02:00
for ( int argnr = 1 ; argnr < = args . size ( ) ; + + argnr ) {
const Library : : ArgumentChecks : : IteratorInfo * i = mSettings - > library . getArgIteratorInfo ( tok , argnr ) ;
if ( ! i )
continue ;
const Token * const argTok = args [ argnr - 1 ] ;
if ( ! isKnownEmptyContainer ( argTok ) )
continue ;
knownEmptyContainerError ( argTok , tok - > str ( ) ) ;
break ;
2020-09-02 13:03:30 +02:00
2020-09-02 07:11:23 +02:00
}
}
2020-08-22 09:16:26 +02:00
}
}
}
2020-06-16 02:40:54 +02:00
static bool isMutex ( const Variable * var )
{
2020-06-18 00:06:06 +02:00
const Token * tok = Token : : typeDecl ( var - > nameToken ( ) ) . first ;
2020-06-16 02:40:54 +02:00
return Token : : Match ( tok , " std :: mutex|recursive_mutex|timed_mutex|recursive_timed_mutex|shared_mutex " ) ;
}
static bool isLockGuard ( const Variable * var )
{
2020-06-18 00:06:06 +02:00
const Token * tok = Token : : typeDecl ( var - > nameToken ( ) ) . first ;
2020-06-16 02:40:54 +02:00
return Token : : Match ( tok , " std :: lock_guard|unique_lock|scoped_lock " ) ;
}
2020-06-18 00:06:06 +02:00
static bool isLocalMutex ( const Variable * var , const Scope * scope )
{
if ( ! var )
return false ;
return ! var - > isReference ( ) & & ! var - > isRValueReference ( ) & & ! var - > isStatic ( ) & & var - > scope ( ) = = scope ;
}
2020-06-16 02:40:54 +02:00
void CheckStl : : globalLockGuardError ( const Token * tok )
{
2020-06-16 17:32:39 +02:00
reportError ( tok , Severity : : warning ,
2020-06-16 02:40:54 +02:00
" globalLockGuard " ,
2020-06-25 01:09:30 +02:00
" Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program. " , CWE833 , false ) ;
2020-06-16 02:40:54 +02:00
}
void CheckStl : : localMutexError ( const Token * tok )
{
2020-06-16 17:32:39 +02:00
reportError ( tok , Severity : : warning ,
2020-06-16 02:40:54 +02:00
" localMutex " ,
2020-06-16 17:32:39 +02:00
" The lock is ineffective because the mutex is locked at the same scope as the mutex itself. " , CWE667 , false ) ;
2020-06-16 02:40:54 +02:00
}
void CheckStl : : checkMutexes ( )
{
for ( const Scope * function : mTokenizer - > getSymbolDatabase ( ) - > functionScopes ) {
2020-07-13 04:31:53 +02:00
std : : set < nonneg int > checkedVars ;
2020-06-16 02:40:54 +02:00
for ( const Token * tok = function - > bodyStart ; tok ! = function - > bodyEnd ; tok = tok - > next ( ) ) {
2020-07-13 04:31:53 +02:00
if ( ! Token : : Match ( tok , " %var% " ) )
continue ;
2020-06-16 02:40:54 +02:00
const Variable * var = tok - > variable ( ) ;
if ( ! var )
continue ;
if ( Token : : Match ( tok , " %var% . lock ( ) " ) ) {
if ( ! isMutex ( var ) )
continue ;
2020-07-13 04:31:53 +02:00
if ( ! checkedVars . insert ( var - > declarationId ( ) ) . second )
continue ;
2020-06-18 00:06:06 +02:00
if ( isLocalMutex ( var , tok - > scope ( ) ) )
2020-06-16 02:40:54 +02:00
localMutexError ( tok ) ;
2020-06-26 22:06:20 +02:00
} else if ( Token : : Match ( tok , " %var% (|{ %var% ) | } | , " )) {
2020-06-16 02:40:54 +02:00
if ( ! isLockGuard ( var ) )
continue ;
const Variable * mvar = tok - > tokAt ( 2 ) - > variable ( ) ;
2020-07-13 04:31:53 +02:00
if ( ! mvar )
continue ;
if ( ! checkedVars . insert ( mvar - > declarationId ( ) ) . second )
continue ;
2020-06-16 02:40:54 +02:00
if ( var - > isStatic ( ) | | var - > isGlobal ( ) )
globalLockGuardError ( tok ) ;
2020-06-18 00:06:06 +02:00
else if ( isLocalMutex ( mvar , tok - > scope ( ) ) )
2020-06-16 02:40:54 +02:00
localMutexError ( tok ) ;
}
}
}
}