2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 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"
# 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>
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>
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:
2023-10-08 09:10:17 +02:00
static const CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
static const CWE CWE597 ( 597U ) ; // Use of Wrong Operator in String Comparison
static const CWE CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
static const CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const CWE CWE667 ( 667U ) ; // Improper Locking
static const CWE CWE704 ( 704U ) ; // Incorrect Type Conversion or Cast
static const CWE CWE762 ( 762U ) ; // Mismatched Memory Management Routines
static const CWE CWE786 ( 786U ) ; // Access of Memory Location Before Start of Buffer
static const CWE CWE788 ( 788U ) ; // Access of Memory Location After End of Buffer
static const CWE CWE825 ( 825U ) ; // Expired Pointer Dereference
static const CWE CWE833 ( 833U ) ; // Deadlock
static const CWE CWE834 ( 834U ) ; // Excessive Iteration
2016-01-25 20:01:48 +01:00
2023-09-11 20:40:39 +02:00
static bool isElementAccessYield ( Library : : Container : : Yield yield )
2020-09-07 06:46:31 +02:00
{
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% ( " ) ) {
2022-10-02 07:12:40 +02:00
const Library : : Container : : Action action = container - > getAction ( parent - > strAt ( 1 ) ) ;
2022-03-27 10:02:30 +02:00
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% ( " ) ) {
2022-10-02 07:12:40 +02:00
const Library : : Container : : Yield yield = container - > getYield ( parent - > strAt ( 1 ) ) ;
2021-11-13 07:45:29 +01:00
if ( isElementAccessYield ( yield ) )
return true ;
}
return false ;
}
2023-02-24 01:10:19 +01:00
static bool containerPopsElement ( const Library : : Container * container , const Token * parent )
{
if ( Token : : Match ( parent , " . %name% ( " ) ) {
const Library : : Container : : Action action = container - > getAction ( parent - > strAt ( 1 ) ) ;
if ( contains ( { Library : : Container : : Action : : POP } , action ) )
return true ;
}
return false ;
}
2021-11-13 07:45:29 +01:00
static const Token * getContainerIndex ( const Library : : Container * container , const Token * parent )
{
if ( Token : : Match ( parent , " . %name% ( " ) ) {
2022-10-02 07:12:40 +02:00
const 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% ( " ) ) {
2022-10-02 07:12:40 +02:00
const Library : : Container : : Yield yield = container - > getYield ( tok - > strAt ( - 1 ) ) ;
2021-11-13 07:45:29 +01:00
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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::outOfBounds " ) ;
2018-08-11 11:40:48 +02:00
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 ;
2023-02-24 01:10:19 +01:00
if ( value . intvalue = = 0 & & ( indexTok | |
( containerYieldsElement ( container , parent ) & & ! containerAppendsElement ( container , parent ) ) | |
containerPopsElement ( 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 ( ) )
2022-09-30 14:43:21 +02:00
sizeTok = sizeTok - > astOperand2 ( ) ? sizeTok - > astOperand2 ( ) : sizeTok - > astOperand1 ( ) ;
2022-01-13 08:03:24 +01:00
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 ( ) )
2023-08-17 16:46:32 +02:00
return " at position " + std : : to_string ( indexValue . intvalue ) + " from the beginning " ;
2020-08-26 21:05:17 +02:00
if ( indexValue . isIteratorEndValue ( ) )
2023-08-17 16:46:32 +02:00
return " at position " + std : : to_string ( - indexValue . intvalue ) + " from the end " ;
std : : string indexString = std : : to_string ( indexValue . intvalue ) ;
2021-11-13 07:45:29 +01:00
if ( indexValue . isSymbolicValue ( ) ) {
indexString = containerName + " .size() " ;
if ( indexValue . intvalue ! = 0 )
2023-08-17 16:46:32 +02:00
indexString + = " + " + std : : to_string ( indexValue . intvalue ) ;
2021-11-13 07:45:29 +01:00
}
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 )
2023-08-17 16:46:32 +02:00
errmsg = ValueFlow : : eitherTheConditionIsRedundant ( containerSize - > condition ) + " or $symbol size can be " + std : : to_string ( containerSize - > intvalue ) + " . Expression ' " + expression + " ' cause access out of bounds. " ;
2019-03-29 11:13:25 +01:00
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
2023-08-17 16:46:32 +02:00
errmsg = " Out of bounds access in ' " + expression + " ', if '$symbol' size is " + std : : to_string ( 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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::outOfBoundsIndexExpression " ) ;
2018-11-28 19:27:28 +01:00
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++ " ) ;
2023-06-20 18:43:21 +02:00
if ( ! end | | end - > argCount ( ) > 0 | | ! incOperator )
2016-08-14 10:49:48 +02:00
return false ;
2023-06-20 18:43:21 +02:00
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 ;
}
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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " 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
}
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 ) ) {
2023-03-17 13:51:55 +01:00
return ! astIsContainerOwned ( tok1 ) | | ! isTemporary ( true , tok1 , & library ) ;
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
{
2023-01-28 10:20:47 +01:00
std : : vector < ValueFlow : : Value > values = ValueFlow : : getLifetimeObjValues ( tok , false , path ) ;
2022-12-30 15:13:47 +01:00
auto it = std : : find_if ( values . cbegin ( ) , values . cend ( ) , [ ] ( const ValueFlow : : Value & v ) {
2021-11-23 22:50:32 +01:00
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%|- " ) ) {
2023-11-17 17:12:38 +01:00
if ( astIsIntegral ( tok1 , true ) | | astIsIntegral ( tok2 , true ) | |
astIsFloat ( tok1 , true ) | | astIsFloat ( tok2 , true ) )
2021-04-19 09:17:02 +02:00
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 ;
}
2023-11-16 15:49:41 +01:00
namespace {
struct ArgIteratorInfo {
const Token * tok ;
const Library : : ArgumentChecks : : IteratorInfo * info ;
} ;
}
2019-12-25 09:32:50 +01:00
2015-06-10 21:14:17 +02:00
void CheckStl : : mismatchingContainers ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " 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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::misMatchingContainerIterator " ) ;
2020-06-06 17:54:56 +02:00
// 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 ;
2022-10-02 07:12:40 +02:00
const Library : : Container : : Action action = c - > getAction ( tok - > strAt ( 2 ) ) ;
2020-06-06 17:54:56 +02:00
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 ) {
2022-10-02 07:12:40 +02:00
const 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
}
2023-11-16 15:49:41 +01:00
namespace {
struct InvalidContainerAnalyzer {
struct Info {
struct Reference {
const Token * tok ;
ErrorPath errorPath ;
const Token * ftok ;
} ;
std : : unordered_map < int , Reference > expressions ;
void add ( const std : : vector < Reference > & refs ) {
for ( const Reference & r : refs ) {
add ( r ) ;
}
}
void add ( const Reference & r ) {
if ( ! r . tok )
return ;
expressions . insert ( std : : make_pair ( r . tok - > exprId ( ) , r ) ) ;
2021-01-11 18:47:38 +01:00
}
2023-11-16 15:49:41 +01:00
std : : vector < Reference > invalidTokens ( ) const {
std : : vector < Reference > result ;
std : : transform ( expressions . cbegin ( ) , expressions . cend ( ) , std : : back_inserter ( result ) , SelectMapValues { } ) ;
2021-01-11 18:47:38 +01:00
return result ;
2023-11-16 15:49:41 +01:00
}
} ;
std : : unordered_map < const Function * , Info > invalidMethods ;
std : : vector < Info : : Reference > invalidatesContainer ( const Token * tok ) const {
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 . cbegin ( ) , refs . cend ( ) , 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 ;
2021-01-11 18:47:38 +01:00
return true ;
2023-11-16 15:49:41 +01:00
} ) ;
std : : vector < const Token * > args = getArguments ( tok ) ;
for ( Info : : Reference & r : result ) {
r . errorPath . push_front ( epi ) ;
r . ftok = tok ;
const Variable * var = r . tok - > variable ( ) ;
if ( ! var )
continue ;
if ( var - > isArgument ( ) ) {
const int n = getArgumentPos ( var , f ) ;
const Token * tok2 = nullptr ;
if ( n > = 0 & & n < args . size ( ) )
tok2 = args [ n ] ;
r . tok = tok2 ;
}
2021-01-11 18:47:38 +01:00
}
}
2023-11-16 15:49:41 +01:00
} else if ( astIsContainer ( tok ) ) {
const Token * ftok = getInvalidMethod ( tok ) ;
if ( ftok ) {
ErrorPath ep ;
ep . emplace_front ( ftok ,
" After calling ' " + ftok - > expressionString ( ) +
" ', iterators or references to the container's data may be invalid . " ) ;
result . emplace_back ( Info : : Reference { tok , ep , ftok } ) ;
}
2021-01-11 18:47:38 +01:00
}
2023-11-16 15:49:41 +01:00
return result ;
2021-01-11 18:47:38 +01:00
}
2023-11-16 15:49:41 +01:00
void analyze ( const SymbolDatabase * symboldatabase ) {
for ( const Scope * scope : symboldatabase - > functionScopes ) {
const Function * f = scope - > function ;
if ( ! f )
2021-01-11 18:47:38 +01:00
continue ;
2023-11-16 15:49:41 +01:00
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-01-11 18:47:38 +01:00
}
}
2023-11-16 15:49:41 +01:00
} ;
}
2021-01-11 18:47:38 +01:00
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 )
2022-12-30 15:13:47 +01:00
errorPath - > insert ( errorPath - > end ( ) , val . errorPath . cbegin ( ) , val . errorPath . cend ( ) ) ;
2022-04-30 09:36:28 +02:00
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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::invalidContainer " ) ;
2019-07-18 10:56:44 +02:00
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 ( ) ) {
2022-10-02 07:12:40 +02:00
const bool isRHS = astIsRHS ( assignExpr ) ;
2021-10-15 11:54:29 +02:00
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 ;
2023-01-28 10:20:47 +01:00
const Variable * var = ValueFlow : : getLifetimeVariable ( info . tok , ep , & addressOf ) ;
2021-10-15 11:54:29 +02:00
// 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 ;
2022-12-30 15:13:47 +01:00
errorPath . insert ( errorPath . end ( ) , info . errorPath . cbegin ( ) , info . errorPath . cend ( ) ) ;
errorPath . insert ( errorPath . end ( ) , r . errorPath . cbegin ( ) , r . errorPath . cend ( ) ) ;
2021-10-15 11:54:29 +02:00
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 )
2022-12-30 15:13:47 +01:00
errorPath . insert ( errorPath . begin ( ) , val - > errorPath . cbegin ( ) , val - > errorPath . cend ( ) ) ;
2019-07-18 10:56:44 +02:00
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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " 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 ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::negativeIndex " ) ;
2017-08-22 11:04:02 +02:00
// 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 ( ) ) {
2023-03-12 15:49:37 +01:00
if ( ! Token : : Match ( tok , " %var% [ " ) | | ! tok - > next ( ) - > astOperand2 ( ) )
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. " ;
2023-06-20 10:55:14 +02:00
const auto severity = index . errorSeverity ( ) & & index . isKnown ( ) ? Severity : : error : Severity : : warning ;
const auto certainty = index . isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ;
reportError ( errorPath , severity , " negativeContainerIndex " , errmsg . str ( ) , CWE786 , certainty ) ;
2017-08-22 11:04:02 +02:00
}
2009-02-11 06:08:29 +01:00
void CheckStl : : erase ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " 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
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::stlBoundaries " ) ;
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 ;
2022-08-20 12:14:55 +02:00
const Library : : Container * container = mSettings - > library . detectIterator ( var - > typeStartToken ( ) ) ;
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
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::if_find " ) ; // warning,performance
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 ( ) ;
2023-10-17 18:32:07 +02:00
container = mSettings - > library . detectContainer ( tok2 ) ; // inner container
2015-01-04 12:43:31 +01:00
} else
container = nullptr ;
2012-01-02 22:52:50 +01:00
}
}
2023-10-20 21:22:06 +02:00
Library : : Container : : Action action { } ;
if ( container & &
( ( action = container - > getAction ( funcTok - > str ( ) ) ) = = Library : : Container : : Action : : FIND | | action = = Library : : Container : : Action : : FIND_CONST ) ) {
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 ( ) } ;
}
2023-05-03 10:02:16 +02:00
static const Token * skipLocalVars ( const Token * const tok )
2019-05-02 11:04:23 +02:00
{
if ( ! tok )
return tok ;
if ( Token : : simpleMatch ( tok , " { " ) )
return skipLocalVars ( tok - > next ( ) ) ;
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 ;
2023-05-03 10:02:16 +02:00
if ( var - > scope ( ) ! = tok - > scope ( ) )
2019-05-02 11:04:23 +02:00
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 ;
2023-06-20 18:43:21 +02:00
return ikeyTok ;
2019-05-02 11:04:23 +02:00
}
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::checkFindInsert " ) ; // performance
2019-05-02 11:04:23 +02:00
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::size " ) ; // performance,c++03
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::redundantCondition " ) ; // style
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::missingComparison " ) ; // warning
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
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::string_c_str " ) ;
2012-02-25 12:43:27 +01:00
// Find all functions that take std::string as argument
2023-05-28 01:11:11 +02:00
struct StrArg {
2023-10-11 14:08:17 +02:00
nonneg int n ;
std : : string argtype ;
2023-05-28 01:11:11 +02:00
} ;
std : : multimap < const Function * , StrArg > 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 ) {
2023-05-28 01:11:11 +02:00
nonneg 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 + + ;
2023-05-28 01:11:11 +02:00
if ( ( var . isStlStringType ( ) | | var . isStlStringViewType ( ) ) & & ( ! var . isReference ( ) | | var . isConst ( ) ) )
c_strFuncParam . emplace ( & func , StrArg { numpar , var . getTypeName ( ) } ) ;
2012-02-25 12:43:27 +01:00
}
}
}
}
2022-08-22 14:06:10 +02:00
auto isString = [ ] ( const Token * str ) - > bool {
2022-08-23 20:26:36 +02:00
while ( Token : : Match ( str , " ::|. " ) )
str = str - > astOperand2 ( ) ;
2022-08-22 14:06:10 +02:00
if ( Token : : Match ( str , " (|[ " ) & & ! ( str - > valueType ( ) & & str - > valueType ( ) - > type = = ValueType : : ITERATOR ) )
str = str - > previous ( ) ;
return str & & ( ( str - > variable ( ) & & str - > variable ( ) - > isStlStringType ( ) ) | | // variable
( str - > function ( ) & & isStlStringType ( str - > function ( ) - > retDef ) ) | | // function returning string
( str - > valueType ( ) & & str - > valueType ( ) - > type = = ValueType : : ITERATOR & & isStlStringType ( str - > valueType ( ) - > containerTypeToken ) ) ) ; // iterator pointing to string
} ;
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 ) ;
2023-02-03 14:10:27 +01:00
} else if ( printPerformance & & tok - > tokAt ( 1 ) - > astOperand2 ( ) & & Token : : Match ( tok - > tokAt ( 1 ) - > astOperand2 ( ) - > tokAt ( - 3 ) , " %var% . c_str|data ( ) ; " )) {
const Token * vartok = tok - > tokAt ( 1 ) - > astOperand2 ( ) - > tokAt ( - 3 ) ;
2023-05-31 16:51:37 +02:00
if ( ( tok - > variable ( ) - > isStlStringType ( ) | | tok - > variable ( ) - > isStlStringViewType ( ) ) & & vartok - > variable ( ) & & vartok - > variable ( ) - > isStlStringType ( ) )
string_c_strAssignment ( tok , tok - > variable ( ) - > getTypeName ( ) ) ;
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 ) {
2023-05-28 01:11:11 +02:00
const auto range = c_strFuncParam . equal_range ( tok - > function ( ) ) ;
for ( std : : multimap < const Function * , StrArg > : : const_iterator i = range . first ; i ! = range . second ; + + i ) {
if ( i - > second . n = = 0 )
2012-02-25 12:43:27 +01:00
continue ;
const Token * tok2 = tok - > tokAt ( 2 ) ;
2019-07-15 14:05:23 +02:00
int j ;
2023-05-28 01:11:11 +02:00
for ( j = 0 ; tok2 & & j < i - > second . n - 1 ; j + + )
2012-02-25 12:43:27 +01:00
tok2 = tok2 - > nextArgument ( ) ;
if ( tok2 )
tok2 = tok2 - > nextArgument ( ) ;
else
break ;
2023-05-28 01:11:11 +02:00
if ( ! tok2 & & j = = i - > second . n - 1 )
2012-02-25 12:43:27 +01:00
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 ( ) " ) ) {
2022-08-22 14:06:10 +02:00
if ( isString ( tok2 - > tokAt ( - 4 ) - > astOperand1 ( ) ) ) {
2023-05-28 01:11:11 +02:00
string_c_strParam ( tok , i - > second . n , i - > second . argtype ) ;
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 ) )
2023-05-28 01:11:11 +02:00
string_c_strParam ( tok , i - > second . n , i - > second . argtype ) ;
2014-01-30 22:09:24 +01:00
}
2012-11-28 08:48:48 +01:00
}
2012-02-25 12:43:27 +01:00
}
2023-07-07 15:54:07 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% (|{ %var% . c_str|data ( ) !!, " ) & &
2023-05-31 16:51:37 +02:00
tok - > variable ( ) & & ( tok - > variable ( ) - > isStlStringType ( ) | | tok - > variable ( ) - > isStlStringViewType ( ) ) & &
tok - > tokAt ( 2 ) - > variable ( ) & & tok - > tokAt ( 2 ) - > variable ( ) - > isStlStringType ( ) ) {
string_c_strConstructor ( tok , tok - > variable ( ) - > getTypeName ( ) ) ;
2022-07-10 11:38:01 +02:00
} 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 ) ;
2022-08-22 14:06:10 +02:00
} else if ( printPerformance & & Token : : simpleMatch ( tok , " << " ) & & tok - > astOperand2 ( ) & & Token : : Match ( tok - > astOperand2 ( ) - > astOperand1 ( ) , " . c_str|data ( ) " ) ) {
2022-08-20 20:52:10 +02:00
const Token * str = tok - > astOperand2 ( ) - > astOperand1 ( ) - > astOperand1 ( ) ;
2022-08-22 14:06:10 +02:00
if ( isString ( str ) ) {
2022-08-20 20:52:10 +02:00
const Token * strm = tok ;
while ( Token : : simpleMatch ( strm , " << " ) )
strm = strm - > astOperand1 ( ) ;
if ( strm & & strm - > variable ( ) & & strm - > variable ( ) - > isStlType ( ) )
string_c_strStream ( 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 ;
2023-06-20 18:43:21 +02:00
}
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 ) ) ;
}
2023-07-10 15:27:33 +02:00
ptrOrRef = refToNonLocal | | ( tok2 - > variable ( ) & & ( tok2 - > variable ( ) - > isPointer ( ) | | tok2 - > variable ( ) - > isSmartPointer ( ) ) ) ;
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
}
2023-05-28 01:11:11 +02:00
void CheckStl : : string_c_strParam ( const Token * tok , nonneg int number , const std : : string & argtype )
2012-02-25 12:43:27 +01:00
{
std : : ostringstream oss ;
2023-05-28 01:11:11 +02:00
oss < < " Passing the result of c_str() to a function that takes " < < argtype < < " as argument no. " < < number < < " is slow and redundant. \n "
" The conversion from const char* as returned by c_str() to " < < argtype < < " creates an unnecessary string copy or length calculation. 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
}
2023-05-31 16:51:37 +02:00
void CheckStl : : string_c_strConstructor ( const Token * tok , const std : : string & argtype )
2022-07-10 11:38:01 +02:00
{
2023-05-31 16:51:37 +02:00
std : : string msg = " Constructing a " + argtype + " from the result of c_str() is slow and redundant. \n "
" Constructing a " + argtype + " from const char* requires a call to strlen(). Solve that by directly passing the string. " ;
2022-07-10 11:38:01 +02:00
reportError ( tok , Severity : : performance , " stlcstrConstructor " , msg , CWE704 , Certainty : : normal ) ;
}
2023-05-31 16:51:37 +02:00
void CheckStl : : string_c_strAssignment ( const Token * tok , const std : : string & argtype )
2022-07-10 11:38:01 +02:00
{
2023-05-31 16:51:37 +02:00
std : : string msg = " Assigning the result of c_str() to a " + argtype + " is slow and redundant. \n "
" Assigning a const char* to a " + argtype + " requires a call to strlen(). Solve that by directly assigning the string. " ;
2022-07-10 11:38:01 +02:00
reportError ( tok , Severity : : performance , " stlcstrAssignment " , msg , CWE704 , Certainty : : normal ) ;
}
void CheckStl : : string_c_strConcat ( const Token * tok )
{
2022-08-20 20:52:10 +02:00
std : : string msg = " Concatenating the result of c_str() and a std::string is slow and redundant. \n "
" Concatenating a const char* with a std::string requires a call to strlen(). Solve that by directly concatenating the strings. " ;
2022-07-10 11:38:01 +02:00
reportError ( tok , Severity : : performance , " stlcstrConcat " , msg , CWE704 , Certainty : : normal ) ;
}
2022-08-20 20:52:10 +02:00
void CheckStl : : string_c_strStream ( const Token * tok )
{
std : : string msg = " Passing the result of c_str() to a stream is slow and redundant. \n "
" Passing a const char* to a stream requires a call to strlen(). Solve that by directly passing the string. " ;
reportError ( tok , Severity : : performance , " stlcstrStream " , 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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::uselessCalls " ) ; // performance,warning
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::checkDereferenceInvalidIterator " ) ; // warning
2013-04-05 06:14:59 +02:00
// 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
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::checkDereferenceInvalidIterator2 " ) ;
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 ;
2022-12-30 15:13:47 +01:00
std : : copy_if ( tok - > values ( ) . cbegin ( ) , tok - > values ( ) . cend ( ) , std : : back_inserter ( contValues ) , [ & ] ( const ValueFlow : : Value & value ) {
2020-08-26 21:05:17 +02:00
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 {
2022-12-30 15:13:47 +01:00
auto it = std : : find_if ( contValues . cbegin ( ) , contValues . cend ( ) , [ & ] ( 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 {
2023-10-18 10:06:17 +02:00
std : : string errmsg = std : : string ( value - > isKnown ( ) ? " Dereference " : " Possible dereference " ) + " of an invalid iterator " ;
2020-08-17 23:36:45 +02:00
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-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 ;
}
2023-03-04 11:58:12 +01:00
static const Token * singleAssignInScope ( const Token * start , nonneg int varid , bool & input , const Settings * settings )
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 ) ;
2023-03-04 11:58:12 +01:00
if ( isVariableChanged ( assignTok - > next ( ) , endStatement , assignTok - > astOperand1 ( ) - > varId ( ) , /*globalvar*/ false , settings , /*cpp*/ true ) )
2018-09-19 18:58:59 +02:00
return nullptr ;
2023-03-04 11:58:12 +01:00
if ( isVariableChanged ( assignTok - > next ( ) , endStatement , varid , /*globalvar*/ false , settings , /*cpp*/ true ) )
2018-09-19 18:58:59 +02:00
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 ;
}
2023-03-04 11:58:12 +01:00
static const Token * singleMemberCallInScope ( const Token * start , nonneg int varid , bool & input , const Settings * settings )
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 ) ;
2023-03-04 11:58:12 +01:00
if ( isVariableChanged ( dotTok - > next ( ) , endStatement , dotTok - > astOperand1 ( ) - > varId ( ) , /*globalvar*/ false , settings , /*cpp*/ true ) )
2018-09-19 18:58:59 +02:00
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 ;
}
2023-03-04 11:58:12 +01:00
static const Token * singleConditionalInScope ( const Token * start , nonneg int varid , const Settings * settings )
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 ;
2023-03-04 11:58:12 +01:00
if ( isVariableChanged ( start , bodyTok , varid , /*globalvar*/ false , settings , /*cpp*/ true ) )
2018-09-19 18:58:59 +02:00
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 ;
}
2023-03-09 17:06:27 +01:00
namespace {
struct LoopAnalyzer {
const Token * bodyTok = nullptr ;
const Token * loopVar = nullptr ;
const Settings * settings = nullptr ;
2023-10-15 14:51:12 +02:00
std : : set < nonneg int > varsChanged ;
2023-03-09 17:06:27 +01:00
explicit LoopAnalyzer ( const Token * tok , const Settings * psettings )
: bodyTok ( tok - > next ( ) - > link ( ) - > next ( ) ) , settings ( psettings )
{
const Token * splitTok = tok - > next ( ) - > astOperand2 ( ) ;
if ( Token : : simpleMatch ( splitTok , " : " ) & & splitTok - > previous ( ) - > varId ( ) ! = 0 ) {
loopVar = splitTok - > previous ( ) ;
}
if ( valid ( ) ) {
findChangedVariables ( ) ;
}
}
bool isLoopVarChanged ( ) const {
return varsChanged . count ( loopVar - > varId ( ) ) > 0 ;
}
bool isModified ( const Token * tok ) const
{
if ( tok - > variable ( ) & & tok - > variable ( ) - > isConst ( ) )
return false ;
int n = 1 + ( astIsPointer ( tok ) ? 1 : 0 ) ;
for ( int i = 0 ; i < n ; i + + ) {
bool inconclusive = false ;
if ( isVariableChangedByFunctionCall ( tok , i , settings , & inconclusive ) )
return true ;
if ( inconclusive )
return true ;
if ( isVariableChanged ( tok , i , settings , true ) )
return true ;
}
return false ;
}
template < class Predicate , class F >
void findTokens ( Predicate pred , F f ) const
{
for ( const Token * tok = bodyTok ; precedes ( tok , bodyTok - > link ( ) ) ; tok = tok - > next ( ) ) {
if ( pred ( tok ) )
f ( tok ) ;
}
}
template < class Predicate >
const Token * findToken ( Predicate pred ) const
{
for ( const Token * tok = bodyTok ; precedes ( tok , bodyTok - > link ( ) ) ; tok = tok - > next ( ) ) {
if ( pred ( tok ) )
return tok ;
}
return nullptr ;
}
bool hasGotoOrBreak ( ) const
{
return findToken ( [ ] ( const Token * tok ) {
return Token : : Match ( tok , " goto|break " ) ;
} ) ;
}
bool valid ( ) const {
return bodyTok & & loopVar ;
}
std : : string findAlgo ( ) const
{
if ( ! valid ( ) )
return " " ;
bool loopVarChanged = isLoopVarChanged ( ) ;
if ( ! loopVarChanged & & varsChanged . empty ( ) ) {
if ( hasGotoOrBreak ( ) )
return " " ;
bool alwaysTrue = true ;
bool alwaysFalse = true ;
auto hasReturn = [ ] ( const Token * tok ) {
return Token : : simpleMatch ( tok , " return " ) ;
} ;
findTokens ( hasReturn , [ & ] ( const Token * tok ) {
const Token * returnTok = tok - > astOperand1 ( ) ;
if ( ! returnTok | | ! returnTok - > hasKnownIntValue ( ) | | ! astIsBool ( returnTok ) ) {
alwaysTrue = false ;
alwaysFalse = false ;
return ;
}
( returnTok - > values ( ) . front ( ) . intvalue ? alwaysTrue : alwaysFalse ) & = true ;
( returnTok - > values ( ) . front ( ) . intvalue ? alwaysFalse : alwaysTrue ) & = false ;
} ) ;
if ( alwaysTrue = = alwaysFalse )
return " " ;
if ( alwaysTrue )
return " std::any_of " ;
2023-06-20 18:43:21 +02:00
return " std::all_of or std::none_of " ;
2023-03-09 17:06:27 +01:00
}
return " " ;
}
bool isLocalVar ( const Variable * var ) const
{
if ( ! var )
return false ;
if ( var - > isPointer ( ) | | var - > isReference ( ) )
return false ;
if ( var - > declarationId ( ) = = loopVar - > varId ( ) )
return false ;
const Scope * scope = var - > scope ( ) ;
2023-03-13 15:44:34 +01:00
return scope & & scope - > isNestedIn ( bodyTok - > scope ( ) ) ;
2023-03-09 17:06:27 +01:00
}
private :
void findChangedVariables ( )
{
std : : set < nonneg int > vars ;
for ( const Token * tok = bodyTok ; precedes ( tok , bodyTok - > link ( ) ) ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) = = 0 )
continue ;
if ( vars . count ( tok - > varId ( ) ) > 0 )
continue ;
if ( isLocalVar ( tok - > variable ( ) ) ) {
vars . insert ( tok - > varId ( ) ) ;
continue ;
}
if ( ! isModified ( tok ) )
continue ;
varsChanged . insert ( tok - > varId ( ) ) ;
vars . insert ( tok - > varId ( ) ) ;
}
}
} ;
} // namespace
2018-09-19 18:58:59 +02:00
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 ;
2022-10-16 13:46:26 +02:00
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::useStlAlgorithm " ) ; // style
2022-10-16 13:46:26 +02:00
auto checkAssignee = [ ] ( const Token * tok ) {
if ( astIsBool ( tok ) ) // std::accumulate is not a good fit for bool values, std::all/any/none_of return early
return false ;
return ! astIsContainer ( tok ) ; // don't warn for containers, where overloaded operators can be costly
} ;
2022-12-18 16:52:04 +01:00
auto isConditionWithoutSideEffects = [ this ] ( const Token * tok ) - > bool {
if ( ! Token : : simpleMatch ( tok , " { " ) | | ! Token : : simpleMatch ( tok - > previous ( ) , " ) " ) )
return false ;
return isConstExpression ( tok - > previous ( ) - > link ( ) - > astOperand2 ( ) , mSettings - > library , true ) ;
} ;
2018-09-19 18:58:59 +02:00
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 ;
2023-03-09 17:06:27 +01:00
LoopAnalyzer a { tok , mSettings } ;
std : : string algoName = a . findAlgo ( ) ;
if ( ! algoName . empty ( ) ) {
useStlAlgorithmError ( tok , algoName ) ;
continue ;
}
2018-09-19 18:58:59 +02:00
const Token * bodyTok = tok - > next ( ) - > link ( ) - > next ( ) ;
const Token * splitTok = tok - > next ( ) - > astOperand2 ( ) ;
2022-10-16 13:46:26 +02:00
const Token * loopVar { } ;
bool isIteratorLoop = false ;
if ( Token : : simpleMatch ( splitTok , " : " ) ) {
loopVar = splitTok - > previous ( ) ;
if ( loopVar - > varId ( ) = = 0 )
continue ;
2023-10-12 10:06:52 +02:00
if ( Token : : simpleMatch ( splitTok - > astOperand2 ( ) , " { " ) )
continue ;
2022-10-16 13:46:26 +02:00
}
else { // iterator-based loop?
const Token * initTok = getInitTok ( tok ) ;
const Token * condTok = getCondTok ( tok ) ;
const Token * stepTok = getStepTok ( tok ) ;
if ( ! initTok | | ! condTok | | ! stepTok )
continue ;
loopVar = Token : : Match ( condTok , " %comp% " ) ? condTok - > astOperand1 ( ) : nullptr ;
if ( ! Token : : Match ( loopVar , " %var% " ) | | ! loopVar - > valueType ( ) | | loopVar - > valueType ( ) - > type ! = ValueType : : Type : : ITERATOR )
continue ;
if ( ! Token : : simpleMatch ( initTok , " = " ) | | ! Token : : Match ( initTok - > astOperand1 ( ) , " %varid% " , loopVar - > varId ( ) ) )
continue ;
if ( ! stepTok - > isIncDecOp ( ) )
continue ;
isIteratorLoop = true ;
}
2018-09-19 18:58:59 +02:00
// Check for single assignment
bool useLoopVarInAssign ;
2023-03-04 11:58:12 +01:00
const Token * assignTok = singleAssignInScope ( bodyTok , loopVar - > varId ( ) , useLoopVarInAssign , mSettings ) ;
2018-09-19 18:58:59 +02:00
if ( assignTok ) {
2022-10-16 13:46:26 +02:00
if ( ! checkAssignee ( assignTok - > astOperand1 ( ) ) )
continue ;
2022-10-02 07:12:40 +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 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 ;
2023-03-04 11:58:12 +01:00
const Token * memberAccessTok = singleMemberCallInScope ( bodyTok , loopVar - > varId ( ) , useLoopVarInMemCall , mSettings ) ;
2022-10-16 13:46:26 +02:00
if ( memberAccessTok & & ! isIteratorLoop ) {
2018-09-19 18:58:59 +02:00
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
2023-03-04 11:58:12 +01:00
const Token * condBodyTok = singleConditionalInScope ( bodyTok , loopVar - > varId ( ) , mSettings ) ;
2018-09-19 18:58:59 +02:00
if ( condBodyTok ) {
// Check for single assign
2023-03-04 11:58:12 +01:00
assignTok = singleAssignInScope ( condBodyTok , loopVar - > varId ( ) , useLoopVarInAssign , mSettings ) ;
2018-09-19 18:58:59 +02:00
if ( assignTok ) {
2022-10-16 13:46:26 +02:00
if ( ! checkAssignee ( assignTok - > astOperand1 ( ) ) )
continue ;
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 " ;
2022-12-18 16:52:04 +01:00
else if ( assignTok - > str ( ) ! = " = " )
2018-09-19 18:58:59 +02:00
algo = " std::accumulate " ;
2022-12-18 16:52:04 +01:00
else if ( isConditionWithoutSideEffects ( condBodyTok ) )
algo = " std::any_of, std::all_of, std::none_of " ;
else
continue ;
2018-09-19 18:58:59 +02:00
}
useStlAlgorithmError ( assignTok , algo ) ;
continue ;
}
// Check for container call
2023-03-04 11:58:12 +01:00
memberAccessTok = singleMemberCallInScope ( condBodyTok , loopVar - > varId ( ) , useLoopVarInMemCall , mSettings ) ;
2018-09-19 18:58:59 +02:00
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 ;
2023-02-07 21:57:59 +01:00
if ( loopVar2 | |
( isIteratorLoop & & loopVar - > variable ( ) & & precedes ( loopVar - > variable ( ) - > nameToken ( ) , tok ) ) ) // iterator declared outside the loop
2018-09-19 18:58:59 +02:00
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 ;
2023-03-09 17:06:27 +01:00
return std : : any_of ( tok - > values ( ) . begin ( ) , tok - > values ( ) . end ( ) , [ & ] ( const ValueFlow : : Value & v ) {
2020-09-02 07:11:23 +02:00
if ( ! v . isKnown ( ) )
2023-03-09 17:06:27 +01:00
return false ;
2020-09-02 07:11:23 +02:00
if ( ! v . isContainerSizeValue ( ) )
2023-03-09 17:06:27 +01:00
return false ;
2020-09-02 07:11:23 +02:00
if ( v . intvalue ! = 0 )
2023-03-09 17:06:27 +01:00
return false ;
2020-09-02 07:11:23 +02:00
return true ;
2023-03-09 17:06:27 +01:00
} ) ;
2020-09-02 07:11:23 +02:00
}
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::knownEmptyContainer " ) ; // style
2020-08-22 09:16:26 +02:00
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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckStl::checkMutexes " ) ; // warning
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 ) ;
}
}
}
}