2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2022-02-05 11:45:17 +01:00
* Copyright ( C ) 2007 - 2022 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
2021-01-11 18:47:38 +01:00
# include "astutils.h"
2020-08-17 23:36:45 +02:00
# include "check.h"
2021-01-11 18:47:38 +01:00
# include "errortypes.h"
2020-04-13 13:44:48 +02:00
# include "library.h"
# include "mathlib.h"
2021-01-11 18:47:38 +01:00
# include "pathanalysis.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
# include "standards.h"
# include "symboldatabase.h"
# include "token.h"
2021-08-16 09:19:07 +02:00
# include "tokenize.h"
2015-11-29 10:49:10 +01:00
# include "utils.h"
2020-06-06 17:54:56 +02:00
# include "valueflow.h"
2017-05-27 04:33:47 +02:00
2022-01-27 19:03:20 +01:00
# include "checknullpointer.h"
2020-08-26 21:05:17 +02:00
# include <algorithm>
2022-01-27 19:03:20 +01:00
# include <cassert>
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>
2022-01-27 19:03:20 +01:00
# include <memory>
2017-05-27 04:33:47 +02:00
# include <set>
2010-10-10 10:52:41 +02:00
# include <sstream>
2022-01-27 19:03:20 +01:00
# include <tuple>
# include <type_traits>
2021-01-11 18:47:38 +01:00
# include <unordered_map>
2017-05-27 04:33:47 +02:00
# include <utility>
2022-01-27 19:03:20 +01:00
# include <vector>
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
2020-09-07 06:46:31 +02:00
static bool isElementAccessYield ( const Library : : Container : : Yield & yield )
{
2021-11-13 07:45:29 +01:00
return contains ( { Library : : Container : : Yield : : ITEM , Library : : Container : : Yield : : AT_INDEX } , yield ) ;
}
2022-03-27 10:02:30 +02:00
static bool containerAppendsElement ( const Library : : Container * container , const Token * parent )
{
if ( Token : : Match ( parent , " . %name% ( " ) ) {
Library : : Container : : Action action = container - > getAction ( parent - > strAt ( 1 ) ) ;
if ( contains ( { Library : : Container : : Action : : INSERT ,
Library : : Container : : Action : : CHANGE ,
Library : : Container : : Action : : CHANGE_INTERNAL ,
Library : : Container : : Action : : PUSH ,
Library : : Container : : Action : : RESIZE } ,
action ) )
return true ;
}
return false ;
}
2021-11-13 07:45:29 +01:00
static bool containerYieldsElement ( const Library : : Container * container , const Token * parent )
{
if ( Token : : Match ( parent , " . %name% ( " ) ) {
Library : : Container : : Yield yield = container - > getYield ( parent - > strAt ( 1 ) ) ;
if ( isElementAccessYield ( yield ) )
return true ;
}
return false ;
}
static const Token * getContainerIndex ( const Library : : Container * container , const Token * parent )
{
if ( Token : : Match ( parent , " . %name% ( " ) ) {
Library : : Container : : Yield yield = container - > getYield ( parent - > strAt ( 1 ) ) ;
2022-03-27 10:02:30 +02:00
if ( yield = = Library : : Container : : Yield : : AT_INDEX & & ! Token : : simpleMatch ( parent - > tokAt ( 2 ) , " ( ) " ) )
2021-11-13 07:45:29 +01:00
return parent - > tokAt ( 2 ) - > astOperand2 ( ) ;
}
if ( ! container - > arrayLike_indexOp & & ! container - > stdStringLike )
return nullptr ;
if ( Token : : simpleMatch ( parent , " [ " ) )
return parent - > astOperand2 ( ) ;
return nullptr ;
}
static const Token * getContainerFromSize ( const Library : : Container * container , const Token * tok )
{
if ( ! tok )
return nullptr ;
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " . %name% ( " ) ) {
Library : : Container : : Yield yield = container - > getYield ( tok - > strAt ( - 1 ) ) ;
if ( yield = = Library : : Container : : Yield : : SIZE )
return tok - > tokAt ( - 2 ) - > astOperand1 ( ) ;
}
return nullptr ;
2020-09-07 06:46:31 +02: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 ) ;
2022-06-23 20:26:47 +02:00
if ( ! container | | container - > stdAssociativeLike )
2018-08-11 11:40:48 +02:00
continue ;
2019-08-15 21:14:27 +02:00
const Token * parent = astParentSkipParens ( tok ) ;
2021-11-13 07:45:29 +01:00
const Token * accessTok = parent ;
if ( Token : : simpleMatch ( accessTok , " . " ) & & Token : : simpleMatch ( accessTok - > astParent ( ) , " ( " ) )
accessTok = accessTok - > astParent ( ) ;
if ( astIsIterator ( accessTok ) & & Token : : simpleMatch ( accessTok - > astParent ( ) , " + " ) )
accessTok = accessTok - > astParent ( ) ;
const Token * indexTok = getContainerIndex ( container , parent ) ;
if ( indexTok = = tok )
continue ;
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 ;
2021-02-24 22:00:06 +01:00
if ( value . isInconclusive ( ) & & ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) )
2018-08-11 11:40:48 +02:00
continue ;
2021-02-24 22:00:06 +01:00
if ( ! value . errorSeverity ( ) & & ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2018-08-11 11:40:48 +02:00
continue ;
2022-03-27 10:02:30 +02:00
if ( value . intvalue = = 0 & & ( indexTok | | ( containerYieldsElement ( container , parent ) & &
! containerAppendsElement ( container , parent ) ) ) ) {
2021-11-13 07:45:29 +01:00
std : : string indexExpr ;
if ( indexTok & & ! indexTok - > hasKnownValue ( ) )
indexExpr = indexTok - > expressionString ( ) ;
outOfBoundsError ( accessTok , tok - > expressionString ( ) , & value , indexExpr , nullptr ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
2021-11-13 07:45:29 +01:00
if ( indexTok ) {
2021-08-16 09:19:07 +02:00
std : : vector < ValueFlow : : Value > indexValues =
ValueFlow : : isOutOfBounds ( value , indexTok , mSettings - > severity . isEnabled ( Severity : : warning ) ) ;
if ( ! indexValues . empty ( ) ) {
outOfBoundsError (
2021-11-13 07:45:29 +01:00
accessTok , tok - > expressionString ( ) , & value , indexTok - > expressionString ( ) , & indexValues . front ( ) ) ;
2018-08-11 11:40:48 +02:00
continue ;
}
}
}
2021-11-13 07:45:29 +01:00
if ( indexTok & & ! indexTok - > hasKnownIntValue ( ) ) {
const ValueFlow : : Value * value =
ValueFlow : : findValue ( indexTok - > values ( ) , mSettings , [ & ] ( const ValueFlow : : Value & v ) {
if ( ! v . isSymbolicValue ( ) )
return false ;
if ( v . isImpossible ( ) )
return false ;
if ( v . intvalue < 0 )
return false ;
2022-01-13 08:03:24 +01:00
const Token * sizeTok = v . tokvalue ;
if ( sizeTok & & sizeTok - > isCast ( ) )
sizeTok = sizeTok - > astOperand1 ( ) ;
const Token * containerTok = getContainerFromSize ( container , sizeTok ) ;
2021-11-13 07:45:29 +01:00
if ( ! containerTok )
return false ;
return containerTok - > exprId ( ) = = tok - > exprId ( ) ;
} ) ;
if ( ! value )
continue ;
outOfBoundsError ( accessTok , tok - > expressionString ( ) , nullptr , indexTok - > expressionString ( ) , value ) ;
}
2018-08-11 11:40:48 +02:00
}
}
}
2022-07-10 10:57:29 +02:00
static std : : string indexValueString ( const ValueFlow : : Value & indexValue , const std : : string & containerName = emptyString )
2020-08-26 21:05:17 +02:00
{
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 " ;
2021-11-13 07:45:29 +01:00
std : : string indexString = MathLib : : toString ( indexValue . intvalue ) ;
if ( indexValue . isSymbolicValue ( ) ) {
indexString = containerName + " .size() " ;
if ( indexValue . intvalue ! = 0 )
indexString + = " + " + MathLib : : toString ( indexValue . intvalue ) ;
}
2021-08-16 09:19:07 +02:00
if ( indexValue . bound = = ValueFlow : : Value : : Bound : : Lower )
2021-11-13 07:45:29 +01:00
return " greater or equal to " + indexString ;
return indexString ;
2020-08-26 21:05:17 +02:00
}
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 ;
2021-11-13 07:45:29 +01:00
if ( ! containerSize ) {
if ( indexValue & & indexValue - > condition )
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( indexValue - > condition ) + " or ' " + index +
" ' can have the value " + indexValueString ( * indexValue , containerName ) + " . Expression ' " +
expression + " ' cause access out of bounds. " ;
else
errmsg = " Out of bounds access in expression ' " + expression + " ' " ;
} else if ( containerSize - > intvalue = = 0 ) {
2018-08-11 11:40:48 +02:00
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 ,
2021-02-24 22:00:06 +01:00
( containerSize & & containerSize - > isInconclusive ( ) ) | | ( indexValue & & indexValue - > isInconclusive ( ) ) ? Certainty : : inconclusive : Certainty : : normal ) ;
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 ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
2018-11-28 19:27:28 +01:00
}
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 )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " invalidIterator1 " , " $symbol: " + iteratorName + " \n Invalid iterator: $symbol " , CWE664 , Certainty : : normal ) ;
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 "
2021-02-24 22:00:06 +01:00
" Same iterator is used with different containers ' " + containerName1 + " ' and ' " + containerName2 + " '. " , CWE664 , Certainty : : normal ) ;
2018-10-17 06:36:51 +02:00
}
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 "
2021-02-24 22:00:06 +01:00
" Same iterator is used with different containers ' " + containerName1 + " ' and ' " + containerName2 + " '. " , CWE664 , Certainty : : normal ) ;
2018-10-17 06:36:51 +02:00
}
void CheckStl : : iteratorsError ( const Token * tok , const Token * containerTok , const std : : string & containerName )
{
std : : list < const Token * > callstack = { tok , containerTok } ;
2021-12-04 12:55:56 +01:00
reportError ( callstack ,
Severity : : error ,
" iterators3 " ,
" $symbol: " + containerName +
" \n "
" Same iterator is used with containers '$symbol' that are temporaries or defined in different scopes. " ,
CWE664 ,
Certainty : : normal ) ;
2018-10-17 06:36:51 +02:00
}
2009-05-03 07:37:39 +02:00
// Error message used when dereferencing an iterator that has been erased..
2018-01-10 09:37:21 +01:00
void CheckStl : : dereferenceErasedError ( const Token * erased , const Token * deref , const std : : string & itername , bool inconclusive )
2009-05-03 07:37:39 +02:00
{
2012-10-17 00:29:06 +02:00
if ( erased ) {
2018-04-09 09:54:39 +02:00
std : : list < const Token * > callstack = { deref , erased } ;
2012-10-14 11:16:48 +02:00
reportError ( callstack , Severity : : error , " eraseDereference " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + itername + " \n "
" Iterator '$symbol' used after element has been erased. \n "
" The iterator '$symbol' is invalid after the element it pointed to has been erased. "
2021-02-24 22:00:06 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
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. "
2021-02-24 22:00:06 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
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 ;
2021-02-24 22:00:06 +01:00
if ( inconclusiveType & & ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) )
2018-10-09 20:10:43 +02:00
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 ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
2020-06-06 17:54:56 +02:00
}
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 ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
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 ' " +
2021-02-24 22:00:06 +01:00
expr1 + " ' and ' " + expr2 + " ' are used together. " , CWE664 , Certainty : : normal ) ;
2018-07-26 22:00:48 +02:00
}
2018-08-05 09:10:54 +02:00
void CheckStl : : sameIteratorExpressionError ( const Token * tok )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : style , " sameIteratorExpression " , " Same iterators expression are used for algorithm. " , CWE664 , Certainty : : normal ) ;
2018-08-05 09:10:54 +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 ;
}
2021-11-23 22:50:32 +01:00
static const Token * getAddressContainer ( const Token * tok )
{
if ( Token : : simpleMatch ( tok , " [ " ) & & tok - > astOperand1 ( ) )
return tok - > astOperand1 ( ) ;
return tok ;
}
static bool isSameIteratorContainerExpression ( const Token * tok1 ,
const Token * tok2 ,
const Library & library ,
ValueFlow : : Value : : LifetimeKind kind = ValueFlow : : Value : : LifetimeKind : : Iterator )
{
2021-12-04 12:55:56 +01:00
if ( isSameExpression ( true , false , tok1 , tok2 , library , false , false ) ) {
if ( astIsContainerOwned ( tok1 ) & & isTemporary ( true , tok1 , & library ) )
return false ;
2021-11-23 22:50:32 +01:00
return true ;
2021-12-04 12:55:56 +01:00
}
2021-11-23 22:50:32 +01:00
if ( kind = = ValueFlow : : Value : : LifetimeKind : : Address ) {
return isSameExpression ( true , false , getAddressContainer ( tok1 ) , getAddressContainer ( tok2 ) , library , false , false ) ;
}
return false ;
}
2022-01-18 14:48:02 +01:00
static ValueFlow : : Value getLifetimeIteratorValue ( const Token * tok , MathLib : : bigint path = 0 )
2021-11-23 22:50:32 +01:00
{
2022-01-18 14:48:02 +01:00
std : : vector < ValueFlow : : Value > values = getLifetimeObjValues ( tok , false , path ) ;
2021-11-23 22:50:32 +01:00
auto it = std : : find_if ( values . begin ( ) , values . end ( ) , [ ] ( const ValueFlow : : Value & v ) {
return v . lifetimeKind = = ValueFlow : : Value : : LifetimeKind : : Iterator ;
} ) ;
if ( it ! = values . end ( ) )
return * it ;
if ( values . size ( ) = = 1 )
return values . front ( ) ;
return ValueFlow : : Value { } ;
}
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 ;
2021-11-23 22:50:32 +01:00
ValueFlow : : Value val1 = getLifetimeIteratorValue ( tok1 ) ;
ValueFlow : : Value val2 = getLifetimeIteratorValue ( tok2 ) ;
2019-12-25 09:32:50 +01:00
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 ;
}
2021-11-23 22:50:32 +01:00
if ( isSameIteratorContainerExpression ( val1 . tokvalue , val2 . tokvalue , mSettings - > library , val1 . lifetimeKind ) )
2019-12-25 09:32:50 +01:00
return false ;
if ( val1 . tokvalue - > expressionString ( ) = = val2 . tokvalue - > expressionString ( ) )
iteratorsError ( tok1 , val1 . tokvalue , val1 . tokvalue - > expressionString ( ) ) ;
else
mismatchingContainersError ( val1 . tokvalue , val2 . tokvalue ) ;
return true ;
}
2021-04-19 09:17:02 +02:00
if ( Token : : Match ( tok1 - > astParent ( ) , " %comp%|- " ) ) {
if ( astIsIntegral ( tok1 , false ) | | astIsIntegral ( tok2 , false ) | | astIsFloat ( tok1 , false ) | |
astIsFloat ( tok2 , false ) )
return false ;
}
2019-12-25 09:32:50 +01:00
const Token * iter1 = getIteratorExpression ( tok1 ) ;
const Token * iter2 = getIteratorExpression ( tok2 ) ;
2021-11-23 22:50:32 +01:00
if ( iter1 & & iter2 & & ! isSameIteratorContainerExpression ( iter1 , iter2 , mSettings - > library ) ) {
2019-12-25 09:32:50 +01:00
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 ] ;
2022-07-28 22:53:59 +02:00
containers [ i - > container ] . emplace_back ( ArgIteratorInfo { argTok , i } ) ;
2019-12-25 09:32:50 +01:00
}
// 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 ( ) ) {
2022-02-08 09:02:59 +01:00
if ( var & & var - > isStlStringType ( ) & & Token : : Match ( var - > nameToken ( ) , " %var% ( " ) & &
Token : : Match ( var - > nameToken ( ) - > tokAt ( 2 ) , " %name% . begin|cbegin|rbegin|crbegin ( ) , %name% . end|cend|rend|crend ( ) ,|) " ) ) {
2015-07-21 14:13:26 +02:00
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 ;
2021-11-23 22:50:32 +01:00
if ( ! astIsLHS ( tok ) )
2020-06-06 17:54:56 +02:00
continue ;
2021-11-23 22:50:32 +01:00
if ( ! Token : : Match ( tok - > astParent ( ) , " . %name% ( !!) " ) )
continue ;
const Token * const ftok = tok - > astParent ( ) - > next ( ) ;
2020-06-06 17:54:56 +02:00
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 ;
}
2021-11-23 22:50:32 +01:00
ValueFlow : : Value val = getLifetimeIteratorValue ( iterTok ) ;
2020-06-06 17:54:56 +02:00
if ( ! val . tokvalue )
continue ;
if ( val . lifetimeKind ! = ValueFlow : : Value : : LifetimeKind : : Iterator )
continue ;
2021-11-23 22:50:32 +01:00
if ( isSameIteratorContainerExpression ( tok , val . tokvalue , mSettings - > library ) )
continue ;
mismatchingContainerIteratorError ( tok , iterTok ) ;
2020-06-06 17:54:56 +02:00
}
}
}
2021-10-15 11:54:29 +02:00
static const Token * getInvalidMethod ( const Token * tok )
2019-07-18 10:56:44 +02:00
{
2021-10-15 11:54:29 +02:00
if ( ! astIsLHS ( tok ) )
return nullptr ;
if ( Token : : Match ( tok - > astParent ( ) , " . assign|clear|swap " ) )
return tok - > astParent ( ) - > next ( ) ;
if ( Token : : Match ( tok - > astParent ( ) , " %assign% " ) )
return tok - > astParent ( ) ;
const Token * ftok = nullptr ;
if ( Token : : Match ( tok - > astParent ( ) , " . %name% ( " ) )
ftok = tok - > astParent ( ) - > next ( ) ;
if ( ! ftok )
return nullptr ;
2020-04-03 13:16:57 +02:00
if ( const Library : : Container * c = tok - > valueType ( ) - > container ) {
2021-10-15 11:54:29 +02:00
Library : : Container : : Action action = c - > getAction ( ftok - > str ( ) ) ;
2020-04-03 13:16:57 +02:00
if ( c - > unstableErase ) {
if ( action = = Library : : Container : : Action : : ERASE )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
}
if ( c - > unstableInsert ) {
if ( action = = Library : : Container : : Action : : RESIZE )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : CLEAR )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : PUSH )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : POP )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : INSERT )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : CHANGE )
2021-10-15 11:54:29 +02:00
return ftok ;
2020-04-03 13:16:57 +02:00
if ( action = = Library : : Container : : Action : : CHANGE_INTERNAL )
2021-10-15 11:54:29 +02:00
return ftok ;
if ( Token : : Match ( ftok , " insert|emplace " ) )
return ftok ;
2020-04-03 13:16:57 +02:00
}
}
2021-10-15 11:54:29 +02:00
return nullptr ;
2019-07-18 10:56:44 +02:00
}
2021-01-11 18:47:38 +01:00
struct InvalidContainerAnalyzer {
struct Info {
struct Reference {
const Token * tok ;
ErrorPath errorPath ;
2021-10-15 11:54:29 +02:00
const Token * ftok ;
2021-01-11 18:47:38 +01:00
} ;
std : : unordered_map < int , Reference > expressions ;
ErrorPath errorPath ;
2021-01-12 21:28:56 +01:00
void add ( const std : : vector < Reference > & refs ) {
2021-01-11 18:47:38 +01:00
for ( const Reference & r : refs ) {
add ( r ) ;
}
}
2021-01-12 21:28:56 +01:00
void add ( const Reference & r ) {
2021-01-11 18:47:38 +01:00
if ( ! r . tok )
return ;
expressions . insert ( std : : make_pair ( r . tok - > exprId ( ) , r ) ) ;
}
2021-01-12 21:28:56 +01:00
std : : vector < Reference > invalidTokens ( ) const {
2021-01-11 18:47:38 +01:00
std : : vector < Reference > result ;
std : : transform ( expressions . begin ( ) , expressions . end ( ) , std : : back_inserter ( result ) , SelectMapValues { } ) ;
return result ;
}
} ;
std : : unordered_map < const Function * , Info > invalidMethods ;
2021-01-12 21:28:56 +01:00
std : : vector < Info : : Reference > invalidatesContainer ( const Token * tok ) const {
2021-01-11 18:47:38 +01:00
std : : vector < Info : : Reference > result ;
if ( Token : : Match ( tok , " %name% ( " ) ) {
const Function * f = tok - > function ( ) ;
if ( ! f )
return result ;
ErrorPathItem epi = std : : make_pair ( tok , " Calling function " + tok - > str ( ) ) ;
const bool dependsOnThis = exprDependsOnThis ( tok - > next ( ) ) ;
auto it = invalidMethods . find ( f ) ;
if ( it ! = invalidMethods . end ( ) ) {
std : : vector < Info : : Reference > refs = it - > second . invalidTokens ( ) ;
std : : copy_if ( refs . begin ( ) , refs . end ( ) , std : : back_inserter ( result ) , [ & ] ( const Info : : Reference & r ) {
const Variable * var = r . tok - > variable ( ) ;
if ( ! var )
return false ;
if ( dependsOnThis & & ! var - > isLocal ( ) & & ! var - > isGlobal ( ) & & ! var - > isStatic ( ) )
return true ;
if ( ! var - > isArgument ( ) )
return false ;
if ( ! var - > isReference ( ) )
return false ;
return true ;
} ) ;
std : : vector < const Token * > args = getArguments ( tok ) ;
for ( Info : : Reference & r : result ) {
r . errorPath . push_front ( epi ) ;
2021-10-15 11:54:29 +02:00
r . ftok = tok ;
2021-01-11 18:47:38 +01:00
const Variable * var = r . tok - > variable ( ) ;
if ( ! var )
continue ;
if ( var - > isArgument ( ) ) {
int n = getArgumentPos ( var , f ) ;
const Token * tok2 = nullptr ;
if ( n > = 0 & & n < args . size ( ) )
tok2 = args [ n ] ;
r . tok = tok2 ;
}
}
}
} else if ( astIsContainer ( tok ) ) {
2021-10-15 11:54:29 +02:00
const Token * ftok = getInvalidMethod ( tok ) ;
if ( ftok ) {
2021-01-11 18:47:38 +01:00
ErrorPath ep ;
2021-10-15 11:54:29 +02:00
ep . emplace_front ( ftok ,
" After calling ' " + ftok - > expressionString ( ) +
2021-01-12 21:28:56 +01:00
" ', iterators or references to the container's data may be invalid . " ) ;
2022-07-28 22:53:59 +02:00
result . emplace_back ( Info : : Reference { tok , ep , ftok } ) ;
2021-01-11 18:47:38 +01:00
}
}
return result ;
}
2021-01-12 21:28:56 +01:00
void analyze ( const SymbolDatabase * symboldatabase ) {
2021-01-11 18:47:38 +01:00
for ( const Scope * scope : symboldatabase - > functionScopes ) {
const Function * f = scope - > function ;
if ( ! f )
continue ;
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " if|while|for|goto|return " ) )
break ;
std : : vector < Info : : Reference > c = invalidatesContainer ( tok ) ;
if ( c . empty ( ) )
continue ;
invalidMethods [ f ] . add ( c ) ;
}
}
}
} ;
2021-10-15 11:54:29 +02:00
static const Token * getLoopContainer ( const Token * tok )
{
if ( ! Token : : simpleMatch ( tok , " for ( " ) )
return nullptr ;
const Token * sepTok = tok - > next ( ) - > astOperand2 ( ) ;
if ( ! Token : : simpleMatch ( sepTok , " : " ) )
return nullptr ;
return sepTok - > astOperand2 ( ) ;
}
2022-04-30 09:36:28 +02:00
static const ValueFlow : : Value * getInnerLifetime ( const Token * tok ,
nonneg int id ,
ErrorPath * errorPath = nullptr ,
int depth = 4 )
{
if ( depth < 0 )
return nullptr ;
if ( ! tok )
return nullptr ;
for ( const ValueFlow : : Value & val : tok - > values ( ) ) {
if ( ! val . isLocalLifetimeValue ( ) )
continue ;
if ( contains ( { ValueFlow : : Value : : LifetimeKind : : Address ,
ValueFlow : : Value : : LifetimeKind : : SubObject ,
ValueFlow : : Value : : LifetimeKind : : Lambda } ,
val . lifetimeKind ) ) {
2022-05-05 06:54:36 +02:00
if ( val . isInconclusive ( ) )
return nullptr ;
2022-04-30 09:36:28 +02:00
if ( val . capturetok )
return getInnerLifetime ( val . capturetok , id , errorPath , depth - 1 ) ;
if ( errorPath )
errorPath - > insert ( errorPath - > end ( ) , val . errorPath . begin ( ) , val . errorPath . end ( ) ) ;
return getInnerLifetime ( val . tokvalue , id , errorPath , depth - 1 ) ;
}
if ( ! val . tokvalue - > variable ( ) )
continue ;
if ( val . tokvalue - > varId ( ) ! = id )
continue ;
return & val ;
}
return nullptr ;
}
2022-08-03 19:04:44 +02:00
static const Token * endOfExpression ( const Token * tok )
{
if ( ! tok )
return nullptr ;
const Token * parent = tok - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " . " ) )
parent = parent - > astParent ( ) ;
if ( ! parent )
return tok - > next ( ) ;
const Token * endToken = nextAfterAstRightmostLeaf ( parent ) ;
if ( ! endToken )
return parent - > next ( ) ;
return endToken ;
}
2019-07-18 10:56:44 +02:00
void CheckStl : : invalidContainer ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
const Library & library = mSettings - > library ;
2021-01-11 18:47:38 +01:00
InvalidContainerAnalyzer analyzer ;
analyzer . analyze ( symbolDatabase ) ;
2019-07-18 10:56:44 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2021-10-15 11:54:29 +02:00
if ( const Token * contTok = getLoopContainer ( tok ) ) {
const Token * blockStart = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * blockEnd = blockStart - > link ( ) ;
if ( contTok - > exprId ( ) = = 0 )
2021-01-11 18:47:38 +01:00
continue ;
2021-10-15 11:54:29 +02:00
if ( ! astIsContainer ( contTok ) )
continue ;
for ( const Token * tok2 = blockStart ; tok2 ! = blockEnd ; tok2 = tok2 - > next ( ) ) {
bool bail = false ;
for ( const InvalidContainerAnalyzer : : Info : : Reference & r : analyzer . invalidatesContainer ( tok2 ) ) {
if ( ! astIsContainer ( r . tok ) )
continue ;
if ( r . tok - > exprId ( ) ! = contTok - > exprId ( ) )
continue ;
const Scope * s = tok2 - > scope ( ) ;
if ( ! s )
continue ;
if ( isReturnScope ( s - > bodyEnd , & mSettings - > library ) )
continue ;
invalidContainerLoopError ( r . ftok , tok , r . errorPath ) ;
bail = true ;
2021-01-11 18:47:38 +01:00
break ;
2019-09-02 06:58:09 +02:00
}
2021-10-15 11:54:29 +02:00
if ( bail )
break ;
2019-09-02 06:58:09 +02:00
}
2021-10-15 11:54:29 +02:00
} else {
for ( const InvalidContainerAnalyzer : : Info : : Reference & r : analyzer . invalidatesContainer ( tok ) ) {
if ( ! astIsContainer ( r . tok ) )
continue ;
std : : set < nonneg int > skipVarIds ;
// Skip if the variable is assigned to
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 ( ) ) ;
2022-08-03 19:04:44 +02:00
const Token * endToken = endOfExpression ( tok ) ;
2021-10-15 11:54:29 +02:00
const ValueFlow : : Value * v = nullptr ;
ErrorPath errorPath ;
PathAnalysis : : Info info =
PathAnalysis { endToken , library } . forwardFind ( [ & ] ( const PathAnalysis : : Info & info ) {
if ( ! info . tok - > variable ( ) )
return false ;
if ( info . tok - > varId ( ) = = 0 )
return false ;
if ( skipVarIds . count ( info . tok - > varId ( ) ) > 0 )
return false ;
// if (Token::simpleMatch(info.tok->next(), "."))
// return false;
if ( Token : : Match ( info . tok - > astParent ( ) , " %assign% " ) & & astIsLHS ( info . tok ) )
skipVarIds . insert ( info . tok - > varId ( ) ) ;
if ( info . tok - > variable ( ) - > isReference ( ) & & ! isVariableDecl ( info . tok ) & &
reaches ( info . tok - > variable ( ) - > nameToken ( ) , tok , library , nullptr ) ) {
ErrorPath ep ;
bool addressOf = false ;
const Variable * var = getLifetimeVariable ( info . tok , ep , & addressOf ) ;
// Check the reference is created before the change
if ( var & & var - > declarationId ( ) = = r . tok - > varId ( ) & & ! addressOf ) {
// An argument always reaches
if ( var - > isArgument ( ) | |
( ! var - > isReference ( ) & & ! var - > isRValueReference ( ) & & ! isVariableDecl ( tok ) & &
reaches ( var - > nameToken ( ) , tok , library , & ep ) ) ) {
errorPath = ep ;
return true ;
}
}
}
2022-04-30 09:36:28 +02:00
ErrorPath ep ;
const ValueFlow : : Value * val = getInnerLifetime ( info . tok , r . tok - > varId ( ) , & ep ) ;
// Check the iterator is created before the change
if ( val & & val - > tokvalue ! = tok & & reaches ( val - > tokvalue , tok , library , & ep ) ) {
v = val ;
errorPath = ep ;
return true ;
2021-01-11 18:47:38 +01:00
}
2021-10-15 11:54:29 +02:00
return false ;
} ) ;
if ( ! info . tok )
continue ;
errorPath . insert ( errorPath . end ( ) , info . errorPath . begin ( ) , info . errorPath . end ( ) ) ;
errorPath . insert ( errorPath . end ( ) , r . errorPath . begin ( ) , r . errorPath . end ( ) ) ;
if ( v ) {
invalidContainerError ( info . tok , r . tok , v , errorPath ) ;
} else {
invalidContainerReferenceError ( info . tok , r . tok , errorPath ) ;
2021-01-12 21:28:56 +01:00
}
2019-07-18 10:56:44 +02:00
}
2019-09-02 06:58:09 +02:00
}
2019-07-18 10:56:44 +02:00
}
}
}
2021-10-15 11:54:29 +02:00
void CheckStl : : invalidContainerLoopError ( const Token * tok , const Token * loopTok , ErrorPath errorPath )
2020-04-04 11:47:02 +02:00
{
2021-10-15 11:54:29 +02:00
const std : : string method = tok ? tok - > str ( ) : " erase " ;
2020-04-04 11:47:02 +02:00
errorPath . emplace_back ( loopTok , " Iterating container here. " ) ;
2021-10-15 11:54:29 +02:00
// Remove duplicate entries from error path
errorPath . remove_if ( [ & ] ( const ErrorPathItem & epi ) {
return epi . first = = tok ;
} ) ;
2020-04-04 11:47:02 +02:00
const std : : string msg = " Calling ' " + method + " ' while iterating the container is invalid. " ;
errorPath . emplace_back ( tok , " " ) ;
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : error , " invalidContainerLoop " , msg , CWE664 , Certainty : : normal ) ;
2020-04-04 11:47:02 +02:00
}
2021-01-21 18:13:32 +01:00
void CheckStl : : invalidContainerError ( const Token * tok , const Token * /*contTok*/ , const ValueFlow : : Value * val , ErrorPath errorPath )
2019-07-18 10:56:44 +02:00
{
2019-09-11 19:25:09 +02:00
const bool inconclusive = val ? val - > isInconclusive ( ) : false ;
2019-07-18 10:56:44 +02:00
if ( val )
errorPath . insert ( errorPath . begin ( ) , val - > errorPath . begin ( ) , val - > errorPath . end ( ) ) ;
std : : string msg = " Using " + lifetimeMessage ( tok , val , errorPath ) ;
errorPath . emplace_back ( tok , " " ) ;
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : error , " invalidContainer " , msg + " that may be invalid. " , CWE664 , inconclusive ? Certainty : : inconclusive : Certainty : : normal ) ;
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 name = contTok ? contTok - > expressionString ( ) : " x " ;
std : : string msg = " Reference to " + name ;
errorPath . emplace_back ( tok , " " ) ;
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : error , " invalidContainerReference " , msg + " that may be invalid. " , CWE664 , Certainty : : normal ) ;
2019-09-02 06:58:09 +02:00
}
2009-02-10 20:56:00 +01:00
void CheckStl : : stlOutOfBounds ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-12-09 22:28:10 +01:00
2012-11-16 06:50:49 +01:00
// Scan through all scopes..
2018-07-13 16:46:29 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
const Token * tok = scope . classDef ;
2013-03-04 19:59:46 +01:00
// only interested in conditions
2020-12-24 22:58:19 +01:00
if ( ( ! scope . isLoopScope ( ) & & scope . type ! = Scope : : eIf ) | | ! 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 ,
2021-08-07 20:51:18 +02:00
[ & ] ( const Token * cond ) {
2018-11-28 20:30:58 +01:00
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
2021-11-13 07:45:29 +01:00
if ( containerToken - > hasKnownValue ( ValueFlow : : Value : : ValueType : : CONTAINER_SIZE ) )
continue ;
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 )
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " $symbol: " + var + " \n When " + num + " ==$symbol.size(), $symbol.at( " + num + " ) is out of bounds. " , CWE788 , Certainty : : normal ) ;
2012-02-25 12:43:27 +01:00
else
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " $symbol: " + var + " \n When " + num + " ==$symbol.size(), $symbol[ " + num + " ] is out of bounds. " , CWE788 , Certainty : : normal ) ;
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. " ;
2021-02-24 22:00:06 +01:00
reportError ( errorPath , index . errorSeverity ( ) ? Severity : : error : Severity : : warning , " negativeContainerIndex " , errmsg . str ( ) , CWE786 , index . isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
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 ;
2022-02-03 21:13:48 +01:00
if ( Token : : Match ( tok - > astParent ( ) , " =|return " ) )
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 "
2021-02-24 22:00:06 +01:00
" container is not guaranteed. One should use operator!= instead to compare iterators. " , CWE664 , Certainty : : normal ) ;
2009-04-13 17:48:13 +02:00
}
2009-11-02 20:24:38 +01:00
2021-01-31 12:05:38 +01:00
static bool if_findCompare ( const Token * const tokBack , bool stdStringLike )
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 ;
2021-01-31 12:05:38 +01:00
if ( tok - > isComparisonOp ( ) ) {
if ( stdStringLike ) {
const Token * const tokOther = tokBack - > astSibling ( ) ;
2021-10-28 08:55:08 +02:00
return ! tokOther | | ! tokOther - > hasKnownIntValue ( ) | | tokOther - > getKnownIntValue ( ) ! = 0 ;
2021-01-31 12:05:38 +01:00
}
2015-07-21 20:56:47 +02:00
return ( ! tok - > astOperand1 ( ) - > isNumber ( ) & & ! tok - > astOperand2 ( ) - > isNumber ( ) ) ;
2021-01-31 12:05:38 +01:00
}
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 ( ) )
2021-01-31 12:05:38 +01:00
return if_findCompare ( tok , stdStringLike ) ; // 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 ( )
{
2021-02-24 22:00:06 +01:00
const bool printWarning = mSettings - > severity . isEnabled ( Severity : : warning ) ;
const bool printPerformance = mSettings - > severity . isEnabled ( Severity : : 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 ( ) ;
2021-10-28 08:55:08 +02:00
if ( Token : : simpleMatch ( conditionStart - > astOperand2 ( ) , " ; " ) )
2020-02-18 19:02:54 +01:00
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 ) {
2021-01-31 12:05:38 +01:00
if ( if_findCompare ( funcTok - > next ( ) , container - > stdStringLike ) )
2015-01-04 12:43:31 +01:00
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
2021-01-31 12:05:38 +01:00
if ( ! if_findCompare ( tok - > tokAt ( 3 ) , false ) ) {
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, "
2021-02-24 22:00:06 +01:00
" you should compare with std::string::npos. " , CWE597 , Certainty : : normal ) ;
2019-05-05 10:35:22 +02:00
if ( ! str )
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " stlIfFind " , " Suspicious condition. The result of find() is an iterator, but it is not properly checked. " , CWE398 , Certainty : : normal ) ;
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 ( ) ) ;
}
2022-06-08 16:58:57 +02:00
if ( tok - > isAssignmentOp ( ) ) {
2019-05-02 11:04:23 +02:00
const Token * varTok = top - > astOperand1 ( ) ;
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : performance ) )
2019-05-02 11:04:23 +02:00
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 ;
2021-01-14 22:31:43 +01:00
// In < C++17 we only warn for small simple types
2021-01-15 08:27:58 +01:00
if ( mSettings - > standards . cpp < Standards : : CPP17 & & ! ( keyTok & & keyTok - > valueType ( ) & & ( keyTok - > valueType ( ) - > isIntegral ( ) | | keyTok - > valueType ( ) - > pointer > 0 ) ) )
2021-01-14 22:31:43 +01:00
continue ;
2019-05-02 11:04:23 +02:00
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 )
{
2021-10-24 19:13:42 +02:00
std : : string replaceExpr ;
if ( tok & & Token : : simpleMatch ( tok - > astParent ( ) , " = " ) & & tok = = tok - > astParent ( ) - > astOperand2 ( ) & & Token : : simpleMatch ( tok - > astParent ( ) - > astOperand1 ( ) , " [ " ) ) {
2021-10-25 19:12:49 +02:00
if ( mSettings - > standards . cpp < Standards : : CPP11 )
// We will recommend using emplace/try_emplace instead
return ;
const std : : string f = ( mSettings - > standards . cpp < Standards : : CPP17 ) ? " emplace " : " try_emplace " ;
2021-10-24 19:13:42 +02:00
replaceExpr = " Instead of ' " + tok - > astParent ( ) - > expressionString ( ) + " ' consider using ' " +
tok - > astParent ( ) - > astOperand1 ( ) - > astOperand1 ( ) - > expressionString ( ) +
2021-10-25 19:12:49 +02:00
" . " + f + " ( " +
2021-10-24 19:13:42 +02:00
tok - > astParent ( ) - > astOperand1 ( ) - > astOperand2 ( ) - > expressionString ( ) +
" , " +
tok - > expressionString ( ) +
" );'. " ;
}
2019-05-02 11:04:23 +02:00
reportError (
2021-10-24 19:13:42 +02:00
tok , Severity : : performance , " stlFindInsert " , " Searching before insertion is not necessary. " + replaceExpr , CWE398 , Certainty : : normal ) ;
2019-05-02 11:04:23 +02:00
}
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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
2021-10-28 08:58:16 +02:00
if ( ( ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " ==|<=|!=|> 0 " ) ) | |
2015-01-31 10:50:39 +01:00
( 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
2021-10-28 08:58:16 +02:00
if ( ( ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " >=|< 1 " ) & & ! end - > tokAt ( 2 ) - > isArithmeticalOp ( ) ) | |
2015-01-31 10:50:39 +01:00
( 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 "
2021-02-24 22:00:06 +01:00
" guaranteed to take constant time. " , CWE398 , Certainty : : normal ) ;
2009-12-19 15:24:59 +01:00
}
2010-09-16 18:49:23 +02:00
void CheckStl : : redundantCondition ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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. "
2021-02-24 22:00:06 +01:00
" It is safe to call the remove method on a non-existing element. " , CWE398 , Certainty : : normal ) ;
2010-09-16 18:49:23 +02:00
}
2010-10-10 10:52:41 +02:00
void CheckStl : : missingComparison ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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..
2021-01-04 13:09:05 +01:00
for ( const Token * tok3 = scope . bodyStart ; tok3 ! = scope . bodyEnd ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > varId ( ) = = iteratorId ) {
if ( Token : : Match ( tok3 , " %varid% = %name% . insert ( ++| %varid% ++| , " , iteratorId ) ) {
2021-01-04 10:07:07 +01:00
// skip insertion..
tok3 = tok3 - > linkAt ( 6 ) ;
if ( ! tok3 )
break ;
2021-01-18 08:11:37 +01:00
} else if ( Token : : simpleMatch ( tok3 - > astParent ( ) , " ++ " ) )
2021-01-22 21:47:24 +01:00
incrementToken = tok3 ;
2021-01-18 08:11:37 +01:00
else if ( Token : : simpleMatch ( tok3 - > astParent ( ) , " + " ) ) {
if ( Token : : Match ( tok3 - > astSibling ( ) , " %num% " ) ) {
2021-01-04 10:07:07 +01:00
const Token * tokenGrandParent = tok3 - > astParent ( ) - > astParent ( ) ;
if ( Token : : Match ( tokenGrandParent , " ==|!= " ) )
2021-01-18 08:11:37 +01:00
break ;
2021-01-04 10:07:07 +01:00
}
2021-01-04 13:09:05 +01:00
} else if ( Token : : Match ( tok3 - > astParent ( ) , " ==|!= " ) )
2021-01-04 10:07:07 +01:00
incrementToken = nullptr ;
2021-01-04 13:09:05 +01:00
} else if ( tok3 - > str ( ) = = " break " | | tok3 - > str ( ) = = " return " )
2017-07-27 18:36:33 +02:00
incrementToken = nullptr ;
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
2021-02-24 22:00:06 +01:00
reportError ( callstack , Severity : : warning , " StlMissingComparison " , errmsg . str ( ) , CWE834 , Certainty : : normal ) ;
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 ( )
{
2021-02-24 22:00:06 +01:00
const bool printInconclusive = mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) ;
const bool printPerformance = mSettings - > severity . isEnabled ( Severity : : 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
2021-02-20 12:52:39 +01:00
std : : multimap < const Function * , 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 ) {
2019-07-15 14:05:23 +02:00
int numpar = 0 ;
2018-08-10 06:47:18 +02:00
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 ( ) ) )
2021-02-20 12:52:39 +01:00
c_strFuncParam . insert ( std : : make_pair ( & func , 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 ) ;
2021-02-20 12:52:39 +01:00
} else if ( tok - > variable ( ) & & tok - > strAt ( 1 ) = = " = " ) {
if ( Token : : Match ( tok - > tokAt ( 2 ) , " %var% . str ( ) . c_str|data ( ) ; " ) ) {
const Variable * var = tok - > variable ( ) ;
const Variable * var2 = tok - > tokAt ( 2 ) - > variable ( ) ;
2021-02-20 17:22:04 +01:00
if ( var - > isPointer ( ) & & var2 & & var2 - > isStlType ( stl_string_stream ) )
2021-02-20 12:52:39 +01:00
string_c_strError ( tok ) ;
} else if ( Token : : Match ( tok - > tokAt ( 2 ) , " %name% ( " ) & &
Token : : Match ( tok - > linkAt ( 3 ) , " ) . c_str|data ( ) ; " ) & &
tok - > tokAt ( 2 ) - > function ( ) & & Token : : Match ( tok - > tokAt ( 2 ) - > function ( ) - > retDef , " std :: string|wstring %name% " ) ) {
const Variable * var = tok - > variable ( ) ;
2021-02-20 17:22:04 +01:00
if ( var - > isPointer ( ) )
2021-02-20 12:52:39 +01:00
string_c_strError ( tok ) ;
2022-07-10 11:38:01 +02:00
} else if ( printPerformance & & Token : : Match ( tok - > tokAt ( 2 ) , " %var% . c_str|data ( ) ; " )) {
2022-07-11 22:58:08 +02:00
if ( tok - > variable ( ) & & tok - > variable ( ) - > isStlStringType ( ) & & tok - > tokAt ( 2 ) - > variable ( ) & & tok - > tokAt ( 2 ) - > variable ( ) - > isStlStringType ( ) )
2022-07-10 11:38:01 +02:00
string_c_strAssignment ( tok ) ;
2021-02-20 12:52:39 +01:00
}
2022-06-08 16:58:57 +02:00
} else if ( printPerformance & & tok - > function ( ) & & Token : : Match ( tok , " %name% ( !!) " ) & & tok - > str ( ) ! = scope . className ) {
2021-02-20 12:52:39 +01:00
const std : : pair < std : : multimap < const Function * , int > : : const_iterator , std : : multimap < const Function * , int > : : const_iterator > range = c_strFuncParam . equal_range ( tok - > function ( ) ) ;
for ( std : : multimap < const Function * , 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
}
2022-07-11 22:58:08 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% (|{ %var% . c_str|data ( ) " ) & &
tok - > variable ( ) & & tok - > variable ( ) - > isStlStringType ( ) & & tok - > tokAt ( 2 ) - > variable ( ) & & tok - > tokAt ( 2 ) - > variable ( ) - > isStlStringType ( ) ) {
2022-07-10 11:38:01 +02:00
string_c_strConstructor ( tok ) ;
} else if ( printPerformance & & tok - > next ( ) & & tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlStringType ( ) & & tok - > valueType ( ) & & tok - > valueType ( ) - > type = = ValueType : : CONTAINER & &
2022-07-11 22:58:08 +02:00
( ( Token : : Match ( tok - > previous ( ) , " %var% + %var% . c_str|data ( ) " ) & & tok - > previous ( ) - > variable ( ) & & tok - > previous ( ) - > variable ( ) - > isStlStringType ( ) ) | |
( Token : : Match ( tok - > tokAt ( - 5 ) , " %var% . c_str|data ( ) + %var% " ) & & tok - > tokAt ( - 5 ) - > variable ( ) & & tok - > tokAt ( - 5 ) - > variable ( ) - > isStlStringType ( ) ) ) ) {
2022-07-10 11:38:01 +02:00
string_c_strConcat ( tok ) ;
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*
2021-02-20 12:52:39 +01:00
else if ( ( returnType = = charPtr | | ( printPerformance & & ( returnType = = stdString | | returnType = = stdStringConstRef ) ) ) & & tok - > str ( ) = = " return " ) {
2016-05-04 11:10:12 +02:00
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 "
2021-02-24 22:00:06 +01:00
" Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted. " , CWE664 , Certainty : : normal ) ;
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 "
2021-02-24 22:00:06 +01: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 , Certainty : : normal ) ;
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. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : performance , " stlcstrParam " , oss . str ( ) , CWE704 , Certainty : : normal ) ;
2012-02-25 12:43:27 +01:00
}
2022-07-10 11:38:01 +02:00
void CheckStl : : string_c_strConstructor ( const Token * tok )
{
std : : string msg = " Constructing a std::string from the result of c_str() is slow and redundant. \n Solve that by directly passing the string. " ;
reportError ( tok , Severity : : performance , " stlcstrConstructor " , msg , CWE704 , Certainty : : normal ) ;
}
void CheckStl : : string_c_strAssignment ( const Token * tok )
{
std : : string msg = " Assigning the result of c_str() to a std::string is slow and redundant. \n Solve that by directly assigning the string. " ;
reportError ( tok , Severity : : performance , " stlcstrAssignment " , msg , CWE704 , Certainty : : normal ) ;
}
void CheckStl : : string_c_strConcat ( const Token * tok )
{
std : : string msg = " Concatenating the result of c_str() and a std::string is slow and redundant. \n Solve that by directly concatenating the strings. " ;
reportError ( tok , Severity : : performance , " stlcstrConcat " , msg , CWE704 , Certainty : : normal ) ;
}
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 ( )
{
2021-02-24 22:00:06 +01:00
const bool printPerformance = mSettings - > severity . isEnabled ( Severity : : performance ) ;
const bool printWarning = mSettings - > severity . isEnabled ( Severity : : 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 ( ) ) ;
2022-07-11 23:07:37 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% . substr ( " ) & & tok - > variable ( ) & & tok - > variable ( ) - > isStlStringType ( ) ) {
const Token * funcTok = tok - > tokAt ( 3 ) ;
const std : : vector < const Token * > args = getArguments ( funcTok ) ;
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " %var% = " ) & & tok - > varId ( ) = = tok - > tokAt ( - 2 ) - > varId ( ) & &
! args . empty ( ) & & args [ 0 ] - > hasKnownIntValue ( ) & & args [ 0 ] - > getKnownIntValue ( ) = = 0 ) {
uselessCallsSubstrError ( tok , Token : : simpleMatch ( funcTok - > astParent ( ) , " = " ) ? SubstrErrorType : : PREFIX : SubstrErrorType : : PREFIX_CONCAT ) ;
} else if ( args . empty ( ) | | ( args [ 0 ] - > hasKnownIntValue ( ) & & args [ 0 ] - > getKnownIntValue ( ) = = 0 & &
( args . size ( ) = = 1 | | ( args . size ( ) = = 2 & & tok - > linkAt ( 3 ) - > strAt ( - 1 ) = = " npos " & & ! tok - > linkAt ( 3 ) - > previous ( ) - > variable ( ) ) ) ) ) {
uselessCallsSubstrError ( tok , SubstrErrorType : : COPY ) ;
} else if ( args . size ( ) = = 2 & & args [ 1 ] - > hasKnownIntValue ( ) & & args [ 1 ] - > getKnownIntValue ( ) = = 0 ) {
uselessCallsSubstrError ( tok , SubstrErrorType : : EMPTY ) ;
}
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 ) ) ;
2022-07-21 22:15:16 +02:00
else if ( printPerformance & & tok - > valueType ( ) & & tok - > valueType ( ) - > type = = ValueType : : CONTAINER ) {
if ( Token : : Match ( tok , " %var% = { %var% . begin ( ) , " ) & & tok - > varId ( ) = = tok - > tokAt ( 3 ) - > varId ( ) )
uselessCallsConstructorError ( tok ) ;
else if ( const Variable * var = tok - > variable ( ) ) {
std : : string pattern = " %var% = " ;
for ( const Token * t = var - > typeStartToken ( ) ; t ! = var - > typeEndToken ( ) - > next ( ) ; t = t - > next ( ) ) {
pattern + = t - > str ( ) ;
pattern + = ' ' ;
}
pattern + = " {|( %varid% . begin ( ) , " ;
if ( Token : : Match ( tok , pattern . c_str ( ) , tok - > varId ( ) ) )
uselessCallsConstructorError ( tok ) ;
}
2022-07-12 17:40:14 +02:00
}
2012-11-16 06:50:49 +01:00
}
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. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " uselessCallsCompare " , errmsg . str ( ) , CWE628 , Certainty : : normal ) ;
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 "
2021-02-24 22:00:06 +01:00
" code is inefficient. Is the object or the parameter wrong here? " , CWE628 , Certainty : : normal ) ;
2011-10-24 23:25:23 +02:00
}
2022-07-11 23:07:37 +02:00
void CheckStl : : uselessCallsSubstrError ( const Token * tok , SubstrErrorType type )
{
std : : string msg = " Ineffective call of function 'substr' because " ;
switch ( type ) {
case SubstrErrorType : : EMPTY :
msg + = " it returns an empty string. " ;
break ;
case SubstrErrorType : : COPY :
msg + = " it returns a copy of the object. Use operator= instead. " ;
break ;
case SubstrErrorType : : PREFIX :
msg + = " a prefix of the string is assigned to itself. Use resize() or pop_back() instead. " ;
break ;
case SubstrErrorType : : PREFIX_CONCAT :
msg + = " a prefix of the string is assigned to itself. Use replace() instead. " ;
break ;
}
reportError ( tok , Severity : : performance , " uselessCallsSubstr " , msg , CWE398 , Certainty : : normal ) ;
2011-10-24 23:25:23 +02:00
}
2012-07-12 12:23:52 +02:00
2022-07-12 17:40:14 +02:00
void CheckStl : : uselessCallsConstructorError ( const Token * tok )
{
const std : : string msg = " Inefficient constructor call: container ' " + tok - > str ( ) + " ' is assigned a partial copy of itself. Use erase() or resize() instead. " ;
reportError ( tok , Severity : : performance , " uselessCallsConstructor " , msg , CWE398 , Certainty : : normal ) ;
}
2012-07-12 12:23:52 +02:00
void CheckStl : : uselessCallsEmptyError ( const Token * tok )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " uselessCallsEmpty " , " Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead? " , CWE398 , Certainty : : normal ) ;
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. "
2021-02-24 22:00:06 +01:00
" Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them. " , CWE762 , Certainty : : normal ) ;
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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 ) {
2020-12-24 22:58:19 +01:00
if ( ! ( scope . type = = Scope : : eIf | | scope . isLoopScope ( ) ) )
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 ( )
{
2021-02-24 22:00:06 +01:00
const bool printInconclusive = ( mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) ) ;
2020-08-17 23:36:45 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " sizeof|decltype|typeid|typeof ( " ) ) {
tok = tok - > next ( ) - > link ( ) ;
continue ;
}
2021-11-13 07:45:29 +01:00
if ( Token : : Match ( tok , " %assign% " ) )
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 ;
2021-11-13 07:45:29 +01:00
bool isInvalidIterator = false ;
2020-08-26 21:05:17 +02:00
const ValueFlow : : Value * cValue = nullptr ;
2021-11-13 07:45:29 +01:00
if ( value . isIteratorEndValue ( ) & & value . intvalue > = 0 ) {
isInvalidIterator = value . intvalue > 0 ;
} else if ( value . isIteratorStartValue ( ) & & value . intvalue < 0 ) {
isInvalidIterator = true ;
} else {
2020-08-26 21:05:17 +02:00
auto it = std : : find_if ( contValues . begin ( ) , contValues . end ( ) , [ & ] ( const ValueFlow : : Value & c ) {
2022-01-18 14:48:02 +01:00
if ( value . path ! = c . path )
return false ;
2020-08-26 21:05:17 +02:00
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 ;
2021-11-13 07:45:29 +01:00
if ( value . isIteratorStartValue ( ) & & value . intvalue > cValue - > intvalue )
isInvalidIterator = true ;
2020-08-26 21:05:17 +02:00
}
bool inconclusive = false ;
2020-08-17 23:36:45 +02:00
bool unknown = false ;
2021-11-13 07:45:29 +01:00
const Token * emptyAdvance = nullptr ;
const Token * advanceIndex = nullptr ;
if ( cValue & & cValue - > intvalue = = 0 ) {
if ( Token : : Match ( tok - > astParent ( ) , " +|- " ) & & astIsIntegral ( tok - > astSibling ( ) , false ) ) {
if ( tok - > astSibling ( ) & & tok - > astSibling ( ) - > hasKnownIntValue ( ) ) {
if ( tok - > astSibling ( ) - > values ( ) . front ( ) . intvalue = = 0 )
continue ;
} else {
advanceIndex = tok - > astSibling ( ) ;
}
emptyAdvance = tok - > astParent ( ) ;
} else if ( Token : : Match ( tok - > astParent ( ) , " ++|-- " ) ) {
emptyAdvance = tok - > astParent ( ) ;
}
}
if ( ! CheckNullPointer : : isPointerDeRef ( tok , unknown , mSettings ) & & ! isInvalidIterator & & ! emptyAdvance ) {
2020-08-26 21:05:17 +02:00
if ( ! unknown )
continue ;
inconclusive = true ;
}
if ( cValue ) {
2022-01-18 14:48:02 +01:00
const ValueFlow : : Value & lValue = getLifetimeIteratorValue ( tok , cValue - > path ) ;
2022-01-21 09:56:41 +01:00
assert ( cValue - > isInconclusive ( ) | | value . isInconclusive ( ) | | lValue . isLifetimeValue ( ) ) ;
if ( ! lValue . isLifetimeValue ( ) )
continue ;
2021-11-13 07:45:29 +01:00
if ( emptyAdvance )
outOfBoundsError ( emptyAdvance ,
lValue . tokvalue - > expressionString ( ) ,
cValue ,
2022-07-10 10:57:29 +02:00
advanceIndex ? advanceIndex - > expressionString ( ) : emptyString ,
2021-11-13 07:45:29 +01:00
nullptr ) ;
else
outOfBoundsError ( tok , lValue . tokvalue - > expressionString ( ) , cValue , tok - > expressionString ( ) , & value ) ;
2020-08-26 21:05:17 +02:00
} 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 ) {
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " derefInvalidIterator " , " Dereference of an invalid iterator " , CWE825 , Certainty : : normal ) ;
reportError ( tok , Severity : : warning , " derefInvalidIteratorRedundantCheck " , errmsgcond , CWE825 , Certainty : : normal ) ;
2020-08-17 23:36:45 +02:00
return ;
}
if ( ! mSettings - > isEnabled ( value , inconclusive ) )
return ;
const ErrorPath errorPath = getErrorPath ( tok , value , " Dereference of an invalid iterator " ) ;
if ( value - > condition ) {
2021-02-24 22:00:06 +01:00
reportError ( errorPath , Severity : : warning , " derefInvalidIteratorRedundantCheck " , errmsgcond , CWE825 , ( inconclusive | | value - > isInconclusive ( ) ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2020-08-17 23:36:45 +02:00
} 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 ,
2021-02-24 22:00:06 +01:00
CWE825 , ( inconclusive | | value - > isInconclusive ( ) ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2020-08-17 23:36:45 +02:00
}
}
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 "
2021-02-24 22:00:06 +01:00
" Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after. " , CWE825 , Certainty : : normal ) ;
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 : : 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 " ) ;
2021-02-24 22:00:06 +01:00
reportError ( errorPath , value ? ( value - > errorSeverity ( ) ? Severity : : error : Severity : : warning ) : Severity : : style , " reademptycontainer " , " $symbol: " + varname + " \n " + errmsg , CWE398 , ! value ? Certainty : : inconclusive : Certainty : : normal ) ;
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 " ,
2021-02-24 22:00:06 +01:00
" Consider using " + algoName + " algorithm instead of a raw loop. " , CWE398 , Certainty : : normal ) ;
2018-09-19 18:58:59 +02:00
}
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : style ) )
2018-09-19 18:58:59 +02:00
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 ( ) ;
2022-06-08 16:58:57 +02:00
if ( loopVar - > varId ( ) = = 0 )
2018-09-19 18:58:59 +02:00
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 " ,
2021-02-24 22:00:06 +01:00
msg , CWE398 , Certainty : : normal ) ;
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
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : style ) )
2020-08-22 09:16:26 +02:00
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 ;
2022-07-10 10:57:29 +02:00
knownEmptyContainerError ( contTok , emptyString ) ;
2020-09-02 07:11:23 +02:00
} 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 ;
2021-09-04 19:06:48 +02:00
return Token : : Match ( tok , " std :: lock_guard|unique_lock|scoped_lock|shared_lock " ) ;
2020-06-16 02:40:54 +02:00
}
2020-06-18 00:06:06 +02:00
static bool isLocalMutex ( const Variable * var , const Scope * scope )
{
if ( ! var )
return false ;
2021-09-04 19:06:48 +02:00
if ( isLockGuard ( var ) )
return false ;
2020-06-18 00:06:06 +02:00
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 " ,
2021-02-24 22:00:06 +01: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 , Certainty : : normal ) ;
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 " ,
2021-02-24 22:00:06 +01:00
" The lock is ineffective because the mutex is locked at the same scope as the mutex itself. " , CWE667 , Certainty : : normal ) ;
2020-06-16 02:40:54 +02:00
}
void CheckStl : : checkMutexes ( )
{
2021-12-20 07:29:45 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
return ;
2020-06-16 02:40:54 +02:00
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 ) ;
}
}
}
}