2009-02-10 20:40:21 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and 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"
2010-10-05 20:54:13 +02:00
# include "executionpath.h"
2011-03-08 01:49:43 +01:00
# include "symboldatabase.h"
2014-04-12 23:39:52 +02:00
# include "checknullpointer.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
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 )
{
2010-05-13 22:14:29 +02:00
reportError ( tok , Severity : : error , " invalidIterator1 " , " Invalid iterator: " + iteratorName ) ;
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 )
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " iterators " , " Same iterator is used with different containers ' " + container1 + " ' and ' " + container2 + " '. " ) ;
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. "
" Dereferencing or comparing it with another iterator is invalid operation. " ) ;
} else {
reportError ( deref , Severity : : error , " eraseDereference " ,
" Invalid iterator ' " + itername + " ' used. \n "
" The iterator ' " + itername + " ' is invalid before being assigned. "
" Dereferencing or comparing it with another iterator is invalid operation. " ) ;
}
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 )
{
while ( Token : : Match ( tok , " %var% . " ) )
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 ( ) ;
2010-12-30 22:36:25 +01:00
// Using same iterator against different containers.
// for (it = foo.begin(); it != bar.end(); ++it)
2012-08-20 13:57:24 +02:00
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
2014-04-09 10:32:56 +02:00
if ( var - > typeEndToken ( ) - > str ( ) = = " auto " & & ! Token : : Match ( var - > typeEndToken ( ) , " auto %var% ; %var% = %var% . begin|end ( ) " ) )
continue ;
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
2012-08-20 13:57:24 +02:00
bool validIterator = Token : : Match ( var - > nameToken ( ) - > next ( ) , " [(=] " ) ;
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?
2013-07-20 12:31:04 +02:00
if ( Token : : Match ( tok2 , " %varid% !=|== %var% . 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?
2011-12-17 11:21:34 +01:00
else if ( Token : : Match ( tok2 , " %var% . insert|erase ( *| %varid% ) | , " , iteratorId)) {
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 ;
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
2013-10-26 17:48:20 +02:00
else if ( Token : : Match ( tok2 , " %varid% = %var% . " , iteratorId ) & &
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
2012-08-20 13:57:24 +02:00
else if ( Token : : Match ( tok2 , " %varid% = %var% . begin|rbegin|cbegin|crbegin|find ( " , iteratorId ) ) {
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
2012-09-06 16:10:51 +02:00
else if ( Token : : Match ( tok2 , " %varid% = %any% " , 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 ( ) ;
2011-10-13 20:53:06 +02:00
} else if ( ! validIterator & & Token : : Match ( tok2 , " %varid% . %var% " , 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 )
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " mismatchingContainers " , " Iterators of different containers are used together. " ) ;
2009-10-18 18:42:01 +02:00
}
void CheckStl : : mismatchingContainers ( )
{
2011-10-18 21:55:41 +02:00
static const char * const algorithm2_strings [ ] = { // func(begin1, end1
2012-04-17 12:54:01 +02:00
" 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 " ,
" unique_copy " , " upper_bound "
2011-10-18 21:55:41 +02:00
} ;
static const char * const algorithm22_strings [ ] = { // 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 "
} ;
static const char * const algorithm1x1_strings [ ] = { // func(begin1, x, end1
" inplace_merge " , " nth_element " , " partial_sort " , " rotate " , " rotate_copy "
} ;
static const std : : set < std : : string > algorithm2 ( algorithm2_strings , & algorithm2_strings [ sizeof ( algorithm2_strings ) / sizeof ( * algorithm2_strings ) ] ) ;
static const std : : set < std : : string > algorithm22 ( algorithm22_strings , & algorithm22_strings [ sizeof ( algorithm22_strings ) / sizeof ( * algorithm22_strings ) ] ) ;
static const std : : set < std : : string > algorithm1x1 ( algorithm1x1_strings , & algorithm1x1_strings [ sizeof ( algorithm1x1_strings ) / sizeof ( * algorithm1x1_strings ) ] ) ;
static const std : : string iteratorBeginFuncPattern = " begin|cbegin|rbegin|crbegin " ;
static const std : : string iteratorEndFuncPattern = " end|cend|rend|crend " ;
2012-04-17 12:21:41 +02:00
static const std : : string pattern1x1_1 = " %var% . " + iteratorBeginFuncPattern + " ( ) , " ;
static const std : : string pattern1x1_2 = " %var% . " + iteratorEndFuncPattern + " ( ) ,|) " ;
static const std : : string pattern2 = pattern1x1_1 + pattern1x1_2 ;
2011-10-18 21:55:41 +02:00
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
}
}
}
2009-02-10 20:56:00 +01:00
void CheckStl : : stlOutOfBounds ( )
{
2014-01-30 05:26:48 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_bounded_container [ ] = {
" array " , " basic_string " , " deque " , " string " , " vector " , " wstring "
} ;
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
if ( ( i - > type ! = Scope : : eFor & & i - > type ! = Scope : : eWhile & & i - > type ! = Scope : : eIf & & i - > type ! = Scope : : eElseIf ) | | ! tok )
2009-02-10 20:56:00 +01:00
continue ;
2013-03-04 19:59:46 +01:00
if ( i - > type = = Scope : : eElseIf )
tok = tok - > tokAt ( 2 ) ;
else if ( i - > type = = Scope : : eFor )
tok = Token : : findsimplematch ( tok - > tokAt ( 2 ) , " ; " ) ;
else
tok = tok - > next ( ) ;
2010-10-15 18:21:53 +02:00
// check if the for loop condition is wrong
2013-03-04 19:59:46 +01:00
if ( Token : : Match ( tok , " ;|( %var% <= %var% . size|length ( ) ;|)|%oror% " ) ) {
// Is it a vector?
const Variable * container = tok - > tokAt ( 3 ) - > variable ( ) ;
if ( ! container )
continue ;
2014-01-30 05:26:48 +01:00
if ( ! container - > isStlType ( stl_bounded_container ) )
2013-03-04 19:59:46 +01:00
continue ;
// variable id for loop variable.
2013-07-20 12:31:04 +02:00
const unsigned int numId = tok - > next ( ) - > varId ( ) ;
2013-03-04 19:59:46 +01:00
// variable id for the container variable
2013-07-20 12:31:04 +02:00
const unsigned int declarationId = container - > declarationId ( ) ;
2013-03-04 19:59:46 +01:00
for ( const Token * tok3 = tok - > tokAt ( 8 ) ; tok3 & & tok3 ! = i - > classEnd ; tok3 = tok3 - > next ( ) ) {
2013-07-20 12:31:04 +02:00
if ( tok3 - > varId ( ) = = declarationId ) {
2013-03-04 19:59:46 +01:00
if ( Token : : Match ( tok3 - > next ( ) , " . size|length ( ) " ) )
break ;
else if ( Token : : Match ( tok3 - > next ( ) , " [ %varid% ] " , numId ) )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 2 ) , tok3 - > str ( ) , false ) ;
else if ( Token : : Match ( tok3 - > next ( ) , " . at ( %varid% ) " , numId ) )
stlOutOfBoundsError ( tok3 , tok3 - > strAt ( 4 ) , tok3 - > str ( ) , true ) ;
2009-05-06 21:55:04 +02:00
}
2009-02-10 20:56:00 +01:00
}
2013-03-04 19:59:46 +01:00
break ;
2009-02-10 20:56:00 +01:00
}
}
2009-03-21 14:20:10 +01:00
}
2009-02-10 20:56:00 +01:00
2009-03-21 14:20:10 +01:00
// Error message for bad iterator usage..
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 )
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " When " + num + " == " + var + " .size(), " + var + " .at( " + num + " ) is out of bounds. " ) ;
2012-02-25 12:43:27 +01:00
else
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " stlOutOfBounds " , " When " + num + " == " + var + " .size(), " + var + " [ " + num + " ] is out of bounds. " ) ;
2009-02-10 20:56:00 +01:00
}
2009-02-11 06:08:29 +01:00
2010-10-05 20:54:13 +02:00
/**
* @ brief % Check for invalid iterator usage after erase / insert / etc
*/
2011-10-13 20:53:06 +02:00
class EraseCheckLoop : public ExecutionPath {
2010-10-05 20:54:13 +02:00
public :
2011-10-13 20:53:06 +02:00
static void checkScope ( CheckStl * checkStl , const Token * it ) {
2010-10-05 20:54:13 +02:00
const Token * tok = it ;
// Search for the start of the loop body..
2012-04-21 23:05:37 +02:00
while ( 0 ! = ( tok = tok - > next ( ) ) ) {
2010-10-05 20:54:13 +02:00
if ( tok - > str ( ) = = " ( " )
2011-01-20 20:48:35 +01:00
tok = tok - > link ( ) ;
2010-10-05 20:54:13 +02:00
else if ( tok - > str ( ) = = " ) " )
2011-01-20 20:48:35 +01:00
break ;
// reassigning iterator in loop head
else if ( Token : : Match ( tok , " %var% = " ) & & tok - > str ( ) = = it - > str ( ) )
break ;
2010-10-05 20:54:13 +02:00
}
if ( ! Token : : simpleMatch ( tok , " ) { " ) )
return ;
2012-10-14 11:16:48 +02:00
EraseCheckLoop c ( checkStl , it - > varId ( ) , it ) ;
2010-10-05 20:54:13 +02:00
std : : list < ExecutionPath * > checks ;
checks . push_back ( c . copy ( ) ) ;
ExecutionPath : : checkScope ( tok - > tokAt ( 2 ) , checks ) ;
c . end ( checks , tok - > link ( ) ) ;
2011-10-13 20:53:06 +02:00
while ( ! checks . empty ( ) ) {
2010-10-05 20:54:13 +02:00
delete checks . back ( ) ;
checks . pop_back ( ) ;
}
}
private :
/** Startup constructor */
2012-10-14 11:16:48 +02:00
EraseCheckLoop ( Check * o , unsigned int varid , const Token * usetoken )
: ExecutionPath ( o , varid ) , eraseToken ( 0 ) , useToken ( usetoken ) {
2010-10-05 20:54:13 +02:00
}
/** @brief token where iterator is erased (non-zero => the iterator is invalid) */
const Token * eraseToken ;
2012-10-14 11:16:48 +02:00
/** @brief name of the iterator */
const Token * useToken ;
2010-10-05 20:54:13 +02:00
/** @brief Copy this check. Called from the ExecutionPath baseclass. */
2011-10-13 20:53:06 +02:00
ExecutionPath * copy ( ) {
2010-10-05 20:54:13 +02:00
return new EraseCheckLoop ( * this ) ;
}
/** @brief is another execution path equal? */
2011-10-13 20:53:06 +02:00
bool is_equal ( const ExecutionPath * e ) const {
2010-10-05 20:54:13 +02:00
const EraseCheckLoop * c = static_cast < const EraseCheckLoop * > ( e ) ;
return ( eraseToken = = c - > eraseToken ) ;
}
/** @brief no implementation => compiler error if used by accident */
void operator = ( const EraseCheckLoop & ) ;
/** @brief parse tokens */
2011-10-13 20:53:06 +02:00
const Token * parse ( const Token & tok , std : : list < ExecutionPath * > & checks ) const {
2010-10-15 18:21:53 +02:00
// bail out if there are assignments. We don't check the assignments properly.
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " [;{}] %var% = " ) | | Token : : Match ( & tok , " = %var% ; " ) ) {
2010-10-05 20:54:13 +02:00
ExecutionPath : : bailOutVar ( checks , tok . next ( ) - > varId ( ) ) ;
}
2010-10-15 18:21:53 +02:00
// the loop stops here. Bail out all execution checks that reach
// this statement
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " [;{}] break ; " ) ) {
2010-10-05 20:54:13 +02:00
ExecutionPath : : bailOut ( checks ) ;
}
2010-10-15 18:21:53 +02:00
// erasing iterator => it is invalidated
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( & tok , " erase ( ++|--| %var% ) " ) ) {
2010-10-15 18:21:53 +02:00
// check if there is a "it = ints.erase(it);" pattern. if so
// the it is not invalidated.
2010-10-05 20:54:13 +02:00
const Token * token = & tok ;
2014-02-15 08:46:28 +01:00
while ( nullptr ! = ( token = token ? token - > previous ( ) : 0 ) ) {
2010-10-05 20:54:13 +02:00
if ( Token : : Match ( token , " [;{}] " ) )
break ;
else if ( token - > str ( ) = = " = " )
token = 0 ;
}
2010-10-15 18:21:53 +02:00
// the it is invalidated by the erase..
2011-10-13 20:53:06 +02:00
if ( token ) {
2010-10-15 18:21:53 +02:00
// get variable id for the iterator
2010-10-05 20:54:13 +02:00
unsigned int iteratorId = 0 ;
if ( tok . tokAt ( 2 ) - > isName ( ) )
iteratorId = tok . tokAt ( 2 ) - > varId ( ) ;
else
iteratorId = tok . tokAt ( 3 ) - > varId ( ) ;
2010-10-15 18:21:53 +02:00
// invalidate this iterator in the corresponding checks
2011-10-13 20:53:06 +02:00
for ( std : : list < ExecutionPath * > : : const_iterator it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it ) {
2010-10-05 20:54:13 +02:00
EraseCheckLoop * c = dynamic_cast < EraseCheckLoop * > ( * it ) ;
2011-10-13 20:53:06 +02:00
if ( c & & c - > varId = = iteratorId ) {
2010-10-05 20:54:13 +02:00
c - > eraseToken = & tok ;
}
}
}
}
2010-10-15 18:21:53 +02:00
// don't skip any tokens. return the token that we received.
2010-10-05 20:54:13 +02:00
return & tok ;
}
2010-10-09 07:15:34 +02:00
/**
* Parse condition . @ sa ExecutionPath : : parseCondition
* @ param tok first token in condition .
* @ param checks The execution paths . All execution paths in the list are executed in the current scope
* @ return true = > bail out all checking
* */
2011-10-13 20:53:06 +02:00
bool parseCondition ( const Token & tok , std : : list < ExecutionPath * > & checks ) {
2010-10-15 18:21:53 +02:00
// no checking of conditions.
2010-10-09 07:15:34 +02:00
( void ) tok ;
( void ) checks ;
return false ;
}
2010-10-05 20:54:13 +02:00
/** @brief going out of scope - all execution paths end */
2011-10-13 20:53:06 +02:00
void end ( const std : : list < ExecutionPath * > & checks , const Token * /*tok*/ ) const {
2010-10-15 18:21:53 +02:00
// check if there are any invalid iterators. If so there is an error.
2011-10-13 20:53:06 +02:00
for ( std : : list < ExecutionPath * > : : const_iterator it = checks . begin ( ) ; it ! = checks . end ( ) ; + + it ) {
2010-10-05 20:54:13 +02:00
EraseCheckLoop * c = dynamic_cast < EraseCheckLoop * > ( * it ) ;
2011-10-13 20:53:06 +02:00
if ( c & & c - > eraseToken ) {
2010-10-05 20:54:13 +02:00
CheckStl * checkStl = dynamic_cast < CheckStl * > ( c - > owner ) ;
2011-10-13 20:53:06 +02:00
if ( checkStl ) {
2012-10-14 11:16:48 +02:00
checkStl - > dereferenceErasedError ( c - > eraseToken , c - > useToken , c - > useToken - > str ( ) ) ;
2010-10-05 20:54:13 +02: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 ) {
const Token * const tok = i - > classDef ;
if ( tok & & i - > type = = Scope : : eFor ) {
2011-10-13 20:53:06 +02:00
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ; " ) {
if ( Token : : Match ( tok2 , " ; %var% != " ) ) {
2011-03-13 09:48:53 +01:00
// Get declaration token for var..
2013-02-05 06:46:26 +01:00
const Variable * variableInfo = tok2 - > next ( ) - > variable ( ) ;
2014-02-15 08:46:28 +01:00
const Token * decltok = variableInfo ? variableInfo - > typeEndToken ( ) : nullptr ;
2011-03-13 09:48:53 +01:00
// Is variable an iterator?
bool isIterator = false ;
if ( decltok & & Token : : Match ( decltok - > tokAt ( - 2 ) , " > :: iterator %varid% " , tok2 - > next ( ) - > varId ( ) ) )
isIterator = true ;
// If tok2->next() is an iterator, check scope
if ( isIterator )
2010-10-05 20:54:13 +02:00
EraseCheckLoop : : checkScope ( this , tok2 - > next ( ) ) ;
2010-05-08 20:11:15 +02:00
}
break ;
}
2011-10-22 22:38:03 +02:00
if ( Token : : Match ( tok2 , " %var% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( ) " ) & &
2011-11-13 13:10:59 +01:00
tok2 - > str ( ) = = tok2 - > strAt ( 8 ) & &
tok2 - > strAt ( 2 ) = = tok2 - > strAt ( 10 ) ) {
2010-10-05 20:54:13 +02:00
EraseCheckLoop : : checkScope ( this , tok2 ) ;
2009-02-11 06:08:29 +01:00
break ;
}
}
}
2011-12-09 22:28:10 +01:00
else if ( i - > type = = Scope : : eWhile & & Token : : Match ( tok , " while ( %var% != " ) ) {
2014-04-12 23:26:13 +02:00
const Variable * var = tok - > tokAt ( 2 ) - > variable ( ) ;
if ( var & & Token : : simpleMatch ( var - > typeEndToken ( ) - > tokAt ( - 2 ) , " > :: iterator " ) )
2010-10-05 20:54:13 +02:00
EraseCheckLoop : : checkScope ( this , tok - > tokAt ( 2 ) ) ;
2010-07-14 09:42:10 +02:00
}
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% [ " ) ) {
// Variable id for pointer
const unsigned int pointerId ( tok - > varId ( ) ) ;
// Variable id for the container variable
const unsigned int containerId ( tok - > tokAt ( 3 ) - > varId ( ) ) ;
if ( pointerId = = 0 | | containerId = = 0 )
continue ;
2009-04-28 21:18:02 +02:00
2012-11-16 06:50:49 +01:00
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..
if ( Token : : Match ( tok2 , " %varid% . push_front|push_back|insert|reserve|resize|clear " , containerId ) ) {
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 ( ) ) ;
if ( varId = = 0 )
continue ;
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
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " invalidIterator2 " , " After " + func + " (), the iterator ' " + iterator_name + " ' may be invalid. " ) ;
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
{
2012-10-14 11:16:48 +02:00
reportError ( tok , Severity : : error , " invalidPointer " , " Invalid pointer ' " + pointer_name + " ' after " + func + " (). " ) ;
2009-04-28 21:18:02 +02:00
}
2009-04-13 17:48:13 +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 ( ) ;
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 ( ) ) {
// Declaring iterator..
2013-01-17 10:15:01 +01:00
if ( tok - > str ( ) = = " < " & & Token : : Match ( tok - > previous ( ) , " bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset " ) ) {
2012-11-16 06:50:49 +01:00
const std : : string & container_name ( tok - > strAt ( - 1 ) ) ;
if ( tok - > link ( ) )
tok = tok - > link ( ) ;
else
2012-11-20 06:12:14 +01:00
continue ;
2009-05-17 18:58:32 +02:00
2012-11-16 06:50:49 +01:00
if ( Token : : Match ( tok , " > :: iterator|const_iterator %var% =|; " ) ) {
const unsigned int iteratorid ( tok - > tokAt ( 3 ) - > varId ( ) ) ;
if ( iteratorid = = 0 )
continue ;
2009-05-17 18:58:32 +02:00
2012-11-16 06:50:49 +01:00
// Using "iterator < ..." is not allowed
const Token * const end = tok - > scope ( ) - > classEnd ;
for ( const Token * tok2 = tok ; tok2 ! = end ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " !!* %varid% < " , iteratorid ) ) {
2013-02-10 07:43:09 +01:00
stlBoundariesError ( tok2 , container_name ) ;
2012-11-16 06:50:49 +01:00
} else if ( Token : : Match ( tok2 , " > %varid% !!. " , iteratorid ) ) {
2013-02-10 07:43:09 +01:00
stlBoundariesError ( tok2 , container_name ) ;
2012-11-16 06:50:49 +01:00
}
2009-05-17 18:58:32 +02:00
}
2009-04-13 17:48:13 +02:00
}
}
}
}
}
2012-05-05 15:21:27 +02:00
// Error message for bad boundary usage..
2013-02-10 07:43:09 +01:00
void CheckStl : : stlBoundariesError ( const Token * tok , const std : : string & container_name )
2009-04-13 17:48:13 +02:00
{
2013-02-10 07:43:09 +01:00
reportError ( tok , Severity : : error , " stlBoundaries " ,
2012-10-14 11:16:48 +02:00
" Dangerous iterator comparison using operator< on 'std:: " + container_name + " '. \n "
" Iterator of container 'std:: " + container_name + " ' compared with operator<. "
" This is dangerous since the order of items in the container is not guaranteed. "
" One should use operator!= instead to compare iterators. " ) ;
2009-04-13 17:48:13 +02:00
}
2009-11-02 20:24:38 +01:00
2013-02-14 19:14:20 +01:00
static bool if_findCompare ( const Token * const tokBack , bool str )
2012-06-07 19:33:18 +02:00
{
const Token * tok = tokBack ;
while ( tok & & tok - > str ( ) = = " ) " ) {
tok = tok - > next ( ) ;
2012-10-07 12:43:14 +02:00
2012-06-07 19:33:18 +02:00
if ( Token : : Match ( tok , " ) !!{ " ) & &
tok - > link ( ) - > previous ( ) & &
( Token : : Match ( tok - > link ( ) - > previous ( ) , " ,|==|!= " ) | |
tok - > link ( ) - > previous ( ) - > isName ( ) ) )
return true ;
}
2012-10-07 12:43:14 +02:00
if ( Token : : Match ( tok , " ,|==|!= " ) )
return true ;
2013-02-14 19:14:20 +01:00
if ( str & & tok - > isComparisonOp ( ) )
return true ;
2012-10-07 12:43:14 +02:00
if ( tok - > isArithmeticalOp ( ) ) // result is used in some calculation
return true ; // TODO: check if there is a comparison of the result somewhere
2012-06-07 19:33:18 +02:00
return false ;
}
2010-02-27 21:26:11 +01:00
void CheckStl : : if_find ( )
{
2013-03-03 11:41:59 +01:00
bool warning = _settings - > isEnabled ( " warning " ) ;
bool performance = _settings - > isEnabled ( " performance " ) ;
if ( ! warning & & ! performance )
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 ) {
2012-04-03 20:59:45 +02:00
if ( ( i - > type ! = Scope : : eIf & & i - > type ! = Scope : : eElseIf & & i - > type ! = Scope : : eWhile ) | | ! i - > classDef )
2011-12-09 22:28:10 +01:00
continue ;
2012-02-25 12:43:27 +01:00
const Token * tok = i - > classDef - > next ( ) ;
if ( tok - > str ( ) = = " if " )
tok = tok - > next ( ) ;
2011-12-09 22:28:10 +01:00
2012-04-14 18:36:19 +02:00
for ( const Token * const end = tok - > link ( ) ; tok ! = end ; tok = ( tok = = end ) ? end : tok - > next ( ) ) {
2012-04-03 20:59:45 +02:00
if ( Token : : Match ( tok , " &&|(|%oror% " ) )
2010-02-27 21:26:11 +01:00
tok = tok - > next ( ) ;
2012-04-03 20:59:45 +02:00
else
2012-01-02 22:52:50 +01:00
continue ;
2010-02-27 21:26:11 +01:00
2012-04-03 20:59:45 +02:00
while ( tok - > str ( ) = = " ( " )
tok = tok - > next ( ) ;
2010-02-28 08:03:22 +01:00
2012-04-03 20:59:45 +02:00
if ( tok - > str ( ) = = " ! " )
tok = tok - > next ( ) ;
2010-02-28 07:04:58 +01:00
2012-04-03 20:59:45 +02:00
if ( Token : : Match ( tok , " %var% . find ( " ) ) {
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > variable ( ) ;
2012-04-03 20:59:45 +02:00
if ( var ) {
// Is the variable a std::string or STL container?
const Token * decl = var - > typeStartToken ( ) ;
2013-02-05 06:46:26 +01:00
const unsigned int varid = tok - > varId ( ) ;
2012-01-02 22:52:50 +01:00
2013-02-14 19:14:20 +01:00
bool str = Token : : Match ( decl , " std :: string|wstring &| %varid% " , varid ) ;
if ( if_findCompare ( tok - > linkAt ( 3 ) , str ) )
continue ;
2012-04-03 20:59:45 +02:00
// stl container
2013-03-03 11:41:59 +01:00
if ( Token : : Match ( decl , " std :: %var% < %type% > &| %varid% " , varid ) & & warning )
2012-04-03 20:59:45 +02:00
if_findError ( tok , false ) ;
2013-03-03 11:41:59 +01:00
else if ( str & & performance )
2012-04-03 20:59:45 +02:00
if_findError ( tok , true ) ;
}
}
2012-01-02 22:52:50 +01:00
2012-04-03 20:59:45 +02:00
//check also for vector-like or pointer containers
else if ( Token : : Match ( tok , " * %var% " ) | | Token : : Match ( tok , " %var% [ " ) ) {
// goto %var%
if ( tok - > str ( ) = = " * " )
tok = tok - > next ( ) ;
2012-01-02 22:52:50 +01:00
2012-04-03 20:59:45 +02:00
const Token * tok2 = tok - > next ( ) ;
if ( tok2 - > str ( ) = = " [ " )
tok2 = tok2 - > link ( ) - > next ( ) ;
if ( ! Token : : simpleMatch ( tok2 , " . find ( " ) )
continue ;
2013-02-14 19:14:20 +01:00
if ( if_findCompare ( tok2 - > linkAt ( 2 ) , false ) )
2012-01-02 22:52:50 +01:00
continue ;
2013-02-05 06:46:26 +01:00
const Variable * var = tok - > variable ( ) ;
2012-04-03 20:59:45 +02:00
if ( var ) {
// Is the variable a std::string or STL container?
const Token * decl = var - > typeStartToken ( ) ;
2013-02-05 06:46:26 +01:00
const unsigned int varid = tok - > varId ( ) ;
2012-04-03 20:59:45 +02:00
//pretty bad limitation.. but it is there in order to avoid
//own implementations of 'find' or any container
2014-01-30 05:26:48 +01:00
if ( ! var - > isStlType ( ) )
2012-04-03 20:59:45 +02:00
continue ;
2012-01-02 22:52:50 +01:00
decl = decl - > tokAt ( 2 ) ;
2012-04-03 20:59:45 +02:00
if ( Token : : Match ( decl , " %var% < " ) ) {
2012-01-02 22:52:50 +01:00
decl = decl - > tokAt ( 2 ) ;
2012-04-03 20:59:45 +02:00
//stl-like
2013-03-03 11:41:59 +01:00
if ( Token : : Match ( decl , " std :: %var% < %type% > > &| %varid% " , varid ) & & warning )
2012-01-02 22:52:50 +01:00
if_findError ( tok , false ) ;
2012-04-03 20:59:45 +02:00
//not stl-like, then let's hope it's a pointer or an array
else if ( Token : : Match ( decl , " %type% > " ) ) {
decl = decl - > tokAt ( 2 ) ;
2013-03-03 11:41:59 +01:00
if ( ( Token : : Match ( decl , " * &| %varid% " , varid ) | |
Token : : Match ( decl , " &| %varid% [ ]| %any% ]| " , varid ) ) & & warning )
2012-04-03 20:59:45 +02:00
if_findError ( tok , false ) ;
}
2013-03-03 11:41:59 +01:00
else if ( Token : : Match ( decl , " std :: string|wstring > &| %varid% " , varid ) & & performance )
2012-04-03 20:59:45 +02:00
if_findError ( tok , true ) ;
2012-01-02 22:52:50 +01:00
}
2012-04-03 20:59:45 +02:00
else if ( decl & & decl - > str ( ) = = " string " ) {
decl = decl - > next ( ) ;
2013-03-04 14:30:01 +01:00
if ( ( Token : : Match ( decl , " * &| %varid% " , varid ) | |
Token : : Match ( decl , " &| %varid% [ ]| %any% ]| " , varid ) ) & & performance )
2012-04-03 20:59:45 +02:00
if_findError ( tok , true ) ;
}
2012-01-02 22:52:50 +01:00
}
}
2012-04-03 20:59:45 +02:00
else if ( Token : : Match ( tok , " std :: find|find_if ( " ) ) {
// check that result is checked properly
2013-03-03 11:41:59 +01:00
if ( ! if_findCompare ( tok - > linkAt ( 3 ) , false ) & & warning ) {
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, "
2012-10-14 11:16:48 +02:00
" you should compare with std::string::npos. " ) ;
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 ?
*/
static bool isContainerSizeSlow ( const Token * tok )
2009-12-19 15:24:59 +01:00
{
2014-01-28 17:31:23 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * stl_size_slow [ ] = {
" array " , " bitset " ,
" forward_list " , " hash_map " , " hash_multimap " , " hash_set " ,
" list " , " map " , " multimap " , " multiset " ,
" priority_queue " , " queue " , " set " , " stack " , " unordered_map " ,
" unordered_multimap " , " unordered_multiset " , " unordered_set "
} ;
2011-03-08 01:49:43 +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 ( ) ;
return var & & var - > isStlType ( stl_size_slow ) ;
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 ;
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 ( ) " ) | |
Token : : Match ( tok , " %var% . %var% . size ( ) " ) ) {
const Token * tok1 = tok ;
2013-02-05 06:46:26 +01:00
// get the variable
if ( tok - > strAt ( 2 ) ! = " size " )
2012-11-16 06:50:49 +01:00
tok1 = tok1 - > tokAt ( 2 ) ;
const Token * const end = tok1 - > tokAt ( 5 ) ;
2012-04-04 19:44:57 +02:00
2013-02-05 06:46:26 +01:00
if ( tok1 - > varId ( ) ) {
2012-11-16 06:50:49 +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 ==|>=|!=|< " ) ) ) {
2013-02-14 16:59:58 +01:00
if ( isContainerSizeSlow ( tok1 ) )
2012-11-16 06:50:49 +01:00
sizeError ( tok1 ) ;
}
2011-03-29 01:31:23 +02:00
2012-11-16 06:50:49 +01:00
// check for comparison to one
2013-02-16 11:50:25 +01:00
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 ( ) ) ) {
2013-02-14 16:59:58 +01:00
if ( isContainerSizeSlow ( tok1 ) )
2012-11-16 06:50:49 +01:00
sizeError ( tok1 ) ;
}
2012-11-04 16:15:26 +01:00
2012-11-16 06:50:49 +01:00
// check for using as boolean expression
else if ( ( Token : : Match ( tok - > tokAt ( - 2 ) , " if|while ( " ) & & end - > str ( ) = = " ) " ) | |
( tok - > previous ( ) - > type ( ) = = Token : : eLogicalOp & & Token : : Match ( end , " &&|)|,|;|%oror% " ) ) ) {
2013-02-14 16:59:58 +01:00
if ( isContainerSizeSlow ( tok1 ) )
2012-11-16 06:50:49 +01:00
sizeError ( tok1 ) ;
}
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 "
" guaranteed to take constant time. " ) ;
2009-12-19 15:24:59 +01:00
}
2010-09-16 18:49:23 +02:00
void CheckStl : : redundantCondition ( )
{
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 ) {
if ( i - > type ! = Scope : : eIf & & i - > type ! = Scope : : eElseIf )
continue ;
const Token * tok = i - > classDef - > tokAt ( 2 ) ;
if ( i - > type = = Scope : : eElseIf )
tok = tok - > next ( ) ;
if ( ! Token : : Match ( tok , " %var% . find ( %any% ) != %var% . end|rend|cend|crend ( ) ) { %var% . remove|erase ( %any% ) ; " ) )
continue ;
2010-09-16 18:49:23 +02:00
// Get tokens for the fields %var% 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
// Check if all the "%var%" fields are the same and if all the "%any%" are the same..
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. "
2012-10-14 11:16:48 +02:00
" It is safe to call the remove method on a non-existing element. " ) ;
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
2012-02-25 12:43:27 +01:00
if ( ! Token : : Match ( tok2 , " %var% = %var% . begin|rbegin|cbegin|crbegin ( ) ; %var% != %var% . end|rend|cend|crend ( ) ; ++| %var% ++| ) { " ) )
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 ( ) ) ;
if ( iteratorId = = 0 )
break ;
// 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 ;
else if ( Token : : Match ( tok3 , " %varid% = %var% . insert ( ++| %varid% ++| , " , iteratorId ) ) {
// 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
2012-10-14 11:16:48 +02:00
reportError ( callstack , Severity : : warning , " StlMissingComparison " , errmsg . str ( ) ) ;
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
}
2010-10-17 19:18:46 +02:00
void CheckStl : : string_c_str ( )
{
2014-01-30 22:09:24 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_string [ ] = {
" string " , " u16string " , " u32string " , " wstring "
} ;
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_string_stream [ ] = {
" istringstream " , " ostringstream " , " stringstream " , " wstringstream "
} ;
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 ;
if ( _settings - > isEnabled ( " performance " ) ) {
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
for ( const Token * tok = func - > argDef - > next ( ) ; tok ! = 0 ; tok = tok - > nextArgument ( ) ) {
2012-02-25 12:43:27 +01:00
numpar + + ;
2012-11-09 20:25:50 +01:00
if ( Token : : Match ( tok , " std :: string|wstring !!& " ) | | Token : : Match ( tok , " const std :: string|wstring " ) )
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 ( ) ) & &
2014-01-30 22:09:24 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlType ( stl_string ) ) {
2012-02-25 12:43:27 +01:00
string_c_strThrowError ( tok ) ;
2014-03-16 19:04:44 +01:00
} else if ( Token : : Match ( tok , " [;{}] %var% = %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 ) ;
} else if ( Token : : Match ( tok , " [;{}] %var% = %var% ( " ) & &
2014-03-16 19:04:44 +01:00
Token : : Match ( tok - > linkAt ( 4 ) , " ) . c_str|data ( ) ; " ) & &
2014-04-12 23:26:13 +02:00
tok - > tokAt ( 3 ) - > function ( ) & & Token : : Match ( tok - > tokAt ( 3 ) - > function ( ) - > retDef , " std :: string|wstring %var% " ) ) {
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 ) ;
2012-04-02 11:02:41 +02:00
} else if ( Token : : Match ( tok , " %var% ( !!) " ) & & c_strFuncParam . find ( tok - > str ( ) ) ! = c_strFuncParam . end ( ) & &
2012-08-26 10:56:46 +02:00
_settings - > isEnabled ( " performance " ) & & ! 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 ( ) ;
2014-01-30 22:09:24 +01:00
if ( var & & var - > isStlType ( stl_string ) ) {
2012-11-28 08:48:48 +01:00
string_c_strParam ( tok , i - > second ) ;
2014-01-30 22:09:24 +01:00
} else if ( Token : : Match ( tok2 - > tokAt ( - 9 ) , " %var% . str ( ) " ) ) { // Check ss.str().c_str() as parameter
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*
if ( returnType = = charPtr ) {
2014-03-16 19:04:44 +01:00
if ( Token : : Match ( tok , " return %var% . c_str|data ( ) ; " ) & & isLocal ( tok - > next ( ) ) & &
2014-01-30 22:09:24 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlType ( stl_string ) ) {
2011-12-17 11:21:34 +01:00
string_c_strError ( tok ) ;
2014-03-16 19:04:44 +01:00
} else if ( Token : : Match ( tok , " return %var% . str ( ) . c_str | data ( ) ; " ) && isLocal(tok->next()) &&
2014-01-30 22:09:24 +01:00
tok - > next ( ) - > variable ( ) & & tok - > next ( ) - > variable ( ) - > isStlType ( stl_string_stream ) ) {
2011-12-17 11:21:34 +01:00
string_c_strError ( tok ) ;
2012-11-06 19:54:52 +01:00
} else if ( Token : : Match ( tok , " return std :: string|wstring ( " ) & &
2014-03-16 19:04:44 +01:00
Token : : Match ( tok - > linkAt ( 4 ) , " ) . c_str|data ( ) ; " ) ) {
2011-12-17 11:21:34 +01:00
string_c_strError ( tok ) ;
2014-03-16 19:04:44 +01:00
} else if ( Token : : Match ( tok , " return %var% ( " ) & & Token : : Match ( tok - > linkAt ( 2 ) , " ) . c_str|data ( ) ; " )) {
2013-01-31 06:41:18 +01:00
const Function * func = tok - > next ( ) - > function ( ) ;
2012-11-06 19:54:52 +01:00
if ( func & & Token : : Match ( func - > tokenDef - > tokAt ( - 3 ) , " std :: string|wstring " ) )
2012-09-10 15:20:38 +02:00
string_c_strError ( tok ) ;
2011-10-26 22:14:47 +02:00
} else if ( Token : : simpleMatch ( tok , " return ( " ) & &
2014-03-16 19:04:44 +01:00
Token : : Match ( tok - > next ( ) - > link ( ) , " ) . c_str|data ( ) ; " ) ) {
2011-10-26 22:14:47 +02:00
// Check for "+ localvar" or "+ std::string(" inside the bracket
2012-02-25 12:43:27 +01:00
bool is_implicit_std_string = _settings - > inconclusive ;
2011-10-26 22:14:47 +02:00
const Token * search_end = tok - > next ( ) - > link ( ) ;
for ( const Token * search_tok = tok - > tokAt ( 2 ) ; 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 ( ) ) & &
search_tok - > next ( ) - > variable ( ) & & search_tok - > next ( ) - > variable ( ) - > isStlType ( stl_string ) ) {
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 )
2011-12-17 11:21:34 +01:00
string_c_strError ( tok ) ;
2010-10-19 20:21:58 +02:00
}
2010-10-17 19:18:46 +02:00
}
2012-02-25 12:43:27 +01:00
// Using c_str() to get the return value is redundant if the function returns std::string or const std::string&.
else if ( ( returnType = = stdString | | returnType = = stdStringConstRef ) & & _settings - > isEnabled ( " performance " ) ) {
if ( tok - > str ( ) = = " return " ) {
const Token * tok2 = Token : : findsimplematch ( tok - > next ( ) , " ; " ) ;
2014-03-16 19:04:44 +01:00
if ( Token : : Match ( tok2 - > tokAt ( - 4 ) , " . c_str|data ( ) " ) ) {
2012-11-29 07:10:56 +01:00
tok2 = tok2 - > tokAt ( - 5 ) ;
2014-01-30 22:09:24 +01:00
if ( tok2 - > variable ( ) & & tok2 - > variable ( ) - > isStlType ( stl_string ) ) { // return var.c_str();
2012-11-29 07:10:56 +01:00
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 "
2011-12-17 11:21:34 +01:00
" Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted. " ) ;
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 "
2012-10-14 11:16:48 +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. " ) ;
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. " ;
2012-02-25 12:43:27 +01:00
reportError ( tok , Severity : : performance , " stlcstrParam " , oss . str ( ) ) ;
}
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 ;
2012-04-17 12:54:01 +02:00
static 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 " ;
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 ) ;
if ( Token : : Match ( tok2 , " > %var% " ) ) {
const Token * tok3 = tok2 - > tokAt ( 2 ) ;
if ( Token : : Match ( tok3 , " ( new %type% " ) & & hasArrayEndParen ( tok3 ) ) {
autoPointerArrayError ( tok2 - > next ( ) ) ;
}
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 {
if ( Token : : Match ( tok , " %var% = %var% ; " ) ) {
if ( _settings - > isEnabled ( " style " ) ) {
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
}
}
2011-11-06 15:32:28 +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 ) ;
}
}
}
}
}
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 "
2011-06-16 20:26:00 +02: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. "
) ;
}
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 "
" 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[]'. "
) ;
}
2011-10-24 23:25:23 +02:00
void CheckStl : : uselessCalls ( )
{
2014-04-13 19:04:35 +02:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const stl_string [ ] = {
" string " , " u16string " , " u32string " , " wstring "
} ;
2014-01-30 22:09:24 +01:00
// THIS ARRAY MUST BE ORDERED ALPHABETICALLY
static const char * const 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 "
} ;
2012-07-12 12:23:52 +02:00
bool performance = _settings - > isEnabled ( " performance " ) ;
2013-03-03 11:41:59 +01:00
bool warning = _settings - > isEnabled ( " warning " ) ;
if ( ! performance & & ! warning )
2012-07-12 12:23:52 +02:00
return ;
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 ( ) ) {
if ( tok - > varId ( ) & & Token : : Match ( tok , " %var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %var% [,)] " ) & &
2013-03-03 11:41:59 +01:00
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) & & warning ) {
2012-11-16 06:50:49 +01:00
uselessCallsReturnValueError ( tok - > tokAt ( 4 ) , tok - > str ( ) , tok - > strAt ( 2 ) ) ;
} else if ( tok - > varId ( ) & & Token : : Match ( tok , " %var% . swap ( %var% ) " ) & &
tok - > varId ( ) = = tok - > tokAt ( 4 ) - > varId ( ) & & performance ) {
uselessCallsSwapError ( tok , tok - > str ( ) ) ;
2014-04-13 19:04:35 +02:00
} else if ( Token : : Match ( tok , " %var% . substr ( " ) & & performance & &
tok - > variable ( ) & & tok - > variable ( ) - > isStlType ( stl_string ) ) {
if ( Token : : Match ( tok - > tokAt ( 4 ) , " 0| ) " ) )
2012-08-10 15:26:07 +02:00
uselessCallsSubstrError ( tok , false ) ;
2014-04-13 19:04:35 +02:00
else if ( tok - > strAt ( 4 ) = = " 0 " & & tok - > linkAt ( 3 ) - > strAt ( - 1 ) = = " npos " ) {
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 ) ;
2014-01-30 22:09:24 +01:00
} else if ( Token : : Match ( tok , " [{};] %var% . empty ( ) ; " ) & & warning & &
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-04-12 19:54:31 +02:00
if ( i - > type = = Scope : : eIf | | i - > type = = Scope : : eElseIf | | 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-04-12 19:54:31 +02:00
if ( i - > type = = Scope : : eElseIf )
startOfCondition = startOfCondition - > next ( ) ;
else if ( i - > type = = Scope : : eDo )
startOfCondition = startOfCondition - > link ( ) - > tokAt ( 2 ) ;
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 =
Token : : findmatch ( startOfCondition , " && %var% != %var% . end|rend|cend|crend ( ) " , endOfCondition ) ;
} else if ( isOrExpression & & ! isAndExpression ) {
validityCheckTok =
Token : : findmatch ( startOfCondition , " %oror% %var% == %var% . end|rend|cend|crend ( ) " , endOfCondition ) ;
}
if ( ! validityCheckTok )
continue ;
const unsigned int iteratorVarId = validityCheckTok - > next ( ) - > varId ( ) ;
if ( ! iteratorVarId )
continue ;
// 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
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 ;
2014-03-18 12:38:22 +01:00
std : : set < unsigned int > empty_map ; // empty std::map-like instances of STL containers
std : : set < unsigned int > empty_nonmap ; // empty non-std::map-like instances of STL containers
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01:00
static const char * MAP_STL_CONTAINERS [ ] = { " map " , " multimap " , " unordered_map " , " unordered_multimap " } ;
static const char * NONMAP_STL_CONTAINERS [ ] = { " deque " , " forward_list " , " list " , " multiset " , " queue " , " set " , " stack " , " string " , " unordered_multiset " , " unordered_set " , " vector " } ;
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 ( ) ) {
if ( Token : : Match ( tok , " for|while|do|} " ) ) { // Loops and end of scope clear the sets.
empty_map . clear ( ) ;
empty_nonmap . clear ( ) ;
}
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 ( ) ;
if ( var ) {
bool insert = false ;
if ( tok - > variable ( ) - > nameToken ( ) = = tok & & var - > isLocal ( ) & & ! var - > isStatic ( ) ) { // Local variable declared
insert = ! Token : : Match ( tok - > tokAt ( 1 ) , " [(=] " ) ; // Only if not initialized
} else if ( Token : : Match ( tok , " %var% . clear ( ) ; " )) {
insert = true ;
}
2014-03-03 19:00:32 +01:00
2014-03-18 12:38:22 +01:00
if ( insert ) {
if ( var - > isStlType ( MAP_STL_CONTAINERS ) )
empty_map . insert ( var - > declarationId ( ) ) ;
else if ( var - > isStlType ( NONMAP_STL_CONTAINERS ) )
empty_nonmap . insert ( var - > declarationId ( ) ) ;
continue ;
2014-03-03 18:27:45 +01:00
}
}
2014-03-18 12:38:22 +01:00
bool map = empty_map . find ( tok - > varId ( ) ) ! = empty_map . end ( ) ;
if ( ! map & & empty_nonmap . find ( tok - > varId ( ) ) = = empty_nonmap . end ( ) )
continue ;
2014-03-03 18:27:45 +01:00
2014-03-18 12:38:22 +01: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)
if ( map )
empty_map . erase ( tok - > varId ( ) ) ;
else
empty_nonmap . erase ( tok - > varId ( ) ) ;
} else if ( Token : : Match ( tok , " %var% [ " ) ) {
// Access through operator[]
if ( map ) { // operator[] inserts an element, if used on a std::map
if ( tok - > strAt ( - 1 ) = = " = " )
readingEmptyStlContainerError ( tok ) ;
empty_map . erase ( tok - > varId ( ) ) ;
} else if ( ( map & & empty_map . find ( tok - > varId ( ) ) ! = empty_map . end ( ) ) | | ( ! map & & empty_nonmap . find ( tok - > varId ( ) ) ! = empty_nonmap . end ( ) ) )
readingEmptyStlContainerError ( tok ) ;
} else if ( Token : : Match ( tok , " %var% . %type% ( " ) ) {
// Member function call
if ( Token : : Match ( tok - > tokAt ( 2 ) , " find|at|data|c_str|back|front|empty|top|size|count " ) ) // These functions read from the container
readingEmptyStlContainerError ( tok ) ;
else if ( map )
empty_map . erase ( tok - > varId ( ) ) ;
else
empty_nonmap . erase ( tok - > varId ( ) ) ;
} else if ( tok - > strAt ( - 1 ) = = " = " ) {
// Assignment (RHS)
2014-03-03 18:27:45 +01:00
readingEmptyStlContainerError ( tok ) ;
2014-03-18 12:38:22 +01:00
} else {
// Unknown usage. Assume it is initialized.
if ( map )
empty_map . erase ( tok - > varId ( ) ) ;
else
empty_nonmap . erase ( tok - > varId ( ) ) ;
}
2014-03-03 18:27:45 +01:00
}
2014-03-18 12:38:22 +01:00
empty_map . clear ( ) ;
empty_nonmap . clear ( ) ;
2014-03-03 18:27:45 +01:00
}
}
void CheckStl : : readingEmptyStlContainerError ( const Token * tok )
{
reportError ( tok , Severity : : style , " reademptycontainer " , " Reading from empty STL container " , true ) ;
}