2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2016-01-01 14:34:45 +01:00
* Copyright ( C ) 2007 - 2016 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"
2011-03-08 01:49:43 +01:00
# include "symboldatabase.h"
2014-04-12 23:39:52 +02:00
# include "checknullpointer.h"
2015-11-29 10:49:10 +01:00
# include "utils.h"
2010-10-10 10:52:41 +02:00
# include <sstream>
2009-02-10 20:40:21 +01:00
2009-03-19 21:20:08 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckStl instance ;
2009-03-19 21:20:08 +01:00
}
2009-03-19 19:24:13 +01:00
2016-06-05 18:24:06 +02:00
// CWE IDs used:
2016-06-07 19:28:32 +02:00
static const struct CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
static const struct CWE CWE597 ( 597U ) ; // Use of Wrong Operator in String Comparison
static const struct CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const struct CWE CWE704 ( 704U ) ; // Incorrect Type Conversion or Cast
static const struct CWE CWE788 ( 788U ) ; // Access of Memory Location After End of Buffer
static const struct CWE CWE834 ( 834U ) ; // Excessive Iteration
2016-01-25 20:01:48 +01: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 )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidIterator1 " , " Invalid iterator: " + iteratorName , CWE664 , false ) ;
2010-04-17 13:37:04 +02:00
}
2009-03-20 20:09:44 +01:00
void CheckStl : : iteratorsError ( const Token * tok , const std : : string & container1 , const std : : string & container2 )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " iterators " , " Same iterator is used with different containers ' " + container1 + " ' and ' " + container2 + " '. " , CWE664 , false ) ;
2009-03-20 20:09:44 +01:00
}
2009-05-03 07:37:39 +02:00
// Error message used when dereferencing an iterator that has been erased..
2012-10-17 00:29:06 +02:00
void CheckStl : : dereferenceErasedError ( const Token * erased , const Token * deref , const std : : string & itername )
2009-05-03 07:37:39 +02:00
{
2012-10-17 00:29:06 +02:00
if ( erased ) {
2012-10-14 11:16:48 +02:00
std : : list < const Token * > callstack ;
callstack . push_back ( deref ) ;
2012-10-17 00:29:06 +02:00
callstack . push_back ( erased ) ;
2012-10-14 11:16:48 +02:00
reportError ( callstack , Severity : : error , " eraseDereference " ,
" Iterator ' " + itername + " ' used after element has been erased. \n "
" The iterator ' " + itername + " ' is invalid after the element it pointed to has been erased. "
2016-01-25 20:01:48 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , false ) ;
2012-10-14 11:16:48 +02:00
} else {
reportError ( deref , Severity : : error , " eraseDereference " ,
" Invalid iterator ' " + itername + " ' used. \n "
" The iterator ' " + itername + " ' is invalid before being assigned. "
2016-01-25 20:01:48 +01:00
" Dereferencing or comparing it with another iterator is invalid operation. " , CWE664 , false ) ;
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 ;
}
2009-02-10 20:40:21 +01:00
void CheckStl : : iterators ( )
{
2012-08-20 13:57:24 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( unsigned int iteratorId = 1 ; iteratorId < symbolDatabase - > getVariableListSize ( ) ; iteratorId + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( iteratorId ) ;
2009-07-28 20:05:00 +02:00
2012-08-20 13:57:24 +02:00
// Check that its an iterator
2013-09-28 11:50:45 +02:00
if ( ! var | | ! var - > isLocal ( ) | | ! Token : : Match ( var - > typeEndToken ( ) , " iterator|const_iterator|reverse_iterator|const_reverse_iterator|auto " ) )
2012-08-12 13:12:17 +02:00
continue ;
2011-12-27 11:02:43 +01:00
2015-11-13 13:22:47 +01:00
if ( var - > typeEndToken ( ) - > str ( ) = = " auto " ) {
if ( Token : : Match ( var - > typeEndToken ( ) , " auto %name% ; %name% = %var% . %name% ( ) " ) ) {
const Token * containertok = var - > typeEndToken ( ) - > tokAt ( 5 ) ;
2015-11-15 14:40:31 +01:00
if ( ! containertok - > variable ( ) )
continue ;
2015-11-13 13:22:47 +01:00
const Library : : Container * container = _settings - > library . detectContainer ( containertok - > variable ( ) - > typeStartToken ( ) ) ;
if ( ! container )
continue ;
Library : : Container : : Yield yield = container - > getYield ( containertok - > strAt ( 2 ) ) ;
2015-11-20 18:22:15 +01:00
if ( yield ! = Library : : Container : : END_ITERATOR & & yield ! = Library : : Container : : START_ITERATOR & & yield ! = Library : : Container : : ITERATOR )
2015-11-13 13:22:47 +01:00
continue ;
} else
continue ;
}
2014-04-09 10:32:56 +02:00
2013-03-14 17:00:22 +01: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++ " ) ;
if ( ! end | | end - > argCount ( ) > 0 | | ! incOperator )
continue ;
}
2010-12-30 22:36:25 +01:00
// the validIterator flag says if the iterator has a valid value or not
2015-11-08 09:42:28 +01:00
bool validIterator = Token : : Match ( var - > nameToken ( ) - > next ( ) , " [(=:] " ) ;
2012-08-20 13:57:24 +02:00
const Scope * invalidationScope = 0 ;
// The container this iterator can be used with
const Variable * container = 0 ;
const Scope * containerAssignScope = 0 ;
2012-04-09 07:19:39 +02:00
// When "validatingToken" is reached the validIterator is set to true
2011-12-17 11:21:34 +01:00
const Token * validatingToken = 0 ;
2010-12-30 22:36:25 +01:00
2012-10-14 11:16:48 +02:00
const Token * eraseToken = 0 ;
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.
2014-03-22 10:32:24 +01:00
for ( const Token * tok2 = var - > nameToken ( ) ; tok2 & & tok2 ! = var - > scope ( ) - > classEnd ; tok2 = tok2 - > next ( ) ) {
2012-08-20 13:57:24 +02:00
if ( invalidationScope & & tok2 = = invalidationScope - > classEnd )
validIterator = true ; // Assume that the iterator becomes valid again
if ( containerAssignScope & & tok2 = = containerAssignScope - > classEnd )
container = 0 ; // We don't know which containers might be used with the iterator
2011-12-17 11:21:34 +01:00
if ( tok2 = = validatingToken )
validIterator = true ;
2010-12-30 22:36:25 +01:00
// Is iterator compared against different container?
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok2 , " %varid% !=|== %name% . end|rend|cend|crend ( ) " , iteratorId ) & & container & & tok2 - > tokAt ( 2 ) - > varId ( ) ! = container - > declarationId ( ) ) {
2012-08-20 13:57:24 +02:00
iteratorsError ( tok2 , container - > name ( ) , tok2 - > strAt ( 2 ) ) ;
2009-08-01 16:37:24 +02:00
tok2 = tok2 - > tokAt ( 6 ) ;
2009-07-28 20:05:00 +02:00
}
2010-12-30 22:36:25 +01:00
// Is the iterator used in a insert/erase operation?
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok2 , " %name% . insert|erase ( *| %varid% ) | , " , iteratorId)) {
2011-12-17 11:21:34 +01:00
const Token * itTok = tok2 - > tokAt ( 4 ) ;
if ( itTok - > str ( ) = = " * " ) {
2012-08-20 13:57:24 +02:00
if ( tok2 - > strAt ( 2 ) = = " insert " )
2011-12-17 11:21:34 +01:00
continue ;
itTok = itTok - > next ( ) ;
}
2010-12-30 22:36:25 +01:00
// It is bad to insert/erase an invalid iterator
2010-04-17 13:37:04 +02:00
if ( ! validIterator )
2011-12-17 11:21:34 +01:00
invalidIteratorError ( tok2 , itTok - > str ( ) ) ;
2010-04-17 13:37:04 +02:00
2010-12-30 22:36:25 +01:00
// If insert/erase is used on different container then
// report an error
2013-07-20 12:31:04 +02:00
if ( container & & tok2 - > varId ( ) ! = container - > declarationId ( ) ) {
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 ( ) ! = " ) " ) {
2016-08-04 09:35:16 +02:00
if ( par2 - > varId ( ) = = container - > declarationId ( ) )
break ;
if ( par2 - > str ( ) = = " ( " )
par2 = par2 - > link ( ) ;
par2 = par2 - > next ( ) ;
}
if ( par2 - > varId ( ) = = container - > declarationId ( ) )
continue ;
}
2010-04-10 10:22:34 +02:00
// Show error message, mismatching iterator is used.
2012-08-20 13:57:24 +02:00
iteratorsError ( tok2 , container - > name ( ) , tok2 - > str ( ) ) ;
2010-04-10 10:22:34 +02:00
}
2010-12-30 22:36:25 +01:00
// invalidate the iterator if it is erased
2014-04-12 19:44:37 +02:00
else if ( tok2 - > strAt ( 2 ) = = " erase " & & ( tok2 - > strAt ( 4 ) ! = " * " | | ( container & & tok2 - > varId ( ) = = container - > declarationId ( ) ) ) ) {
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
2012-01-14 15:12:03 +01:00
validatingToken = tok2 - > linkAt ( 5 ) ;
2011-12-17 11:21:34 +01:00
tok2 = tok2 - > tokAt ( 5 ) ;
2009-11-10 17:20:20 +01:00
}
2010-12-30 22:36:25 +01:00
// Reassign the iterator
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok2 , " %varid% = %name% . begin|rbegin|cbegin|crbegin|find ( " , iteratorId ) ) {
2012-08-20 13:57:24 +02:00
validatingToken = tok2 - > linkAt ( 5 ) ;
2013-02-05 06:46:26 +01:00
container = tok2 - > tokAt ( 2 ) - > variable ( ) ;
2012-08-20 13:57:24 +02:00
containerAssignScope = tok2 - > scope ( ) ;
// skip ahead
tok2 = tok2 - > tokAt ( 5 ) ;
}
// Reassign the iterator
2015-12-24 14:40:48 +01:00
else if ( Token : : Match ( tok2 , " %varid% = " , iteratorId ) ) {
2010-12-30 22:36:25 +01:00
// Assume that the iterator becomes valid.
2010-12-30 22:41:22 +01:00
// TODO: add checking that checks if the iterator becomes valid or not
2012-08-20 13:57:24 +02:00
validatingToken = Token : : findmatch ( tok2 - > tokAt ( 2 ) , " [;)] " ) ;
2010-12-30 22:36:25 +01:00
// skip ahead
2012-08-20 13:57:24 +02:00
tok2 = tok2 - > tokAt ( 2 ) ;
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 ) ) {
2012-10-14 11:16:48 +02:00
dereferenceErasedError ( eraseToken , tok2 , tok2 - > strAt ( 1 ) ) ;
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 ) ) {
2012-10-14 11:16:48 +02:00
dereferenceErasedError ( eraseToken , tok2 , tok2 - > str ( ) ) ;
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
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok2 , " return|break " ) ) {
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
2014-03-22 10:32:24 +01:00
else if ( tok2 & & 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
2009-10-18 18:42:01 +02:00
// Error message for bad iterator usage..
void CheckStl : : mismatchingContainersError ( const Token * tok )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " mismatchingContainers " , " Iterators of different containers are used together. " , CWE664 , false ) ;
2009-10-18 18:42:01 +02:00
}
2015-06-10 21:14:17 +02:00
namespace {
2015-06-17 22:28:15 +02:00
const std : : set < std : : string > algorithm2 = make_container < std : : set < std : : string > > ( ) // func(begin1, end1
< < " adjacent_find " < < " all_of " < < " any_of " < < " binary_search " < < " copy " < < " copy_if " < < " count " < < " count_if " < < " equal " < < " equal_range "
< < " find " < < " find_if " < < " find_if_not " < < " for_each " < < " generate " < < " is_heap " < < " is_heap_until " < < " is_partitioned "
< < " is_permutation " < < " is_sorted " < < " is_sorted_until " < < " lower_bound " < < " make_heap " < < " max_element " < < " minmax_element "
< < " min_element " < < " mismatch " < < " move " < < " move_backward " < < " next_permutation " < < " none_of " < < " partition " < < " partition_copy "
< < " partition_point " < < " pop_heap " < < " prev_permutation " < < " push_heap " < < " random_shuffle " < < " remove " < < " remove_copy "
< < " remove_copy_if " < < " remove_if " < < " replace " < < " replace_copy " < < " replace_copy_if " < < " replace_if " < < " reverse " < < " reverse_copy "
< < " search_n " < < " shuffle " < < " sort " < < " sort_heap " < < " stable_partition " < < " stable_sort " < < " swap_ranges " < < " transform " < < " unique "
2015-07-21 14:13:26 +02:00
< < " unique_copy " < < " upper_bound " < < " string " < < " wstring " < < " u16string " < < " u32string " ;
2015-06-17 22:28:15 +02:00
const std : : set < std : : string > algorithm22 = make_container < std : : set < std : : string > > ( ) // func(begin1 << end1 << begin2 << end2
< < " find_end " < < " find_first_of " < < " includes " < < " lexicographical_compare " < < " merge " < < " partial_sort_copy "
< < " search " < < " set_difference " < < " set_intersection " < < " set_symmetric_difference " < < " set_union " ;
const std : : set < std : : string > algorithm1x1 = make_container < std : : set < std : : string > > ( ) // func(begin1 << x << end1
< < " inplace_merge " < < " nth_element " < < " partial_sort " < < " rotate " < < " rotate_copy " ;
2011-10-18 21:55:41 +02:00
2015-11-30 22:13:49 +01:00
const std : : string iteratorBeginFuncPattern = " begin|cbegin|rbegin|crbegin " ;
const std : : string iteratorEndFuncPattern = " end|cend|rend|crend " ;
2011-10-18 21:55:41 +02:00
2015-11-30 22:13:49 +01:00
const std : : string pattern1x1_1 = " %name% . " + iteratorBeginFuncPattern + " ( ) , " ;
const std : : string pattern1x1_2 = " %name% . " + iteratorEndFuncPattern + " ( ) ,|) " ;
const std : : string pattern2 = pattern1x1_1 + pattern1x1_2 ;
2015-06-10 21:14:17 +02:00
}
void CheckStl : : mismatchingContainers ( )
{
2010-12-30 22:36:25 +01:00
// Check if different containers are used in various calls of standard functions
2012-11-16 06:50:49 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t ii = 0 ; ii < functions ; + + ii ) {
const Scope * scope = symbolDatabase - > functionScopes [ ii ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , " std :: %type% ( !!) " ) )
continue ;
const Token * arg1 = tok - > tokAt ( 4 ) ;
2009-10-18 18:42:01 +02:00
2012-11-16 06:50:49 +01:00
// TODO: If iterator variables are used instead then there are false negatives.
if ( Token : : Match ( arg1 , pattern2 . c_str ( ) ) & & algorithm2 . find ( tok - > strAt ( 2 ) ) ! = algorithm2 . end ( ) ) {
if ( arg1 - > str ( ) ! = arg1 - > strAt ( 6 ) ) {
mismatchingContainersError ( arg1 ) ;
}
} else if ( algorithm22 . find ( tok - > strAt ( 2 ) ) ! = algorithm22 . end ( ) ) {
if ( Token : : Match ( arg1 , pattern2 . c_str ( ) ) & & arg1 - > str ( ) ! = arg1 - > strAt ( 6 ) )
2012-04-17 12:21:41 +02:00
mismatchingContainersError ( arg1 ) ;
2012-11-16 06:50:49 +01:00
// Find third parameter
const Token * arg3 = arg1 ;
for ( unsigned int i = 0 ; i < 2 & & arg3 ; i + + )
arg3 = arg3 - > nextArgument ( ) ;
if ( Token : : Match ( arg3 , pattern2 . c_str ( ) ) & & arg3 - > str ( ) ! = arg3 - > strAt ( 6 ) )
mismatchingContainersError ( arg3 ) ;
} else if ( Token : : Match ( arg1 , pattern1x1_1 . c_str ( ) ) & & algorithm1x1 . find ( tok - > strAt ( 2 ) ) ! = algorithm1x1 . end ( ) ) {
// Find third parameter
const Token * arg3 = arg1 - > tokAt ( 6 ) - > nextArgument ( ) ;
if ( Token : : Match ( arg3 , pattern1x1_2 . c_str ( ) ) ) {
if ( arg1 - > str ( ) ! = arg3 - > str ( ) ) {
mismatchingContainersError ( arg1 ) ;
}
2011-10-18 21:55:41 +02:00
}
2012-04-17 12:21:41 +02:00
}
2012-11-16 06:50:49 +01:00
tok = arg1 - > linkAt ( - 1 ) ;
2009-10-18 18:42:01 +02:00
}
}
2015-07-21 14:13:26 +02:00
for ( unsigned int varid = 0 ; varid < symbolDatabase - > getVariableListSize ( ) ; varid + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( varid ) ;
if ( var & & var - > isStlStringType ( ) & & Token : : Match ( var - > nameToken ( ) , " %var% ( " ) & & Token : : Match ( var - > nameToken ( ) - > tokAt ( 2 ) , pattern2 . c_str ( ) ) ) {
if ( var - > nameToken ( ) - > strAt ( 2 ) ! = var - > nameToken ( ) - > strAt ( 8 ) ) {
mismatchingContainersError ( var - > nameToken ( ) ) ;
}
}
}
2009-10-18 18:42:01 +02:00
}
2009-02-10 20:56:00 +01:00
void CheckStl : : stlOutOfBounds ( )
{
2011-12-09 22:28:10 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-16 06:50:49 +01:00
// Scan through all scopes..
2011-12-09 22:28:10 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
2013-03-04 19:59:46 +01:00
const Token * tok = i - > classDef ;
// only interested in conditions
2014-10-02 15:19:01 +02:00
if ( ( i - > type ! = Scope : : eFor & & i - > type ! = Scope : : eWhile & & i - > type ! = Scope : : eIf & & i - > type ! = Scope : : eDo ) | | ! tok )
2009-02-10 20:56:00 +01:00
continue ;
2014-07-02 16:16:19 +02:00
if ( i - > type = = Scope : : eFor )
2013-03-04 19:59:46 +01:00
tok = Token : : findsimplematch ( tok - > tokAt ( 2 ) , " ; " ) ;
2014-10-02 15:19:01 +02:00
else if ( i - > type = = Scope : : eDo ) {
tok = tok - > linkAt ( 1 ) - > tokAt ( 2 ) ;
} else
2013-03-04 19:59:46 +01:00
tok = tok - > next ( ) ;
2015-01-03 22:36:39 +01:00
if ( ! tok )
continue ;
2015-01-03 22:17:35 +01:00
tok = tok - > next ( ) ;
2013-03-04 19:59:46 +01:00
2010-10-15 18:21:53 +02:00
// check if the for loop condition is wrong
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %var% <= %var% . %name% ( ) ;|)|%oror% " ) ) {
2013-03-04 19:59:46 +01:00
// Is it a vector?
2015-01-03 22:17:35 +01:00
const Variable * var = tok - > tokAt ( 2 ) - > variable ( ) ;
if ( ! var )
continue ;
const Library : : Container * container = _settings - > library . detectContainer ( var - > typeStartToken ( ) ) ;
2013-03-04 19:59:46 +01:00
if ( ! container )
continue ;
2015-01-03 22:17:35 +01:00
2015-01-04 14:31:58 +01:00
if ( container - > getYield ( tok - > strAt ( 4 ) ) ! = Library : : Container : : SIZE )
2013-03-04 19:59:46 +01:00
continue ;
// variable id for loop variable.
2015-01-03 22:17:35 +01:00
const unsigned int numId = tok - > varId ( ) ;
2013-03-04 19:59:46 +01:00
// variable id for the container variable
2015-01-03 22:17:35 +01:00
const unsigned int declarationId = var - > declarationId ( ) ;
2013-03-04 19:59:46 +01:00
2014-10-02 15:19:01 +02:00
for ( const Token * tok3 = i - > classStart ; tok3 & & tok3 ! = i - > classEnd ; tok3 = tok3 - > next ( ) ) {
2013-07-20 12:31:04 +02:00
if ( tok3 - > varId ( ) = = declarationId ) {
2015-01-03 22:17:35 +01:00
tok3 = tok3 - > next ( ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok3 , " . %name% ( ) " ) ) {
2015-01-04 14:31:58 +01:00
if ( container - > getYield ( tok3 - > strAt ( 1 ) ) = = Library : : Container : : SIZE )
2015-01-03 22:17:35 +01:00
break ;
} else if ( container - > arrayLike_indexOp & & Token : : Match ( tok3 , " [ %varid% ] " , numId ) )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 1 ) , var - > name ( ) , false ) ;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok3 , " . %name% ( %varid% ) " , numId)) {
2015-01-03 22:17:35 +01:00
Library : : Container : : Yield yield = container - > getYield ( tok3 - > strAt ( 1 ) ) ;
if ( yield = = Library : : Container : : AT_INDEX )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 3 ) , var - > name ( ) , true ) ;
}
2009-05-06 21:55:04 +02:00
}
2009-02-10 20:56:00 +01:00
}
2015-01-03 22:17:35 +01:00
continue ;
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 )
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " When " + num + " == " + var + " .size(), " + var + " .at( " + num + " ) is out of bounds. " , CWE788 , false ) ;
2012-02-25 12:43:27 +01:00
else
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " When " + num + " == " + var + " .size(), " + var + " [ " + num + " ] is out of bounds. " , CWE788 , false ) ;
2009-02-10 20:56:00 +01:00
}
2009-02-11 06:08:29 +01:00
void CheckStl : : erase ( )
{
2011-12-09 22:28:10 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
2015-07-23 18:53:31 +02:00
if ( i - > type = = Scope : : eFor & & Token : : simpleMatch ( i - > classDef , " for ( " ) ) {
const Token * tok = i - > classDef - > linkAt ( 1 ) ;
if ( ! Token : : Match ( tok - > tokAt ( - 3 ) , " ; ++| %var% ++| ) { " ) )
continue ;
tok = tok - > previous ( ) ;
if ( ! tok - > isName ( ) )
tok = tok - > previous ( ) ;
eraseCheckLoopVar ( * i , tok - > variable ( ) ) ;
} else if ( i - > type = = Scope : : eWhile & & Token : : Match ( i - > classDef , " while ( %var% != " ) ) {
eraseCheckLoopVar ( * i , i - > classDef - > tokAt ( 2 ) - > variable ( ) ) ;
}
}
}
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 )
{
if ( ! var | | ! Token : : simpleMatch ( var - > typeEndToken ( ) , " iterator " ) )
return ;
for ( const Token * tok = scope . classStart ; tok ! = scope . classEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) ! = " ( " )
continue ;
2015-07-30 10:13:49 +02:00
if ( ! Token : : Match ( tok - > tokAt ( - 2 ) , " . erase ( ++| %varid% ) " , var - > declarationId ( ) ) )
continue ;
if ( Token : : simpleMatch ( tok - > astParent ( ) , " = " ) )
2015-07-23 18:53:31 +02:00
continue ;
// Iterator is invalid..
unsigned int indentlevel = 0U ;
const Token * tok2 = tok - > link ( ) ;
for ( ; tok2 ! = scope . classEnd ; tok2 = tok2 - > next ( ) ) {
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 ;
2015-07-23 18:53:31 +02:00
dereferenceErasedError ( tok , tok2 , tok2 - > str ( ) ) ;
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
}
2015-07-23 18:53:31 +02:00
if ( tok2 = = scope . classEnd )
dereferenceErasedError ( tok , scope . classDef , var - > nameToken ( ) - > str ( ) ) ;
2009-02-11 06:08:29 +01:00
}
}
2009-02-18 20:57:43 +01:00
void CheckStl : : pushback ( )
{
2012-02-25 12:43:27 +01:00
// Pointer can become invalid after push_back, push_front, reserve or resize..
2012-11-16 06:50:49 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% = & %var% [ " ) ) {
2015-11-13 13:22:47 +01:00
// Skip it directly if it is a pointer or an array
2015-11-15 14:55:10 +01:00
const Token * containerTok = tok - > tokAt ( 3 ) ;
if ( containerTok - > variable ( ) & & containerTok - > variable ( ) - > isArrayOrPointer ( ) )
2015-11-13 13:22:47 +01:00
continue ;
2012-11-16 06:50:49 +01:00
// Variable id for pointer
const unsigned int pointerId ( tok - > varId ( ) ) ;
bool invalidPointer = false ;
2014-04-12 23:39:52 +02:00
const Token * function = nullptr ;
const Token * end2 = tok - > scope ( ) - > classEnd ;
for ( const Token * tok2 = tok ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
2012-11-16 06:50:49 +01:00
// push_back on vector..
2015-11-15 14:55:10 +01:00
if ( Token : : Match ( tok2 , " %varid% . push_front|push_back|insert|reserve|resize|clear " , containerTok - > varId ( ) ) ) {
2012-11-16 06:50:49 +01:00
invalidPointer = true ;
2014-04-12 23:39:52 +02:00
function = tok2 - > tokAt ( 2 ) ;
2012-11-16 06:50:49 +01:00
}
2009-04-28 21:18:02 +02:00
2012-11-16 06:50:49 +01:00
// Using invalid pointer..
if ( invalidPointer & & tok2 - > varId ( ) = = pointerId ) {
2014-04-12 23:39:52 +02:00
bool unknown = false ;
if ( CheckNullPointer : : isPointerDeRef ( tok2 , unknown ) )
invalidPointerError ( tok2 , function - > str ( ) , tok2 - > str ( ) ) ;
2012-11-16 06:50:49 +01:00
break ;
}
2009-04-28 21:18:02 +02:00
}
}
}
}
2012-02-25 12:43:27 +01:00
// Iterator becomes invalid after reserve, resize, insert, push_back or push_front..
2014-04-12 19:44:37 +02:00
for ( unsigned int iteratorId = 1 ; iteratorId < symbolDatabase - > getVariableListSize ( ) ; iteratorId + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( iteratorId ) ;
2009-10-31 15:27:33 +01:00
2014-04-12 19:44:37 +02:00
// Check that its an iterator
if ( ! var | | ! var - > isLocal ( ) | | ! Token : : Match ( var - > typeEndToken ( ) , " iterator|const_iterator|reverse_iterator|const_reverse_iterator " ) )
continue ;
2009-10-31 15:27:33 +01:00
2014-04-12 19:44:37 +02:00
// ... on std::vector
if ( ! Token : : Match ( var - > typeStartToken ( ) , " std| ::| vector < " ) )
continue ;
2011-01-06 12:20:54 +01:00
2014-04-12 19:44:37 +02:00
// the variable id for the vector
unsigned int vectorid = 0 ;
2011-01-06 12:20:54 +01:00
2014-04-12 19:44:37 +02:00
const Token * validatingToken = 0 ;
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
std : : string invalidIterator ;
const Token * end2 = var - > scope ( ) - > classEnd ;
for ( const Token * tok2 = var - > nameToken ( ) ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
2009-09-27 09:59:19 +02:00
2014-04-12 19:44:37 +02:00
if ( validatingToken = = tok2 ) {
invalidIterator . clear ( ) ;
validatingToken = 0 ;
}
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
// Using push_back or push_front inside a loop..
if ( Token : : simpleMatch ( tok2 , " for ( " ) ) {
tok2 = tok2 - > tokAt ( 2 ) ;
}
2009-04-25 17:14:02 +02:00
2014-04-12 19:44:37 +02:00
if ( Token : : Match ( tok2 , " %varid% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %varid% != %var% . end|rend|cend|crend ( ) ; ++| %varid% ++| ) { " , iteratorId ) ) {
// variable id for the loop iterator
const unsigned int varId ( tok2 - > tokAt ( 2 ) - > varId ( ) ) ;
2009-09-27 09:59:19 +02:00
2014-04-12 19:44:37 +02:00
const Token * pushbackTok = nullptr ;
2011-01-06 12:20:54 +01:00
2014-04-12 19:44:37 +02:00
// Count { and } for tok3
const Token * tok3 = tok2 - > tokAt ( 20 ) ;
for ( const Token * const end3 = tok3 - > linkAt ( - 1 ) ; tok3 ! = end3 ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > str ( ) = = " break " | | tok3 - > str ( ) = = " return " ) {
pushbackTok = 0 ;
break ;
} else if ( Token : : Match ( tok3 , " %varid% . push_front|push_back|insert|reserve|resize|clear|erase ( " , varId ) & & ! tok3 - > previous ( ) - > isAssignmentOp ( ) ) {
if ( tok3 - > strAt ( 2 ) ! = " erase " | | ( tok3 - > tokAt ( 4 ) - > varId ( ) ! = iteratorId & & tok3 - > tokAt ( 5 ) - > varId ( ) ! = iteratorId ) ) // This case is handled in: CheckStl::iterators()
2012-11-16 06:50:49 +01:00
pushbackTok = tok3 - > tokAt ( 2 ) ;
2009-07-14 12:40:47 +02:00
}
2009-11-10 19:07:04 +01:00
}
2009-02-18 20:57:43 +01:00
2014-04-12 19:44:37 +02:00
if ( pushbackTok )
invalidIteratorError ( pushbackTok , pushbackTok - > str ( ) , tok2 - > str ( ) ) ;
}
2009-11-02 21:53:01 +01:00
2014-04-12 19:44:37 +02:00
// Assigning iterator..
if ( Token : : Match ( tok2 , " %varid% = " , iteratorId ) ) {
if ( Token : : Match ( tok2 - > tokAt ( 2 ) , " %var% . begin|end|rbegin|rend|cbegin|cend|crbegin|crend|insert|erase|find ( " ) ) {
if ( ! invalidIterator . empty ( ) & & Token : : Match ( tok2 - > tokAt ( 4 ) , " insert|erase ( *| %varid% )|, " , iteratorId ) ) {
invalidIteratorError ( tok2 , invalidIterator , var - > name ( ) ) ;
2012-11-16 06:50:49 +01:00
break ;
}
2014-04-12 19:44:37 +02:00
vectorid = tok2 - > tokAt ( 2 ) - > varId ( ) ;
tok2 = tok2 - > linkAt ( 5 ) ;
} else {
vectorid = 0 ;
}
invalidIterator = " " ;
}
2009-10-31 15:27:33 +01:00
2014-04-12 19:44:37 +02:00
// push_back on vector..
if ( vectorid > 0 & & Token : : Match ( tok2 , " %varid% . push_front|push_back|insert|reserve|resize|clear|erase ( " , vectorid ) ) {
if ( ! invalidIterator . empty ( ) & & Token : : Match ( tok2 - > tokAt ( 2 ) , " insert|erase ( *| %varid% ,|) " , iteratorId ) ) {
invalidIteratorError ( tok2 , invalidIterator , var - > name ( ) ) ;
break ;
2012-11-16 06:50:49 +01:00
}
2012-11-11 13:03:58 +01:00
2014-04-12 19:44:37 +02:00
if ( tok2 - > strAt ( 2 ) ! = " erase " | | ( tok2 - > tokAt ( 4 ) - > varId ( ) ! = iteratorId & & tok2 - > tokAt ( 5 ) - > varId ( ) ! = iteratorId ) ) // This case is handled in: CheckStl::iterators()
invalidIterator = tok2 - > strAt ( 2 ) ;
tok2 = tok2 - > linkAt ( 3 ) ;
}
else if ( tok2 - > str ( ) = = " return " | | tok2 - > str ( ) = = " throw " )
validatingToken = Token : : findsimplematch ( tok2 - > next ( ) , " ; " ) ;
2010-04-16 16:56:55 +02:00
2014-04-12 19:44:37 +02:00
// TODO: instead of bail out for 'else' try to check all execution paths.
else if ( tok2 - > str ( ) = = " break " | | tok2 - > str ( ) = = " else " )
invalidIterator . clear ( ) ;
2012-11-16 06:50:49 +01:00
2014-04-12 19:44:37 +02:00
// Using invalid iterator..
if ( ! invalidIterator . empty ( ) ) {
if ( Token : : Match ( tok2 , " ++|--|*|+|-|(|,|=|!= %varid% " , iteratorId ) )
invalidIteratorError ( tok2 , invalidIterator , tok2 - > strAt ( 1 ) ) ;
if ( Token : : Match ( tok2 , " %varid% ++|--|+|-|. " , iteratorId ) )
invalidIteratorError ( tok2 , invalidIterator , tok2 - > str ( ) ) ;
2009-10-31 15:27:33 +01:00
}
2009-02-18 20:57:43 +01:00
}
}
}
2009-03-21 17:58:13 +01:00
// Error message for bad iterator usage..
2009-10-21 20:15:11 +02:00
void CheckStl : : invalidIteratorError ( const Token * tok , const std : : string & func , const std : : string & iterator_name )
2009-03-21 17:58:13 +01:00
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidIterator2 " , " After " + func + " (), the iterator ' " + iterator_name + " ' may be invalid. " , CWE664 , false ) ;
2009-03-21 17:58:13 +01:00
}
2009-03-18 22:40:38 +01:00
2009-04-28 21:18:02 +02:00
// Error message for bad iterator usage..
2012-10-14 11:16:48 +02:00
void CheckStl : : invalidPointerError ( const Token * tok , const std : : string & func , const std : : string & pointer_name )
2009-04-28 21:18:02 +02:00
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidPointer " , " Invalid pointer ' " + pointer_name + " ' after " + func + " (). " , CWE664 , false ) ;
2009-04-28 21:18:02 +02:00
}
2013-02-10 07:43:09 +01:00
void CheckStl : : stlBoundaries ( )
2009-04-13 17:48:13 +02:00
{
2012-11-16 06:50:49 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2015-11-20 12:46:59 +01:00
for ( unsigned int iteratorId = 1 ; iteratorId < symbolDatabase - > getVariableListSize ( ) ; iteratorId + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( iteratorId ) ;
if ( ! var | | ! var - > scope ( ) | | ! var - > scope ( ) - > isExecutable ( ) )
continue ;
const Library : : Container * container = _settings - > library . detectContainer ( var - > typeStartToken ( ) , true ) ;
if ( ! container | | container - > opLessAllowed )
continue ;
const Token * const end = var - > scope ( ) - > classEnd ;
for ( const Token * tok = var - > nameToken ( ) ; tok ! = end ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " !!* %varid% < " , var - > declarationId ( ) ) ) {
stlBoundariesError ( tok ) ;
} else if ( Token : : Match ( tok , " > %varid% !!. " , var - > declarationId ( ) ) ) {
stlBoundariesError ( tok ) ;
2009-04-13 17:48:13 +02:00
}
}
}
}
2012-05-05 15:21:27 +02:00
// Error message for bad boundary usage..
2015-11-20 12:46:59 +01:00
void CheckStl : : stlBoundariesError ( const Token * tok )
2009-04-13 17:48:13 +02:00
{
2013-02-10 07:43:09 +01:00
reportError ( tok , Severity : : error , " stlBoundaries " ,
2015-11-20 12:46:59 +01:00
" Dangerous comparison using operator< on iterator. \n "
" Iterator compared with operator<. This is dangerous since the order of items in the "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" container is not guaranteed. One should use operator!= instead to compare iterators. " , CWE664 , false ) ;
2009-04-13 17:48:13 +02:00
}
2009-11-02 20:24:38 +01:00
2015-01-03 11:06:44 +01:00
static bool if_findCompare ( const Token * const tokBack )
2012-06-07 19:33:18 +02:00
{
2015-01-03 11:06:44 +01:00
const Token * tok = tokBack - > astParent ( ) ;
if ( ! tok )
2015-01-03 11:29:13 +01:00
return true ;
2015-01-03 11:06:44 +01:00
if ( tok - > isComparisonOp ( ) )
2015-07-21 20:56:47 +02:00
return ( ! tok - > astOperand1 ( ) - > isNumber ( ) & & ! tok - > astOperand2 ( ) - > isNumber ( ) ) ;
2015-01-03 11:06:44 +01:00
if ( tok - > isArithmeticalOp ( ) ) // result is used in some calculation
return true ; // TODO: check if there is a comparison of the result somewhere
2015-01-04 12:43:31 +01:00
if ( tok - > str ( ) = = " . " )
return true ; // Dereferencing is OK, the programmer might know that the element exists - TODO: An inconclusive warning might be appropriate
2015-01-03 11:29:13 +01:00
if ( tok - > isAssignmentOp ( ) )
return if_findCompare ( tok ) ; // Go one step upwards in the AST
2012-06-07 19:33:18 +02:00
return false ;
}
2010-02-27 21:26:11 +01:00
void CheckStl : : if_find ( )
{
2015-04-10 14:18:52 +02:00
const bool printWarning = _settings - > isEnabled ( " warning " ) ;
const bool printPerformance = _settings - > isEnabled ( " performance " ) ;
if ( ! printWarning & & ! printPerformance )
2010-02-28 06:56:47 +01:00
return ;
2011-08-14 16:46:35 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2011-12-09 22:28:10 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
2014-07-02 16:16:19 +02:00
if ( ( i - > type ! = Scope : : eIf & & i - > type ! = Scope : : eWhile ) | | ! i - > classDef )
2011-12-09 22:28:10 +01:00
continue ;
2015-11-20 20:08:35 +01:00
for ( const Token * tok = i - > classDef - > next ( ) ; tok - > str ( ) ! = " { " ; tok = tok - > next ( ) ) {
2015-01-04 12:43:31 +01:00
const Token * funcTok = nullptr ;
const Library : : Container * container = nullptr ;
2014-11-18 14:35:36 +01:00
2015-11-20 20:08:35 +01:00
if ( Token : : Match ( tok , " %name% ( " ) )
tok = tok - > linkAt ( 1 ) ;
else if ( tok - > variable ( ) & & Token : : Match ( tok , " %var% . %name% ( " ) ) {
2015-01-04 12:43:31 +01:00
container = _settings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ;
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 ( ) )
container = _settings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ;
else { // Container of container - find the inner container
container = _settings - > library . detectContainer ( tok - > variable ( ) - > typeStartToken ( ) ) ; // outer container
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 ( ) ;
container = _settings - > library . detectContainer ( tok2 ) ; // innner container
} else
container = nullptr ;
2012-01-02 22:52:50 +01:00
}
}
2015-01-04 12:43:31 +01:00
if ( container & & container - > getAction ( funcTok - > str ( ) ) = = Library : : Container : : FIND ) {
if ( if_findCompare ( funcTok - > next ( ) ) )
continue ;
2015-11-20 18:22:15 +01:00
if ( printWarning & & container - > getYield ( funcTok - > str ( ) ) = = Library : : Container : : ITERATOR )
2015-01-04 12:43:31 +01:00
if_findError ( tok , false ) ;
2016-01-29 08:55:46 +01:00
else if ( printPerformance & & container - > stdStringLike & & funcTok - > str ( ) = = " find " )
2015-01-04 12:43:31 +01:00
if_findError ( tok , true ) ;
2015-04-10 14:18:52 +02:00
} else if ( printWarning & & Token : : Match ( tok , " std :: find|find_if ( " ) ) {
2012-04-03 20:59:45 +02:00
// check that result is checked properly
2015-01-03 11:06:44 +01:00
if ( ! if_findCompare ( tok - > tokAt ( 3 ) ) ) {
2012-04-03 20:59:45 +02:00
if_findError ( tok , false ) ;
}
2010-02-28 07:04:58 +01:00
}
}
2010-02-27 21:26:11 +01:00
}
}
void CheckStl : : if_findError ( const Token * tok , bool str )
{
2010-04-02 07:30:58 +02:00
if ( str )
2012-04-04 19:40:28 +02:00
reportError ( tok , Severity : : performance , " stlIfStrFind " ,
2012-10-14 11:16:48 +02:00
" Inefficient usage of string::find() in condition; string::compare() would be faster. \n "
2013-02-10 07:43:09 +01:00
" Either inefficient or wrong usage of string::find(). string::compare() will be faster if "
2012-04-04 19:40:28 +02:00
" string::find's result is compared with 0, because it will not scan the whole "
" string. If your intention is to check that there are no findings in the string, "
2016-06-05 18:24:06 +02:00
" you should compare with std::string::npos. " , CWE597 , false ) ;
2010-02-27 21:26:11 +01:00
else
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : warning , " stlIfFind " , " Suspicious condition. The result of find() is an iterator, but it is not properly checked. " ) ;
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 ( )
{
2011-09-03 15:30:30 +02:00
if ( ! _settings - > isEnabled ( " performance " ) )
2010-04-21 08:38:25 +02:00
return ;
2015-05-17 20:02:41 +02:00
if ( _settings - > standards . cpp = = Standards : : CPP11 )
return ;
2012-11-16 06:50:49 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% . size ( ) " ) | |
2015-01-31 10:50:39 +01:00
Token : : Match ( tok , " %name% . %var% . size ( ) " ) ) {
2013-02-05 06:46:26 +01:00
// get the variable
2015-10-14 14:06:04 +02:00
const Token * varTok = tok ;
2013-02-05 06:46:26 +01:00
if ( tok - > strAt ( 2 ) ! = " size " )
2015-10-14 14:06:04 +02:00
varTok = varTok - > tokAt ( 2 ) ;
2012-11-16 06:50:49 +01:00
2015-10-14 14:06:04 +02:00
const Token * const end = varTok - > tokAt ( 5 ) ;
2012-04-04 19:44:57 +02:00
2015-01-31 10:50:39 +01:00
// check for comparison to zero
if ( ( tok - > previous ( ) & & ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " ==|<=|!=|> 0 " ) ) | |
( end - > next ( ) & & ! end - > next ( ) - > isArithmeticalOp ( ) & & Token : : Match ( tok - > tokAt ( - 2 ) , " 0 ==|>=|!=|< " ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) ) {
sizeError ( varTok ) ;
continue ;
}
2015-01-31 10:50:39 +01:00
}
2011-03-29 01:31:23 +02:00
2015-01-31 10:50:39 +01:00
// check for comparison to one
if ( ( tok - > previous ( ) & & ! tok - > previous ( ) - > isArithmeticalOp ( ) & & Token : : Match ( end , " >=|< 1 " ) & & ! end - > tokAt ( 2 ) - > isArithmeticalOp ( ) ) | |
( end - > next ( ) & & ! end - > next ( ) - > isArithmeticalOp ( ) & & Token : : Match ( tok - > tokAt ( - 2 ) , " 1 <=|> " ) & & ! tok - > tokAt ( - 3 ) - > isArithmeticalOp ( ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) )
sizeError ( varTok ) ;
2015-01-31 10:50:39 +01:00
}
2012-11-04 16:15:26 +01:00
2015-01-31 10:50:39 +01:00
// check for using as boolean expression
else if ( ( Token : : Match ( tok - > tokAt ( - 2 ) , " if|while ( " ) & & end - > str ( ) = = " ) " ) | |
2015-08-14 20:46:13 +02:00
( tok - > previous ( ) - > tokType ( ) = = Token : : eLogicalOp & & Token : : Match ( end , " &&|)|,|;|%oror% " ) ) ) {
2015-10-14 14:06:04 +02:00
if ( isCpp03ContainerSizeSlow ( varTok ) )
sizeError ( varTok ) ;
2011-03-29 01:31:23 +02:00
}
2009-12-19 15:24:59 +01:00
}
}
}
}
void CheckStl : : sizeError ( const Token * tok )
{
2011-12-24 08:10:16 +01:00
const std : : string varname = tok ? tok - > str ( ) : std : : string ( " list " ) ;
2010-12-04 09:15:48 +01:00
reportError ( tok , Severity : : performance , " stlSize " ,
" Possible inefficient checking for ' " + varname + " ' emptiness. \n "
2011-02-04 10:10:24 +01:00
" Checking for ' " + varname + " ' emptiness might be inefficient. "
2010-12-04 09:15:48 +01:00
" Using " + varname + " .empty() instead of " + varname + " .size() can be faster. " +
varname + " .size() can take linear time but " + varname + " .empty() is "
2016-06-05 18:24:06 +02:00
" guaranteed to take constant time. " , CWE398 , false ) ;
2009-12-19 15:24:59 +01:00
}
2010-09-16 18:49:23 +02:00
void CheckStl : : redundantCondition ( )
{
2015-04-06 14:10:27 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
return ;
2014-04-12 22:24:31 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
2014-07-02 16:16:19 +02:00
if ( i - > type ! = Scope : : eIf )
2014-04-12 22:24:31 +02:00
continue ;
const Token * tok = i - > classDef - > tokAt ( 2 ) ;
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( tok , " %name% . find ( %any% ) != %name% . end|rend|cend|crend ( ) ) { %name% . remove|erase ( %any% ) ; " ) )
2014-04-12 22:24:31 +02:00
continue ;
2015-01-31 10:50:39 +01:00
// Get tokens for the fields %name% and %any%
2014-04-12 22:24:31 +02:00
const Token * var1 = tok ;
2012-04-04 19:44:57 +02:00
const Token * any1 = var1 - > tokAt ( 4 ) ;
const Token * var2 = any1 - > tokAt ( 3 ) ;
const Token * var3 = var2 - > tokAt ( 7 ) ;
const Token * any2 = var3 - > tokAt ( 4 ) ;
2010-09-16 18:49:23 +02:00
2015-01-31 10:50:39 +01:00
// Check if all the "%name%" fields are the same and if all the "%any%" are the same..
2010-09-16 18:49:23 +02:00
if ( var1 - > str ( ) = = var2 - > str ( ) & &
var2 - > str ( ) = = var3 - > str ( ) & &
2011-10-13 20:53:06 +02:00
any1 - > str ( ) = = any2 - > str ( ) ) {
2010-09-16 18:49:23 +02:00
redundantIfRemoveError ( tok ) ;
}
}
}
void CheckStl : : redundantIfRemoveError ( const Token * tok )
{
2011-02-04 10:10:24 +01:00
reportError ( tok , Severity : : style , " redundantIfRemove " ,
2012-10-14 11:16:48 +02:00
" Redundant checking of STL container element existence before removing it. \n "
2011-02-04 10:10:24 +01:00
" Redundant checking of STL container element existence before removing it. "
2016-06-05 18:24:06 +02:00
" It is safe to call the remove method on a non-existing element. " , CWE398 , false ) ;
2010-09-16 18:49:23 +02:00
}
2010-10-10 10:52:41 +02:00
void CheckStl : : missingComparison ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
2012-02-25 12:43:27 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-10-10 10:52:41 +02:00
2012-02-25 12:43:27 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eFor | | ! i - > classDef )
continue ;
2010-10-10 10:52:41 +02:00
2012-02-25 12:43:27 +01:00
for ( const Token * tok2 = i - > classDef - > tokAt ( 2 ) ; tok2 ! = i - > classStart ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ; " )
break ;
2010-10-10 10:52:41 +02:00
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( tok2 , " %var% = %name% . begin|rbegin|cbegin|crbegin ( ) ; %name% != %name% . end|rend|cend|crend ( ) ; ++| %name% ++| ) { " ) )
2012-02-25 12:43:27 +01:00
continue ;
2010-10-10 10:52:41 +02:00
2012-02-25 12:43:27 +01:00
// same container
if ( tok2 - > strAt ( 2 ) ! = tok2 - > strAt ( 10 ) )
2012-04-04 19:44:57 +02:00
break ;
const unsigned int iteratorId ( tok2 - > varId ( ) ) ;
// same iterator
if ( iteratorId = = tok2 - > tokAt ( 10 ) - > varId ( ) )
break ;
2010-10-30 11:22:30 +02:00
2012-02-25 12:43:27 +01:00
// increment iterator
2012-04-04 19:44:57 +02:00
if ( ! Token : : Match ( tok2 - > tokAt ( 16 ) , " ++ %varid% ) " , iteratorId ) & &
! Token : : Match ( tok2 - > tokAt ( 16 ) , " %varid% ++ ) " , iteratorId ) ) {
break ;
2012-02-25 12:43:27 +01:00
}
2011-01-06 12:20:54 +01:00
2014-02-16 11:47:52 +01:00
const Token * incrementToken = nullptr ;
2012-02-25 12:43:27 +01:00
// Parse loop..
2012-04-04 19:44:57 +02:00
for ( const Token * tok3 = i - > classStart ; tok3 ! = i - > classEnd ; tok3 = tok3 - > next ( ) ) {
if ( Token : : Match ( tok3 , " %varid% ++ " , iteratorId ) )
2012-02-25 12:43:27 +01:00
incrementToken = tok3 ;
else if ( Token : : Match ( tok3 - > previous ( ) , " ++ %varid% !!. " , iteratorId ) )
incrementToken = tok3 ;
else if ( Token : : Match ( tok3 , " %varid% !=|== " , iteratorId ) )
incrementToken = 0 ;
else if ( tok3 - > str ( ) = = " break " | | tok3 - > str ( ) = = " return " )
incrementToken = 0 ;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok3 , " %varid% = %name% . insert ( ++| %varid% ++| , " , iteratorId ) ) {
2012-02-25 12:43:27 +01:00
// skip insertion..
tok3 = tok3 - > linkAt ( 6 ) ;
if ( ! tok3 )
break ;
2010-10-10 10:52:41 +02:00
}
}
2012-02-25 12:43:27 +01:00
if ( incrementToken )
missingComparisonError ( incrementToken , tok2 - > tokAt ( 16 ) ) ;
2010-10-10 10:52:41 +02:00
}
}
}
void CheckStl : : missingComparisonError ( const Token * incrementToken1 , const Token * incrementToken2 )
{
2012-10-14 11:16:48 +02:00
std : : list < const Token * > callstack ;
callstack . push_back ( incrementToken1 ) ;
callstack . push_back ( incrementToken2 ) ;
2010-10-10 10:52:41 +02:00
std : : ostringstream errmsg ;
2010-11-29 19:27:31 +01:00
errmsg < < " Missing bounds check for extra iterator increment in loop. \n "
2012-10-14 11:16:48 +02:00
< < " The iterator incrementing is suspicious - it is incremented at line " ;
if ( incrementToken1 )
errmsg < < incrementToken1 - > linenr ( ) ;
errmsg < < " and then at line " ;
if ( incrementToken2 )
errmsg < < incrementToken2 - > linenr ( ) ;
errmsg < < " . The loop might unintentionally skip an element in the container. "
2010-11-27 09:57:26 +01:00
< < " There is no comparison between these increments to prevent that the iterator is "
< < " incremented beyond the end. " ;
2010-10-10 10:52:41 +02:00
2016-06-05 18:24:06 +02:00
reportError ( callstack , Severity : : warning , " StlMissingComparison " , errmsg . str ( ) , CWE834 , false ) ;
2010-10-10 10:52:41 +02:00
}
2013-02-05 06:46:26 +01:00
static bool isLocal ( const Token * tok )
2011-12-17 11:21:34 +01:00
{
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > variable ( ) ;
2011-12-18 19:44:38 +01:00
return var & & ! var - > isStatic ( ) & & var - > isLocal ( ) ;
2011-12-17 11:21:34 +01:00
}
2015-06-10 21:14:17 +02:00
namespace {
2015-11-30 22:13:49 +01:00
const std : : set < std : : string > stl_string_stream = make_container < std : : set < std : : string > > ( ) < <
2015-05-17 20:02:41 +02:00
" istringstream " < < " ostringstream " < < " stringstream " < < " wstringstream " ;
2015-06-10 21:14:17 +02:00
}
void CheckStl : : string_c_str ( )
{
const bool printInconclusive = _settings - > inconclusive ;
const bool printPerformance = _settings - > isEnabled ( " performance " ) ;
2014-01-30 22:09:24 +01:00
2011-12-17 11:21:34 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-02-25 12:43:27 +01:00
// Find all functions that take std::string as argument
std : : multimap < std : : string , unsigned int > c_strFuncParam ;
2015-04-10 14:18:52 +02:00
if ( printPerformance ) {
2012-02-25 12:43:27 +01:00
for ( std : : list < Scope > : : const_iterator scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2012-03-23 17:59:51 +01:00
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( c_strFuncParam . erase ( func - > tokenDef - > str ( ) ) ! = 0 ) { // Check if function with this name was already found
c_strFuncParam . insert ( std : : make_pair ( func - > tokenDef - > str ( ) , 0 ) ) ; // Disable, because there are overloads. TODO: Handle overloads
2012-02-25 12:43:27 +01:00
continue ;
}
unsigned int numpar = 0 ;
2012-03-23 17:59:51 +01:00
c_strFuncParam . insert ( std : : make_pair ( func - > tokenDef - > str ( ) , numpar ) ) ; // Insert function as dummy, to indicate that there is at least one function with that name
2015-11-20 11:20:23 +01:00
for ( std : : list < Variable > : : const_iterator var = func - > argumentList . cbegin ( ) ; var ! = func - > argumentList . cend ( ) ; + + var ) {
2012-02-25 12:43:27 +01:00
numpar + + ;
2015-11-20 11:20:23 +01:00
if ( var - > isStlStringType ( ) & & ( ! var - > isReference ( ) | | var - > isConst ( ) ) )
2012-03-23 17:59:51 +01:00
c_strFuncParam . insert ( std : : make_pair ( func - > tokenDef - > str ( ) , numpar ) ) ;
2012-02-25 12:43:27 +01:00
}
}
}
}
2011-12-17 11:21:34 +01:00
// Try to detect common problems when using string::c_str()
for ( std : : list < Scope > : : const_iterator scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2012-02-25 12:43:27 +01:00
if ( scope - > type ! = Scope : : eFunction | | ! scope - > function )
continue ;
2011-11-04 19:21:19 +01:00
2012-02-25 12:43:27 +01:00
enum { charPtr , stdString , stdStringConstRef , Other } returnType = Other ;
2012-11-06 19:54:52 +01:00
if ( Token : : Match ( scope - > function - > tokenDef - > tokAt ( - 2 ) , " char|wchar_t * " ) )
2012-02-25 12:43:27 +01:00
returnType = charPtr ;
2012-11-06 19:54:52 +01:00
else if ( Token : : Match ( scope - > function - > tokenDef - > tokAt ( - 5 ) , " const std :: string|wstring & " ) )
2012-02-25 12:43:27 +01:00
returnType = stdStringConstRef ;
2012-11-06 19:54:52 +01:00
else if ( Token : : Match ( scope - > function - > tokenDef - > tokAt ( - 3 ) , " std :: string|wstring !!& " ) )
2012-02-25 12:43:27 +01:00
returnType = stdString ;
2012-05-22 21:58:46 +02:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-02-25 12:43:27 +01:00
// Invalid usage..
2014-03-16 19:04:44 +01:00
if ( Token : : Match ( tok , " throw %var% . c_str|data ( ) ; " ) & & isLocal ( tok - > next ( ) ) & &
2015-11-20 11:20:23 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlStringType ( ) ) {
2012-02-25 12:43:27 +01:00
string_c_strThrowError ( tok ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " [;{}] %name% = %var% . str ( ) . c_str|data ( ) ; " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2014-01-30 22:09:24 +01:00
const Variable * var2 = tok - > tokAt ( 3 ) - > variable ( ) ;
if ( var & & var - > isPointer ( ) & & var2 & & var2 - > isStlType ( stl_string_stream ) )
2012-02-25 12:43:27 +01:00
string_c_strError ( tok ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " [;{}] %var% = %name% ( " ) & &
2014-03-16 19:04:44 +01:00
Token : : Match ( tok - > linkAt ( 4 ) , " ) . c_str|data ( ) ; " ) & &
2015-01-31 10:50:39 +01:00
tok - > tokAt ( 3 ) - > function ( ) & & Token : : Match ( tok - > tokAt ( 3 ) - > function ( ) - > retDef , " std :: string|wstring %name% " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2012-02-25 12:43:27 +01:00
if ( var & & var - > isPointer ( ) )
string_c_strError ( tok ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %name% ( !!) " ) & & c_strFuncParam . find ( tok - > str ( ) ) ! = c_strFuncParam . end ( ) & &
2014-07-23 15:06:27 +02:00
! Token : : Match ( tok - > previous ( ) , " ::|. " ) & & tok - > varId ( ) = = 0 & & tok - > str ( ) ! = scope - > className ) { // calling function. TODO: Add support for member functions
2012-02-25 12:43:27 +01:00
std : : pair < std : : multimap < std : : string , unsigned int > : : const_iterator , std : : multimap < std : : string , unsigned int > : : const_iterator > range = c_strFuncParam . equal_range ( tok - > str ( ) ) ;
for ( std : : multimap < std : : string , unsigned int > : : const_iterator i = range . first ; i ! = range . second ; + + i ) {
if ( i - > second = = 0 )
continue ;
const Token * tok2 = tok - > tokAt ( 2 ) ;
unsigned int j ;
for ( j = 0 ; tok2 & & j < i - > second - 1 ; j + + )
tok2 = tok2 - > nextArgument ( ) ;
if ( tok2 )
tok2 = tok2 - > nextArgument ( ) ;
else
break ;
if ( ! tok2 & & j = = i - > second - 1 )
tok2 = tok - > next ( ) - > link ( ) ;
else
tok2 = tok2 - > previous ( ) ;
2014-03-16 19:04:44 +01:00
if ( tok2 & & Token : : Match ( tok2 - > tokAt ( - 4 ) , " . c_str|data ( ) " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok2 - > tokAt ( - 5 ) - > variable ( ) ;
2015-11-20 11:20:23 +01:00
if ( var & & var - > isStlStringType ( ) ) {
2012-11-28 08:48:48 +01:00
string_c_strParam ( tok , i - > second ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok2 - > tokAt ( - 9 ) , " %name% . str ( ) " ) ) { // Check ss.str().c_str() as parameter
2014-01-30 22:09:24 +01:00
const Variable * ssVar = tok2 - > tokAt ( - 9 ) - > variable ( ) ;
if ( ssVar & & ssVar - > isStlType ( stl_string_stream ) )
string_c_strParam ( tok , i - > second ) ;
}
2012-11-28 08:48:48 +01:00
}
2012-02-25 12:43:27 +01:00
}
}
2011-11-04 19:21:19 +01:00
2012-02-25 12:43:27 +01:00
// Using c_str() to get the return value is only dangerous if the function returns a char*
2016-05-04 11:10:12 +02:00
if ( ( returnType = = charPtr | | ( printPerformance & & ( returnType = = stdString | | returnType = = stdStringConstRef ) ) ) & & tok - > str ( ) = = " return " ) {
bool err = false ;
const Token * tok2 = tok - > next ( ) ;
if ( Token : : Match ( tok2 , " std :: string|wstring ( " ) & &
Token : : Match ( tok2 - > linkAt ( 3 ) , " ) . c_str|data ( ) ; " ) ) {
err = true ;
} else if ( Token : : simpleMatch ( tok2 , " ( " ) & &
Token : : Match ( tok2 - > link ( ) , " ) . c_str|data ( ) ; " ) ) {
2011-10-26 22:14:47 +02:00
// Check for "+ localvar" or "+ std::string(" inside the bracket
2015-04-10 14:18:52 +02:00
bool is_implicit_std_string = printInconclusive ;
2016-05-04 11:10:12 +02:00
const Token * search_end = tok2 - > link ( ) ;
for ( const Token * search_tok = tok2 - > next ( ) ; search_tok ! = search_end ; search_tok = search_tok - > next ( ) ) {
2014-01-30 22:09:24 +01:00
if ( Token : : Match ( search_tok , " + %var% " ) & & isLocal ( search_tok - > next ( ) ) & &
2015-11-20 11:20:23 +01:00
search_tok - > next ( ) - > variable ( ) & & search_tok - > next ( ) - > variable ( ) - > isStlStringType ( ) ) {
2011-10-26 22:14:47 +02:00
is_implicit_std_string = true ;
break ;
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( search_tok , " + std :: string|wstring ( " ) ) {
2011-10-26 22:14:47 +02:00
is_implicit_std_string = true ;
break ;
}
}
if ( is_implicit_std_string )
2016-05-04 11:10:12 +02:00
err = true ;
2010-10-19 20:21:58 +02:00
}
2016-05-04 11:10:12 +02:00
bool local = false ;
2016-05-06 17:24:44 +02:00
bool ptr = 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 ) ;
2016-05-06 17:24:44 +02:00
ptr = tok2 - > variable ( ) & & tok2 - > variable ( ) - > isPointer ( ) ;
2016-05-04 11:10:12 +02:00
}
while ( tok2 ) {
if ( Token : : Match ( tok2 , " %var% .|:: " ) ) {
2016-05-06 17:24:44 +02:00
if ( ptr )
local = false ;
2016-05-04 11:10:12 +02:00
lastVar = tok2 - > variable ( ) ;
tok2 = tok2 - > tokAt ( 2 ) ;
} else if ( Token : : Match ( tok2 , " %name% ( " ) & & Token : : simpleMatch ( tok2 - > linkAt ( 1 ) , " ) . " ) ) {
lastFunc = tok2 - > function ( ) ;
local = false ;
funcStr = tok2 - > str ( ) = = " str " ;
tok2 = tok2 - > linkAt ( 1 ) - > tokAt ( 2 ) ;
} else
break ;
}
if ( Token : : Match ( tok2 , " c_str|data ( ) ; " ) ) {
2016-05-06 17:24:44 +02:00
if ( ( local | | returnType ! = charPtr ) & & lastVar & & lastVar - > isStlStringType ( ) )
2016-05-04 11:10:12 +02:00
err = true ;
else if ( funcStr & & lastVar & & lastVar - > isStlType ( stl_string_stream ) )
err = true ;
else if ( lastFunc & & Token : : Match ( lastFunc - > tokenDef - > tokAt ( - 3 ) , " std :: string|wstring " ) )
err = true ;
}
if ( err ) {
if ( returnType = = charPtr )
string_c_strError ( tok ) ;
else
string_c_strReturn ( tok ) ;
2012-02-25 12:43:27 +01:00
}
}
2010-10-17 19:18:46 +02:00
}
}
}
2011-11-20 19:26:07 +01:00
void CheckStl : : string_c_strThrowError ( const Token * tok )
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlcstrthrow " , " Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception. \n "
2011-11-20 19:26:07 +01:00
" Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid. " ) ;
}
2011-12-17 11:21:34 +01:00
void CheckStl : : string_c_strError ( const Token * tok )
2010-10-17 19:18:46 +02:00
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlcstr " , " Dangerous usage of c_str(). The value returned by c_str() is invalid after this call. \n "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted. " , CWE664 , false ) ;
2010-10-17 19:18:46 +02:00
}
2012-02-25 12:43:27 +01:00
void CheckStl : : string_c_strReturn ( const Token * tok )
{
reportError ( tok , Severity : : performance , " stlcstrReturn " , " Returning the result of c_str() in a function that returns std::string is slow and redundant. \n "
2016-06-05 18:24:06 +02:00
" The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string. " , CWE704 , false ) ;
2012-02-25 12:43:27 +01:00
}
void CheckStl : : string_c_strParam ( const Token * tok , unsigned int number )
{
std : : ostringstream oss ;
2012-10-14 11:16:48 +02:00
oss < < " Passing the result of c_str() to a function that takes std::string as argument no. " < < number < < " is slow and redundant. \n "
" The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string. " ;
2016-06-05 18:24:06 +02:00
reportError ( tok , Severity : : performance , " stlcstrParam " , oss . str ( ) , CWE704 , false ) ;
2012-02-25 12:43:27 +01:00
}
2011-11-06 15:32:28 +01:00
static bool hasArrayEnd ( const Token * tok1 )
{
const Token * end = Token : : findsimplematch ( tok1 , " ; " ) ;
return ( end & & Token : : simpleMatch ( end - > previous ( ) , " ] ; " ) ) ;
}
static bool hasArrayEndParen ( const Token * tok1 )
{
const Token * end = Token : : findsimplematch ( tok1 , " ; " ) ;
return ( end & & end - > previous ( ) & &
2011-11-12 22:33:03 +01:00
Token : : simpleMatch ( end - > tokAt ( - 2 ) , " ] ) ; " ) ) ;
2011-11-06 15:32:28 +01:00
}
2011-06-16 20:26:00 +02:00
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void CheckStl : : checkAutoPointer ( )
{
2011-10-30 18:34:49 +01:00
std : : set < unsigned int > autoPtrVarId ;
2015-03-19 06:41:54 +01:00
std : : map < unsigned int , const std : : string > mallocVarId ; // variables allocated by the malloc-like function
2015-11-30 22:13:49 +01:00
const char STL_CONTAINER_LIST [ ] = " array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|vector|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset|basic_string " ;
2016-05-22 17:18:50 +02:00
const int malloc = _settings - > library . allocId ( " malloc " ) ; // allocation function, which are not compatible with auto_ptr
2015-04-07 07:07:08 +02:00
const bool printStyle = _settings - > isEnabled ( " style " ) ;
2011-06-16 20:26:00 +02:00
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " auto_ptr < " ) ) {
2012-07-12 12:23:52 +02:00
if ( ( tok - > strAt ( - 1 ) = = " < " & & Token : : Match ( tok - > tokAt ( - 2 ) , STL_CONTAINER_LIST ) ) | |
2011-10-27 10:41:34 +02:00
( Token : : simpleMatch ( tok - > tokAt ( - 3 ) , " < std :: auto_ptr " ) & & Token : : Match ( tok - > tokAt ( - 4 ) , STL_CONTAINER_LIST ) ) ) {
2011-06-16 20:26:00 +02:00
autoPointerContainerError ( tok ) ;
2011-10-13 20:53:06 +02:00
} else {
2013-05-01 11:11:57 +02:00
const Token * tok2 = tok - > linkAt ( 1 ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok2 , " > %name% " ) ) {
2013-05-01 11:11:57 +02:00
const Token * tok3 = tok2 - > tokAt ( 2 ) ;
if ( Token : : Match ( tok3 , " ( new %type% " ) & & hasArrayEndParen ( tok3 ) ) {
autoPointerArrayError ( tok2 - > next ( ) ) ;
}
2016-05-22 17:18:50 +02:00
if ( Token : : Match ( tok3 , " ( %name% ( " ) & & malloc & & _settings - > library . alloc ( tok3 - > next ( ) , - 1 ) = = malloc ) {
2015-03-19 06:41:54 +01:00
// malloc-like function allocated memory passed to the auto_ptr constructor -> error
autoPointerMallocError ( tok2 - > next ( ) , tok3 - > next ( ) - > str ( ) ) ;
}
if ( Token : : Match ( tok3 , " ( %var% " ) ) {
std : : map < unsigned int , const std : : string > : : const_iterator it = mallocVarId . find ( tok3 - > next ( ) - > varId ( ) ) ;
2015-04-12 14:51:57 +02:00
if ( it ! = mallocVarId . cend ( ) ) {
2015-03-19 06:41:54 +01:00
// pointer on the memory allocated by malloc used in the auto pointer constructor -> error
autoPointerMallocError ( tok2 - > next ( ) , it - > second ) ;
}
}
2013-05-01 11:11:57 +02:00
while ( tok3 & & tok3 - > str ( ) ! = " ; " ) {
tok3 = tok3 - > next ( ) ;
}
if ( tok3 ) {
tok3 = tok3 - > tokAt ( - 2 ) ;
if ( Token : : simpleMatch ( tok3 - > previous ( ) , " [ ] ) " ) ) {
2011-11-05 15:40:50 +01:00
autoPointerArrayError ( tok2 - > next ( ) ) ;
2013-05-01 11:11:57 +02:00
} else if ( tok3 - > varId ( ) ) {
const Token * decltok = Token : : findmatch ( _tokenizer - > tokens ( ) , " %varid% = new %type% " , tok3 - > varId ( ) ) ;
if ( decltok & & hasArrayEnd ( decltok ) ) {
2011-06-16 20:26:00 +02:00
autoPointerArrayError ( tok2 - > next ( ) ) ;
2011-08-05 01:50:18 +02:00
}
2013-05-01 11:11:57 +02:00
}
if ( tok2 - > next ( ) - > varId ( ) ) {
autoPtrVarId . insert ( tok2 - > next ( ) - > varId ( ) ) ;
2011-06-16 20:26:00 +02:00
}
}
}
}
2011-10-13 20:53:06 +02:00
} else {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% = %var% ; " ) ) {
2015-04-07 07:07:08 +02:00
if ( printStyle ) {
2012-02-25 12:43:27 +01:00
std : : set < unsigned int > : : const_iterator iter = autoPtrVarId . find ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
2011-10-13 20:53:06 +02:00
if ( iter ! = autoPtrVarId . end ( ) ) {
2011-11-12 22:33:03 +01:00
autoPointerError ( tok - > tokAt ( 2 ) ) ;
2011-06-16 20:26:00 +02:00
}
}
2014-12-30 14:53:41 +01:00
} else if ( ( Token : : Match ( tok , " %var% = new %type% " ) & & hasArrayEnd ( tok ) ) | |
( Token : : Match ( tok , " %var% . reset ( new %type% " ) & & hasArrayEndParen ( tok ) ) ) {
2012-02-25 12:43:27 +01:00
std : : set < unsigned int > : : const_iterator iter = autoPtrVarId . find ( tok - > varId ( ) ) ;
2011-10-13 20:53:06 +02:00
if ( iter ! = autoPtrVarId . end ( ) ) {
2011-06-16 20:26:00 +02:00
autoPointerArrayError ( tok ) ;
}
2016-05-22 17:18:50 +02:00
} else if ( Token : : Match ( tok , " %var% = %name% ( " ) & & malloc & & _settings - > library . alloc ( tok - > tokAt ( 2 ) , - 1 ) = = malloc ) {
2015-03-19 06:41:54 +01:00
// C library function like 'malloc' used together with auto pointer -> error
std : : set < unsigned int > : : const_iterator iter = autoPtrVarId . find ( tok - > varId ( ) ) ;
if ( iter ! = autoPtrVarId . end ( ) ) {
autoPointerMallocError ( tok , tok - > strAt ( 2 ) ) ;
} else if ( tok - > varId ( ) ) {
// it is not an auto pointer variable and it is allocated by malloc like function.
mallocVarId . insert ( std : : make_pair ( tok - > varId ( ) , tok - > strAt ( 2 ) ) ) ;
}
2016-05-22 17:18:50 +02:00
} else if ( Token : : Match ( tok , " %var% . reset ( %name% ( " ) & & malloc & & _settings - > library . alloc ( tok - > tokAt ( 4 ) , - 1 ) = = malloc ) {
2015-03-19 06:41:54 +01:00
// C library function like 'malloc' used when resetting auto pointer -> error
std : : set < unsigned int > : : const_iterator iter = autoPtrVarId . find ( tok - > varId ( ) ) ;
if ( iter ! = autoPtrVarId . end ( ) ) {
autoPointerMallocError ( tok , tok - > strAt ( 4 ) ) ;
}
2011-06-16 20:26:00 +02:00
}
}
}
}
void CheckStl : : autoPointerError ( const Token * tok )
{
reportError ( tok , Severity : : style , " useAutoPointerCopy " ,
2012-10-14 11:16:48 +02:00
" Copying 'auto_ptr' pointer to another does not create two equal objects since one has lost its ownership of the pointer. \n "
" 'std::auto_ptr' has semantics of strict ownership, meaning that the 'auto_ptr' instance is the sole entity responsible for the object's lifetime. If an 'auto_ptr' is copied, the source looses the reference. "
2011-06-16 20:26:00 +02:00
) ;
}
void CheckStl : : autoPointerContainerError ( const Token * tok )
{
reportError ( tok , Severity : : error , " useAutoPointerContainer " ,
2012-10-14 11:16:48 +02:00
" You can randomly lose access to pointers if you store 'auto_ptr' pointers in an STL container. \n "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" An element of container must be able to be copied but 'auto_ptr' does not fulfill this requirement. You should consider to use 'shared_ptr' or 'unique_ptr'. It is suitable for use in containers, because they no longer copy their values, they move them. " , CWE664 , false
2011-06-16 20:26:00 +02:00
) ;
}
void CheckStl : : autoPointerArrayError ( const Token * tok )
{
reportError ( tok , Severity : : error , " useAutoPointerArray " ,
" Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with operator 'new[]'. \n "
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
" Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes arrays, which are allocated by operator 'new[]' and must be deallocated by operator 'delete[]'. " , CWE664 , false
2011-06-16 20:26:00 +02:00
) ;
}
2015-03-19 06:41:54 +01:00
void CheckStl : : autoPointerMallocError ( const Token * tok , const std : : string & allocFunction )
{
const std : : string summary = " Object pointed by an 'auto_ptr' is destroyed using operator 'delete'. You should not use 'auto_ptr' for pointers obtained with function ' " + allocFunction + " '. " ;
const std : : string verbose = summary + " This means that you should only use 'auto_ptr' for pointers obtained with operator 'new'. This excludes use C library allocation functions (for example ' " + allocFunction + " '), which must be deallocated by the appropriate C library function. " ;
reportError ( tok , Severity : : error , " useAutoPointerMalloc " , summary + " \n " + verbose ) ;
}
2015-06-10 21:14:17 +02:00
namespace {
2015-11-30 22:13:49 +01:00
const std : : set < std : : string > stl_containers_with_empty_and_clear = make_container < std : : set < std : : string > > ( ) < <
2015-06-10 21:14:17 +02:00
" deque " < < " forward_list " < < " list " < <
" map " < < " multimap " < < " multiset " < < " set " < < " string " < <
" unordered_map " < < " unordered_multimap " < < " unordered_multiset " < <
" unordered_set " < < " vector " < < " wstring " ;
}
2011-10-24 23:25:23 +02:00
void CheckStl : : uselessCalls ( )
{
2015-04-10 14:18:52 +02:00
const bool printPerformance = _settings - > isEnabled ( " performance " ) ;
const bool printWarning = _settings - > isEnabled ( " warning " ) ;
if ( ! printPerformance & & ! printWarning )
return ;
2014-01-30 22:09:24 +01:00
2012-08-10 15:26:07 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-16 06:50:49 +01:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2015-04-10 14:18:52 +02:00
if ( printWarning & & Token : : Match ( tok , " %var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %name% [,)] " ) & &
2014-07-23 15:06:27 +02:00
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) ) {
2016-02-02 20:26:02 +01:00
const Variable * var = tok - > variable ( ) ;
if ( ! var | | ! var - > isStlType ( ) )
continue ;
2012-11-16 06:50:49 +01:00
uselessCallsReturnValueError ( tok - > tokAt ( 4 ) , tok - > str ( ) , tok - > strAt ( 2 ) ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% . swap ( %name% ) " ) & &
2014-07-23 15:06:27 +02:00
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) ) {
2016-02-02 20:26:02 +01:00
const Variable * var = tok - > variable ( ) ;
if ( ! var | | ! var - > isStlType ( ) )
continue ;
2012-11-16 06:50:49 +01:00
uselessCallsSwapError ( tok , tok - > str ( ) ) ;
2015-04-10 14:18:52 +02:00
} else if ( printPerformance & & Token : : Match ( tok , " %var% . substr ( " ) & &
2015-11-20 11:20:23 +01:00
tok - > variable ( ) & & tok - > variable ( ) - > isStlStringType ( ) ) {
2016-02-02 20:26:02 +01:00
if ( Token : : Match ( tok - > tokAt ( 4 ) , " 0| ) " ) ) {
2012-08-10 15:26:07 +02:00
uselessCallsSubstrError ( tok , false ) ;
2016-02-02 20:26:02 +01:00
} else if ( tok - > strAt ( 4 ) = = " 0 " & & tok - > linkAt ( 3 ) - > strAt ( - 1 ) = = " npos " ) {
2014-04-13 19:04:35 +02:00
if ( ! tok - > linkAt ( 3 ) - > previous ( ) - > variable ( ) ) // Make sure that its no variable
2012-11-16 06:50:49 +01:00
uselessCallsSubstrError ( tok , false ) ;
2014-04-13 19:04:35 +02:00
} else if ( Token : : simpleMatch ( tok - > linkAt ( 3 ) - > tokAt ( - 2 ) , " , 0 ) " ) )
2012-11-16 06:50:49 +01:00
uselessCallsSubstrError ( tok , true ) ;
2015-04-10 14:18:52 +02:00
} else if ( printWarning & & Token : : Match ( tok , " [{};] %var% . empty ( ) ; " ) & &
2014-01-30 22:09:24 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlType ( stl_containers_with_empty_and_clear ) )
2012-11-16 06:50:49 +01:00
uselessCallsEmptyError ( tok - > next ( ) ) ;
else if ( Token : : Match ( tok , " [{};] std :: remove|remove_if|unique ( " ) & & tok - > tokAt ( 5 ) - > nextArgument ( ) )
uselessCallsRemoveError ( tok - > next ( ) , tok - > strAt ( 3 ) ) ;
}
2011-10-24 23:25:23 +02:00
}
}
2011-10-31 21:32:30 +01:00
void CheckStl : : uselessCallsReturnValueError ( const Token * tok , const std : : string & varname , const std : : string & function )
2011-10-24 23:25:23 +02:00
{
std : : ostringstream errmsg ;
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. " ;
2011-10-24 23:25:23 +02:00
reportError ( tok , Severity : : warning , " uselessCallsCompare " , errmsg . str ( ) ) ;
}
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
{
std : : ostringstream errmsg ;
2011-10-31 21:32:30 +01:00
errmsg < < " It is inefficient to swap a object with itself by calling ' " < < varname < < " .swap( " < < varname < < " )' \n "
< < " The 'swap()' function has no logical effect when given itself as parameter "
< < " ( " < < varname < < " .swap( " < < varname < < " )). As it is currently the "
2012-10-14 11:16:48 +02:00
< < " code is inefficient. Is the object or the parameter wrong here? " ;
2011-10-24 23:25:23 +02:00
reportError ( tok , Severity : : performance , " uselessCallsSwap " , errmsg . str ( ) ) ;
}
2012-04-04 19:40:28 +02:00
void CheckStl : : uselessCallsSubstrError ( const Token * tok , bool empty )
2011-10-24 23:25:23 +02:00
{
2012-04-04 19:40:28 +02:00
if ( empty )
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : performance , " uselessCallsSubstr " , " Ineffective call of function 'substr' because it returns an empty string. " ) ;
2012-04-04 19:40:28 +02:00
else
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : performance , " uselessCallsSubstr " , " Ineffective call of function 'substr' because it returns a copy of the object. Use operator= instead. " ) ;
2011-10-24 23:25:23 +02:00
}
2012-07-12 12:23:52 +02:00
void CheckStl : : uselessCallsEmptyError ( const Token * tok )
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : warning , " uselessCallsEmpty " , " Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead? " ) ;
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
{
2012-09-07 14:23:32 +02:00
reportError ( tok , Severity : : warning , " uselessCallsRemove " , " Return value of std:: " + function + " () ignored. Elements remain in container. \n "
" The return value of std:: " + function + " () is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. "
2012-08-21 11:30:27 +02:00
" Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them. " ) ;
}
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 ( )
{
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
// Iterate over "if", "while", and "for" conditions where there may
// be an iterator that is dereferenced before being checked for validity.
const std : : list < Scope > & scopeList = _tokenizer - > getSymbolDatabase ( ) - > scopeList ;
for ( std : : list < Scope > : : const_iterator i = scopeList . begin ( ) ; i ! = scopeList . end ( ) ; + + i ) {
2014-07-02 16:16:19 +02:00
if ( i - > type = = Scope : : eIf | | i - > type = = Scope : : eDo | | i - > type = = Scope : : eWhile | | i - > type = = Scope : : eFor ) {
2013-04-05 06:14:59 +02:00
const Token * const tok = i - > classDef ;
const Token * startOfCondition = tok - > next ( ) ;
2014-07-02 16:16:19 +02:00
if ( i - > type = = Scope : : eDo )
2014-04-12 19:54:31 +02:00
startOfCondition = startOfCondition - > link ( ) - > tokAt ( 2 ) ;
2015-04-06 17:23:48 +02:00
if ( ! startOfCondition ) // ticket #6626 invalid code
continue ;
2013-04-05 06:14:59 +02:00
const Token * endOfCondition = startOfCondition - > link ( ) ;
if ( ! endOfCondition )
continue ;
// For "for" loops, only search between the two semicolons
if ( i - > type = = Scope : : eFor ) {
2014-03-14 16:26:37 +01:00
startOfCondition = Token : : findsimplematch ( tok - > tokAt ( 2 ) , " ; " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
if ( ! startOfCondition )
continue ;
2014-03-14 16:26:37 +01:00
endOfCondition = Token : : findsimplematch ( startOfCondition - > next ( ) , " ; " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
if ( ! endOfCondition )
continue ;
}
// Only consider conditions composed of all "&&" terms and
// conditions composed of all "||" terms
const bool isOrExpression =
2013-04-08 11:26:58 +02:00
Token : : findsimplematch ( startOfCondition , " || " , endOfCondition ) ! = 0 ;
2013-04-05 06:14:59 +02:00
const bool isAndExpression =
2013-04-08 11:26:58 +02:00
Token : : findsimplematch ( startOfCondition , " && " , endOfCondition ) ! = 0 ;
2013-04-05 06:14:59 +02:00
// Look for a check of the validity of an iterator
const Token * validityCheckTok = 0 ;
if ( ! isOrExpression & & isAndExpression ) {
validityCheckTok =
2015-01-31 10:50:39 +01:00
Token : : findmatch ( startOfCondition , " && %var% != %name% . end|rend|cend|crend ( ) " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
} else if ( isOrExpression & & ! isAndExpression ) {
validityCheckTok =
2015-01-31 10:50:39 +01:00
Token : : findmatch ( startOfCondition , " %oror% %var% == %name% . end|rend|cend|crend ( ) " , endOfCondition ) ;
2013-04-05 06:14:59 +02:00
}
if ( ! validityCheckTok )
continue ;
const unsigned int iteratorVarId = validityCheckTok - > next ( ) - > varId ( ) ;
// If the iterator dereference is to the left of the check for
// the iterator's validity, report an error.
const Token * const dereferenceTok =
Token : : findmatch ( startOfCondition , " * %varid% " , validityCheckTok , iteratorVarId ) ;
if ( dereferenceTok )
dereferenceInvalidIteratorError ( dereferenceTok , dereferenceTok - > strAt ( 1 ) ) ;
}
}
}
void CheckStl : : dereferenceInvalidIteratorError ( const Token * deref , const std : : string & iterName )
{
reportError ( deref , Severity : : warning ,
" derefInvalidIterator " , " Possible dereference of an invalid iterator: " + iterName + " \n " +
" Make sure to check that the iterator is valid before dereferencing it - not after. " ) ;
}
2014-03-03 18:27:45 +01:00
2015-11-20 14:15:00 +01:00
void CheckStl : : readingEmptyStlContainer_parseUsage ( const Token * tok , const Library : : Container * container , std : : map < unsigned int , const Library : : Container * > & empty , bool noerror )
2015-05-02 13:54:13 +02:00
{
// Check for various conditions for the way stl containers and variables can be used
if ( tok - > strAt ( 1 ) = = " = " | | ( tok - > strAt ( 1 ) = = " [ " & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ] = " ) ) ) {
// Assignment (LHS)
empty . erase ( tok - > varId ( ) ) ;
} else if ( Token : : Match ( tok , " %name% [ " ) ) {
// Access through operator[]
2015-11-20 14:15:00 +01:00
if ( ! container - > arrayLike_indexOp ) { // operator[] inserts an element if used on a std::map
2015-05-02 13:54:13 +02:00
if ( ! noerror & & tok - > strAt ( - 1 ) = = " = " )
readingEmptyStlContainerError ( tok ) ;
empty . erase ( tok - > varId ( ) ) ;
} else if ( ! noerror )
readingEmptyStlContainerError ( tok ) ;
} else if ( Token : : Match ( tok , " %name% . %type% ( " ) ) {
// Member function call
2016-07-10 10:47:53 +02:00
const Library : : Container : : Action action = container - > getAction ( tok - > strAt ( 2 ) ) ;
if ( ( action = = Library : : Container : : FIND | | action = = Library : : Container : : ERASE | | action = = Library : : Container : : POP | | action = = Library : : Container : : CLEAR ) & & ! noerror ) {
readingEmptyStlContainerError ( tok ) ;
return ;
}
const Token * parent = tok - > tokAt ( 3 ) - > astParent ( ) ;
const Library : : Container : : Yield yield = container - > getYield ( tok - > strAt ( 2 ) ) ;
bool yieldsIterator = ( yield = = Library : : Container : : ITERATOR | | yield = = Library : : Container : : START_ITERATOR | | yield = = Library : : Container : : END_ITERATOR ) ;
2015-11-20 14:15:00 +01:00
if ( yield ! = Library : : Container : : NO_YIELD & &
2016-07-10 10:47:53 +02:00
( ! parent | | Token : : Match ( parent , " %cop%|* " ) | | parent - > isAssignmentOp ( ) | | ! yieldsIterator ) ) { // These functions read from the container
if ( ! noerror & & ( ! yieldsIterator | | ! parent | | ! parent - > isAssignmentOp ( ) ) )
2015-05-02 13:54:13 +02:00
readingEmptyStlContainerError ( tok ) ;
2016-07-10 10:47:53 +02:00
} else
empty . erase ( tok - > varId ( ) ) ;
2015-05-02 13:54:13 +02:00
} else if ( tok - > strAt ( - 1 ) = = " = " ) {
// Assignment (RHS)
if ( ! noerror )
readingEmptyStlContainerError ( tok ) ;
} else {
// Unknown usage. Assume it is initialized.
empty . erase ( tok - > varId ( ) ) ;
}
}
2014-03-03 18:27:45 +01:00
void CheckStl : : readingEmptyStlContainer ( )
{
if ( ! _settings - > isEnabled ( " style " ) )
return ;
2014-03-18 12:38:22 +01:00
if ( ! _settings - > inconclusive )
2014-03-03 18:27:45 +01:00
return ;
2015-11-20 14:15:00 +01:00
std : : map < unsigned int , const Library : : Container * > emptyContainer ;
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01:00
const std : : list < Scope > & scopeList = _tokenizer - > getSymbolDatabase ( ) - > scopeList ;
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01:00
for ( std : : list < Scope > : : const_iterator i = scopeList . begin ( ) ; i ! = scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eFunction )
continue ;
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01:00
for ( const Token * tok = i - > classStart - > next ( ) ; tok ! = i - > classEnd ; tok = tok - > next ( ) ) {
2015-05-02 14:01:31 +02:00
if ( Token : : Match ( tok , " for|while " ) ) { // Loops and end of scope clear the sets.
2015-05-02 13:54:13 +02:00
const Token * tok2 = tok - > linkAt ( 1 ) ;
if ( ! tok2 )
continue ;
tok2 = tok2 - > next ( ) ;
for ( const Token * end2 = tok2 - > link ( ) ; tok2 & & tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
if ( ! tok2 - > varId ( ) )
continue ;
2016-02-03 12:54:44 +01:00
const std : : map < unsigned int , const Library : : Container * > : : const_iterator container = emptyContainer . find ( tok2 - > varId ( ) ) ;
2015-11-20 14:15:00 +01:00
if ( container = = emptyContainer . end ( ) )
2015-05-02 13:54:13 +02:00
continue ;
2015-11-20 14:15:00 +01:00
readingEmptyStlContainer_parseUsage ( tok2 , container - > second , emptyContainer , true ) ;
2015-05-02 13:54:13 +02:00
}
2015-05-11 13:10:11 +02:00
} else if ( Token : : Match ( tok , " do|}|break|case " ) ) {
2015-11-20 14:15:00 +01:00
emptyContainer . clear ( ) ;
2014-03-18 12:38:22 +01:00
}
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01:00
if ( ! tok - > varId ( ) )
2014-03-03 18:27:45 +01:00
continue ;
2014-03-18 12:38:22 +01:00
// Check whether a variable should be marked as "empty"
const Variable * var = tok - > variable ( ) ;
2015-11-20 14:15:00 +01:00
if ( var & & ! var - > isArrayOrPointer ( ) & & ! var - > typeStartToken ( ) - > isStandardType ( ) ) {
2014-03-18 12:38:22 +01:00
bool insert = false ;
2014-07-25 13:05:13 +02:00
if ( var - > nameToken ( ) = = tok & & var - > isLocal ( ) & & ! var - > isStatic ( ) ) { // Local variable declared
2014-03-18 12:38:22 +01:00
insert = ! Token : : Match ( tok - > tokAt ( 1 ) , " [(=] " ) ; // Only if not initialized
2015-11-29 10:49:10 +01:00
} else if ( Token : : Match ( tok , " %var% . clear ( ) ; " )) {
2014-03-18 12:38:22 +01:00
insert = true ;
}
2014-03-03 19:00:32 +01:00
2014-03-18 12:38:22 +01:00
if ( insert ) {
2015-11-20 14:15:00 +01:00
const Library : : Container * container = _settings - > library . detectContainer ( var - > typeStartToken ( ) ) ;
if ( container )
emptyContainer [ var - > declarationId ( ) ] = container ;
2014-03-18 12:38:22 +01:00
continue ;
2014-03-03 18:27:45 +01:00
}
}
2016-02-03 12:54:44 +01:00
const std : : map < unsigned int , const Library : : Container * > : : const_iterator container = emptyContainer . find ( tok - > varId ( ) ) ;
2015-11-20 14:15:00 +01:00
if ( container = = emptyContainer . end ( ) )
2014-03-18 12:38:22 +01:00
continue ;
2014-03-03 18:27:45 +01:00
2015-11-20 14:15:00 +01:00
readingEmptyStlContainer_parseUsage ( tok , container - > second , emptyContainer , false ) ;
2014-03-03 18:27:45 +01:00
}
2015-11-20 14:15:00 +01:00
emptyContainer . clear ( ) ;
2014-03-03 18:27:45 +01:00
}
}
void CheckStl : : readingEmptyStlContainerError ( const Token * tok )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : style , " reademptycontainer " , " Reading from empty STL container ' " + ( tok ? tok - > str ( ) : std : : string ( " var " ) ) + " ' " , CWE ( 0U ) , true ) ;
2014-03-03 18:27:45 +01:00
}