2009-01-26 20:14:46 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2013-01-01 17:29:08 +01:00
* Copyright ( C ) 2007 - 2013 Daniel Marjamäki and Cppcheck team .
2009-01-26 20:14:46 +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-01-26 20:14:46 +01:00
*/
//---------------------------------------------------------------------------
# include "checkother.h"
2009-05-02 10:45:15 +02:00
# include "mathlib.h"
2010-12-16 19:04:47 +01:00
# include "symboldatabase.h"
2010-10-31 12:31:11 +01:00
2010-04-02 20:23:37 +02:00
# include <cmath> // fabs()
2011-02-19 20:02:28 +01:00
# include <stack>
2011-11-29 19:46:36 +01:00
# include <algorithm> // find_if()
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
2009-03-20 18:16:21 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
CheckOther instance ;
2009-01-26 20:14:46 +01:00
}
2013-02-27 21:02:12 +01:00
//----------------------------------------------------------------------------------
// The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value.
// If this return value is stored in a character variable and then compared
// to compared to EOF, which is an integer, the comparison maybe be false.
//
// Reference:
// - Ticket #160
// - http://www.cplusplus.com/reference/cstdio/fgetc/
// - http://www.cplusplus.com/reference/cstdio/getc/
// - http://www.cplusplus.com/reference/cstdio/getchar/
2013-04-09 13:57:42 +02:00
// - http://www.cplusplus.com/reference/cstdio/ungetc/ ...
2013-02-27 21:02:12 +01:00
//----------------------------------------------------------------------------------
void CheckOther : : checkCastIntToCharAndBack ( )
{
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
2013-03-03 18:54:44 +01:00
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
std : : map < unsigned int , std : : string > vars ;
2013-02-27 21:02:12 +01:00
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc ( " ) ) {
2013-03-03 18:54:44 +01:00
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
vars [ tok - > varId ( ) ] = tok - > strAt ( 2 ) ;
2013-02-27 21:02:12 +01:00
}
} else if ( Token : : Match ( tok , " EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc ( " ) ) {
tok = tok - > tokAt ( 3 ) ;
if ( tok & & tok - > varId ( ) ) {
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
2013-03-01 12:42:04 +01:00
checkCastIntToCharAndBackError ( tok , tok - > strAt ( 2 ) ) ;
2013-02-27 21:02:12 +01:00
}
}
2013-03-04 14:25:35 +01:00
} else if ( Token : : Match ( tok , " EOF %comp% ( %var% = std :: cin . get ( " ) | | Token : : Match ( tok , " EOF %comp% ( %var% = cin . get ( " ) ) {
tok = tok - > tokAt ( 3 ) ;
if ( tok & & tok - > varId ( ) ) {
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
checkCastIntToCharAndBackError ( tok , " cin.get " ) ;
}
}
} else if ( Token : : Match ( tok , " %var% = std :: cin . get ( " ) | | Token : : Match ( tok , " %var% = cin . get ( " ) ) {
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
vars [ tok - > varId ( ) ] = " cin.get " ;
}
2013-02-27 21:02:12 +01:00
}
if ( Token : : Match ( tok , " %var% %comp% EOF " ) ) {
2013-03-03 18:54:44 +01:00
if ( vars . find ( tok - > varId ( ) ) ! = vars . end ( ) ) {
checkCastIntToCharAndBackError ( tok , vars [ tok - > varId ( ) ] ) ;
2013-02-27 21:02:12 +01:00
}
} else if ( Token : : Match ( tok , " EOF %comp% %var% " ) ) {
tok = tok - > tokAt ( 2 ) ;
2013-03-03 18:54:44 +01:00
if ( vars . find ( tok - > varId ( ) ) ! = vars . end ( ) ) {
checkCastIntToCharAndBackError ( tok , vars [ tok - > varId ( ) ] ) ;
2013-02-27 21:02:12 +01:00
}
}
}
}
}
void CheckOther : : checkCastIntToCharAndBackError ( const Token * tok , const std : : string & strFunctionName )
{
reportError (
tok ,
Severity : : warning ,
" checkCastIntToCharAndBack " ,
" Storing " + strFunctionName + " () return value in char variable and then comparing with EOF. \n "
" When saving " + strFunctionName + " () return value in char variable there is loss of precision. "
" When " + strFunctionName + " () returns EOF this value is truncated. Comparing the char "
" variable with EOF can have unexpected results. For instance a loop \" while (EOF != (c = " + strFunctionName + " ()); \" "
" loops forever on some compilers/platforms and on other compilers/platforms it will stop "
" when the file contains a matching character. "
) ;
}
2011-02-11 23:38:23 +01:00
//---------------------------------------------------------------------------
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2011-01-24 21:40:49 +01:00
void CheckOther : : clarifyCalculation ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-01-24 21:40:49 +01:00
return ;
2011-11-30 20:23:29 +01:00
2012-11-14 18:12:33 +01:00
const SymbolDatabase * 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 ( tok - > str ( ) = = " ? " ) {
// condition
const Token * cond = tok - > previous ( ) ;
if ( cond - > isName ( ) | | cond - > isNumber ( ) )
cond = cond - > previous ( ) ;
else if ( cond - > str ( ) = = " ) " )
cond = cond - > link ( ) - > previous ( ) ;
else
continue ;
2011-01-24 21:40:49 +01:00
2012-11-14 18:12:33 +01:00
if ( cond & & cond - > str ( ) = = " ! " )
cond = cond - > previous ( ) ;
2011-09-06 22:37:19 +02:00
2012-11-14 18:12:33 +01:00
if ( ! cond )
continue ;
2011-09-06 22:37:19 +02:00
2012-11-14 18:12:33 +01:00
// calculation
if ( ! cond - > isArithmeticalOp ( ) )
continue ;
2011-01-24 21:40:49 +01:00
2012-11-14 18:12:33 +01:00
const std : : string & op = cond - > str ( ) ;
cond = cond - > previous ( ) ;
2011-04-02 11:43:20 +02:00
2012-11-14 18:12:33 +01:00
// skip previous multiplications..
while ( cond & & cond - > previous ( ) ) {
if ( ( cond - > isName ( ) | | cond - > isNumber ( ) ) & & cond - > previous ( ) - > str ( ) = = " * " )
cond = cond - > tokAt ( - 2 ) ;
else if ( cond - > str ( ) = = " ) " )
cond = cond - > link ( ) - > previous ( ) ;
else
break ;
}
2011-01-24 21:40:49 +01:00
2012-11-14 18:12:33 +01:00
if ( ! cond )
continue ;
2011-01-24 21:40:49 +01:00
2012-11-14 18:12:33 +01:00
// first multiplication operand
if ( cond - > str ( ) = = " ) " ) {
2011-04-02 11:43:20 +02:00
clarifyCalculationError ( cond , op ) ;
2012-11-14 18:12:33 +01:00
} else if ( cond - > isName ( ) | | cond - > isNumber ( ) ) {
if ( Token : : Match ( cond - > previous ( ) , " return|=|+|-|,|( " ) | | cond - > strAt ( - 1 ) = = op )
clarifyCalculationError ( cond , op ) ;
}
2011-01-24 21:40:49 +01:00
}
}
}
}
2011-04-03 22:12:22 +02:00
void CheckOther : : clarifyCalculationError ( const Token * tok , const std : : string & op )
2011-01-24 21:40:49 +01:00
{
2011-04-02 11:43:20 +02:00
// suspicious calculation
2011-04-03 22:12:22 +02:00
const std : : string calc ( " 'a " + op + " b?c:d' " ) ;
2011-04-02 11:43:20 +02:00
// recommended calculation #1
2011-04-03 22:12:22 +02:00
const std : : string s1 ( " '(a " + op + " b)?c:d' " ) ;
2011-04-02 11:43:20 +02:00
// recommended calculation #2
2011-04-03 22:12:22 +02:00
const std : : string s2 ( " 'a " + op + " (b?c:d)' " ) ;
2011-04-02 11:43:20 +02:00
2011-01-24 21:40:49 +01:00
reportError ( tok ,
2011-03-09 22:20:14 +01:00
Severity : : style ,
2011-01-24 21:40:49 +01:00
" clarifyCalculation " ,
2012-09-16 19:44:02 +02:00
" Clarify calculation precedence for ' " + op + " ' and '?'. \n "
2011-04-02 11:43:20 +02:00
" Suspicious calculation. Please use parentheses to clarify the code. "
2012-09-16 19:44:02 +02:00
" The code ' " + calc + " ' should be written as either ' " + s1 + " ' or ' " + s2 + " '. " ) ;
2011-01-24 21:40:49 +01:00
}
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2011-03-09 22:20:14 +01:00
// Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))'
2011-08-19 00:14:15 +02:00
// Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))'
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2011-03-09 22:20:14 +01:00
void CheckOther : : clarifyCondition ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-03-09 22:20:14 +01:00
return ;
2011-08-19 13:40:54 +02:00
2012-06-23 07:29:49 +02:00
const bool isC = _tokenizer - > isC ( ) ;
2012-11-14 18:12:33 +01:00
const SymbolDatabase * 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% [=&|^] " ) ) {
for ( const Token * tok2 = tok - > tokAt ( 3 ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ( " | | tok2 - > str ( ) = = " [ " )
tok2 = tok2 - > link ( ) ;
else if ( tok2 - > type ( ) = = Token : : eComparisonOp ) {
// This might be a template
if ( ! isC & & tok2 - > link ( ) )
break ;
clarifyConditionError ( tok , tok - > strAt ( 2 ) = = " = " , false ) ;
2011-08-19 18:06:28 +02:00
break ;
2012-11-14 18:12:33 +01:00
} else if ( ! tok2 - > isName ( ) & & ! tok2 - > isNumber ( ) & & tok2 - > str ( ) ! = " . " )
break ;
}
2011-03-09 22:20:14 +01:00
}
}
}
2011-08-19 13:40:54 +02:00
// using boolean result in bitwise operation ! x [&|^]
2012-11-14 18:12:33 +01:00
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 ( ) ) {
2012-12-01 01:31:35 +01:00
if ( Token : : Match ( tok , " %comp%|! " ) ) {
2012-11-14 18:12:33 +01:00
if ( tok - > link ( ) ) // don't write false positives when templates are used
continue ;
2011-08-19 18:55:20 +02:00
2012-11-14 18:12:33 +01:00
const Token * tok2 = tok - > next ( ) ;
2011-08-19 18:06:28 +02:00
2012-11-14 18:12:33 +01:00
// Todo: There are false positives if '(' if encountered. It
// is assumed there is something like '(char *)&..' and therefore
// it bails out.
if ( Token : : Match ( tok2 , " (|& " ) )
2011-08-19 18:06:28 +02:00
continue ;
2012-11-14 18:12:33 +01:00
while ( tok2 & & ( tok2 - > isName ( ) | | tok2 - > isNumber ( ) | | Token : : Match ( tok2 , " .|(|[ " ) ) ) {
if ( Token : : Match ( tok2 , " (|[ " ) )
tok2 = tok2 - > link ( ) ;
tok2 = tok2 - > next ( ) ;
2012-04-08 18:07:11 +02:00
}
2012-11-14 18:12:33 +01:00
if ( Token : : Match ( tok2 , " [&|^] " ) ) {
// don't write false positives when templates are used
if ( Token : : Match ( tok2 , " &|* ,|> " ) | | Token : : simpleMatch ( tok2 - > previous ( ) , " const & " ) )
continue ;
// #3609 - CWinTraits<WS_CHILD|WS_VISIBLE>::..
if ( ! isC & & Token : : Match ( tok - > previous ( ) , " %var% < " ) ) {
const Token * tok3 = tok2 ;
while ( Token : : Match ( tok3 , " [&|^] %var% " ) )
tok3 = tok3 - > tokAt ( 2 ) ;
if ( Token : : Match ( tok3 , " ,|> " ) )
continue ;
}
clarifyConditionError ( tok , false , true ) ;
}
2011-08-19 13:40:54 +02:00
}
}
}
2011-03-09 22:20:14 +01:00
}
2011-08-19 13:40:54 +02:00
void CheckOther : : clarifyConditionError ( const Token * tok , bool assign , bool boolop )
2011-03-09 22:20:14 +01:00
{
2011-08-19 00:14:15 +02:00
std : : string errmsg ;
2011-08-19 13:40:54 +02:00
2011-08-19 00:14:15 +02:00
if ( assign )
2012-09-16 19:44:02 +02:00
errmsg = " Suspicious condition (assignment + comparison); Clarify expression with parentheses. " ;
2011-08-19 13:40:54 +02:00
else if ( boolop )
2012-09-16 19:44:02 +02:00
errmsg = " Boolean result is used in bitwise operation. Clarify expression with parentheses. \n "
2013-03-25 11:26:33 +01:00
" Suspicious expression. Boolean result is used in bitwise operation. The operator '!' "
2011-08-19 13:40:54 +02:00
" and the comparison operators have higher precedence than bitwise operators. "
" It is recommended that the expression is clarified with parentheses. " ;
2011-08-19 00:14:15 +02:00
else
2012-09-16 19:44:02 +02:00
errmsg = " Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses. \n "
2013-03-25 11:26:33 +01:00
" Suspicious condition. Comparison operators have higher precedence than bitwise operators. "
" Please clarify the condition with parentheses. " ;
2011-08-19 00:14:15 +02:00
2011-03-09 22:20:14 +01:00
reportError ( tok ,
Severity : : style ,
" clarifyCondition " ,
2011-08-19 00:14:15 +02:00
errmsg ) ;
2011-03-09 22:20:14 +01:00
}
2012-08-24 11:28:50 +02:00
//---------------------------------------------------------------------------
2013-02-10 07:43:09 +01:00
// Clarify (meaningless) statements like *foo++; with parentheses.
2012-08-24 11:28:50 +02:00
//---------------------------------------------------------------------------
void CheckOther : : clarifyStatement ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2012-08-24 11:28:50 +02:00
return ;
2012-11-14 18:12:33 +01:00
const SymbolDatabase * 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 ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " * %var% " ) ) {
const Token * tok2 = tok - > previous ( ) ;
while ( tok2 & & tok2 - > str ( ) = = " * " )
tok2 = tok2 - > previous ( ) ;
if ( Token : : Match ( tok2 , " [{};] " ) ) {
tok = tok - > tokAt ( 2 ) ;
for ( ; ; ) {
if ( tok - > str ( ) = = " [ " )
tok = tok - > link ( ) - > next ( ) ;
if ( Token : : Match ( tok , " .|:: %var% " ) ) {
if ( tok - > strAt ( 2 ) = = " ( " )
tok = tok - > linkAt ( 2 ) - > next ( ) ;
else
tok = tok - > tokAt ( 2 ) ;
} else
break ;
}
if ( Token : : Match ( tok , " ++|-- [;,] " ) )
clarifyStatementError ( tok ) ;
2012-09-03 17:10:27 +02:00
}
2012-08-24 11:28:50 +02:00
}
}
}
}
2012-09-15 20:19:02 +02:00
void CheckOther : : clarifyStatementError ( const Token * tok )
2012-08-24 11:28:50 +02:00
{
2012-09-15 20:19:02 +02:00
reportError ( tok , Severity : : warning , " clarifyStatement " , " Ineffective statement similar to '*A++;'. Did you intend to write '(*A)++;'? \n "
" A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. "
" Thus, the dereference is meaningless. Did you intend to write '(*A)++;'? " ) ;
2012-08-24 11:28:50 +02:00
}
2011-10-06 22:01:48 +02:00
2011-10-11 08:41:39 +02:00
void CheckOther : : checkSuspiciousSemicolon ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > inconclusive | | ! _settings - > isEnabled ( " warning " ) )
2011-10-11 08:41:39 +02:00
return ;
2011-12-09 22:28:10 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
// Look for "if(); {}", "for(); {}" or "while(); {}"
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type = = Scope : : eIf | | i - > type = = Scope : : eElse | | i - > type = = Scope : : eElseIf | | i - > type = = Scope : : eWhile | | i - > type = = Scope : : eFor ) {
2011-10-11 08:41:39 +02:00
// Ensure the semicolon is at the same line number as the if/for/while statement
// and the {..} block follows it without an extra empty line.
2012-05-24 15:34:59 +02:00
if ( Token : : simpleMatch ( i - > classStart , " { ; } { " ) & &
i - > classStart - > previous ( ) - > linenr ( ) = = i - > classStart - > tokAt ( 2 ) - > linenr ( )
& & i - > classStart - > linenr ( ) + 1 > = i - > classStart - > tokAt ( 3 ) - > linenr ( ) ) {
2013-03-03 18:54:44 +01:00
SuspiciousSemicolonError ( i - > classDef ) ;
2011-10-11 08:41:39 +02:00
}
}
}
}
void CheckOther : : SuspiciousSemicolonError ( const Token * tok )
{
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : warning , " suspiciousSemicolon " ,
2013-03-03 18:54:44 +01:00
" Suspicious use of ; at the end of ' " + ( tok ? tok - > str ( ) : std : : string ( ) ) + " ' statement. " , true ) ;
2011-10-11 08:41:39 +02:00
}
2011-10-06 22:01:48 +02:00
//---------------------------------------------------------------------------
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : warningOldStylePointerCast ( )
2009-01-26 20:14:46 +01:00
{
2012-03-03 21:14:20 +01:00
// Only valid on C++ code
if ( ! _settings - > isEnabled ( " style " ) | | ! _tokenizer - > isCPP ( ) )
2011-10-26 21:17:27 +02:00
return ;
2010-04-21 08:38:25 +02:00
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2009-01-26 20:14:46 +01:00
// Old style pointer casting..
2011-12-04 19:49:01 +01:00
if ( ! Token : : Match ( tok , " ( const| %type% * ) (| %var% " ) & &
2010-04-02 07:30:58 +02:00
! Token : : Match ( tok , " ( const| %type% * ) (| new " ) )
2009-01-26 20:14:46 +01:00
continue ;
2011-11-13 13:10:59 +01:00
if ( tok - > strAt ( 1 ) = = " const " )
2012-03-03 21:14:20 +01:00
tok = tok - > next ( ) ;
2009-07-20 21:52:27 +02:00
2012-03-03 21:14:20 +01:00
if ( tok - > strAt ( 4 ) = = " const " )
2009-06-18 22:26:21 +02:00
continue ;
2009-01-26 20:14:46 +01:00
// Is "type" a class?
2012-03-03 21:14:20 +01:00
const std : : string pattern ( " class|struct " + tok - > strAt ( 1 ) ) ;
2012-05-24 15:34:59 +02:00
if ( Token : : findmatch ( _tokenizer - > tokens ( ) , pattern . c_str ( ) , tok ) )
2012-03-03 21:14:20 +01:00
cstyleCastError ( tok ) ;
2009-01-26 20:14:46 +01:00
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : cstyleCastError ( const Token * tok )
{
reportError ( tok , Severity : : style , " cstyleCast " , " C-style pointer casting " ) ;
}
2012-02-26 11:56:32 +01:00
//---------------------------------------------------------------------------
// float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation
//---------------------------------------------------------------------------
static std : : string analyzeType ( const Token * tok )
{
2012-03-01 18:38:20 +01:00
if ( tok - > str ( ) = = " double " ) {
2012-02-26 11:56:32 +01:00
if ( tok - > isLong ( ) )
return " long double " ;
else
return " double " ;
2012-02-27 14:13:34 +01:00
}
2012-02-26 11:56:32 +01:00
if ( tok - > str ( ) = = " float " )
return " float " ;
2012-03-25 12:55:39 +02:00
if ( Token : : Match ( tok , " int|long|short|char|size_t " ) )
2012-02-26 11:56:32 +01:00
return " integer " ;
return " " ;
}
void CheckOther : : invalidPointerCast ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) & & ! _settings - > isEnabled ( " portability " ) )
2012-02-26 11:56:32 +01:00
return ;
2012-11-14 18:12:33 +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 ( ) ) {
const Token * toTok = 0 ;
const Token * nextTok = 0 ;
// Find cast
if ( Token : : Match ( tok , " ( const| %type% const| * ) " ) | |
Token : : Match ( tok , " ( const| %type% %type% const| * ) " ) ) {
toTok = tok - > next ( ) ;
nextTok = tok - > link ( ) - > next ( ) ;
if ( nextTok & & nextTok - > str ( ) = = " ( " )
nextTok = nextTok - > next ( ) ;
} else if ( Token : : Match ( tok , " reinterpret_cast < const| %type% const| * > ( " ) | |
Token : : Match ( tok , " reinterpret_cast < const| %type% %type% const| * > ( " ) ) {
nextTok = tok - > tokAt ( 5 ) ;
while ( nextTok - > str ( ) ! = " ( " )
nextTok = nextTok - > next ( ) ;
2012-02-26 11:56:32 +01:00
nextTok = nextTok - > next ( ) ;
2012-11-14 18:12:33 +01:00
toTok = tok - > tokAt ( 2 ) ;
}
if ( toTok & & toTok - > str ( ) = = " const " )
toTok = toTok - > next ( ) ;
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
if ( ! nextTok | | ! toTok | | ! toTok - > isStandardType ( ) )
continue ;
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
// Find casted variable
2013-02-06 06:39:58 +01:00
const Variable * var = 0 ;
2012-11-14 18:12:33 +01:00
bool allocation = false ;
bool ref = false ;
if ( Token : : Match ( nextTok , " new %type% " ) )
allocation = true ;
else if ( Token : : Match ( nextTok , " %var% !![ " ) )
2013-02-06 06:39:58 +01:00
var = nextTok - > variable ( ) ;
2012-11-14 18:12:33 +01:00
else if ( Token : : Match ( nextTok , " & %var% " ) & & ! Token : : Match ( nextTok - > tokAt ( 2 ) , " (|[ " ) ) {
2013-02-06 06:39:58 +01:00
var = nextTok - > next ( ) - > variable ( ) ;
2012-11-14 18:12:33 +01:00
ref = true ;
}
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
const Token * fromTok = 0 ;
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
if ( allocation ) {
fromTok = nextTok - > next ( ) ;
} else {
if ( ! var | | ( ! ref & & ! var - > isPointer ( ) & & ! var - > isArray ( ) ) | | ( ref & & ( var - > isPointer ( ) | | var - > isArray ( ) ) ) )
continue ;
fromTok = var - > typeStartToken ( ) ;
}
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
while ( Token : : Match ( fromTok , " static|const " ) )
fromTok = fromTok - > next ( ) ;
if ( ! fromTok - > isStandardType ( ) )
continue ;
2012-02-26 11:56:32 +01:00
2012-11-14 18:12:33 +01:00
std : : string fromType = analyzeType ( fromTok ) ;
std : : string toType = analyzeType ( toTok ) ;
if ( fromType ! = toType & & ! fromType . empty ( ) & & ! toType . empty ( ) & & ( toType ! = " integer " | | _settings - > isEnabled ( " portability " ) ) & & ( toTok - > str ( ) ! = " char " | | _settings - > inconclusive ) )
invalidPointerCastError ( tok , fromType , toType , toTok - > str ( ) = = " char " ) ;
}
2012-02-26 11:56:32 +01:00
}
}
2012-03-25 12:55:39 +02:00
void CheckOther : : invalidPointerCastError ( const Token * tok , const std : : string & from , const std : : string & to , bool inconclusive )
2012-02-26 11:56:32 +01:00
{
2012-03-25 12:55:39 +02:00
if ( to = = " integer " ) { // If we cast something to int*, this can be useful to play with its binary data representation
if ( ! inconclusive )
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : portability , " invalidPointerCast " , " Casting from " + from + " * to integer* is not portable due to different binary data representations on different platforms. " ) ;
2012-03-25 12:55:39 +02:00
else
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : portability , " invalidPointerCast " , " Casting from " + from + " * to char* is not portable due to different binary data representations on different platforms. " , true ) ;
2012-03-25 12:55:39 +02:00
} else
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : warning , " invalidPointerCast " , " Casting between " + from + " * and " + to + " * which have an incompatible binary data representation. " ) ;
2012-02-26 11:56:32 +01:00
}
2013-02-17 17:33:32 +01:00
//---------------------------------------------------------------------------
// This check detects errors on POSIX systems, when a pipe command called
// with a wrong dimensioned file descriptor array. The pipe command requires
// exactly an integer array of dimension two as parameter.
//
// References:
// - http://linux.die.net/man/2/pipe
// - ticket #3521
//---------------------------------------------------------------------------
void CheckOther : : checkPipeParameterSize ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > standards . posix )
2013-02-17 17:33:32 +01:00
return ;
const SymbolDatabase * 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 , " pipe ( %var% ) " ) | |
Token : : Match ( tok , " pipe2 ( %var% , " ) ) {
const Token * const varTok = tok - > tokAt ( 2 ) ;
const Variable * var = varTok - > variable ( ) ;
MathLib : : bigint dim ;
2013-04-11 14:22:22 +02:00
if ( var & & ( var - > isArray ( ) | | var - > isPointer ( ) ) & & ! var - > isArgument ( ) & & ( ( dim = var - > dimension ( 0U ) ) < 2 ) ) {
2013-02-17 17:33:32 +01:00
const std : : string strDim = MathLib : : longToString ( dim ) ;
checkPipeParameterSizeError ( varTok , varTok - > str ( ) , strDim ) ;
}
}
}
}
}
void CheckOther : : checkPipeParameterSizeError ( const Token * tok , const std : : string & strVarName , const std : : string & strDim )
{
reportError ( tok , Severity : : error ,
2013-03-03 18:54:44 +01:00
" wrongPipeParameterSize " , " Buffer ' " + strVarName + " ' must have size of 2 integers if used as parameter of pipe(). \n "
" The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers. \n "
" The variable ' " + strVarName + " ' is an array of size " + strDim + " , which does not match. " ) ;
2013-02-17 17:33:32 +01:00
}
2013-03-11 17:04:30 +01:00
//-----------------------------------------------------------------------------
2013-03-11 17:38:03 +01:00
// check usleep(), which is allowed to be called with in a range of [0,999999]
2013-03-11 17:04:30 +01:00
//
// Reference:
// - http://man7.org/linux/man-pages/man3/usleep.3.html
//-----------------------------------------------------------------------------
void CheckOther : : checkSleepTimeInterval ( )
{
if ( ! _settings - > standards . posix )
return ;
const SymbolDatabase * 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 , " usleep ( %num% ) " ) ) {
const Token * const numTok = tok - > tokAt ( 2 ) ;
MathLib : : bigint value = MathLib : : toLongNumber ( numTok - > str ( ) ) ;
2013-03-11 17:38:03 +01:00
if ( value > 999999 ) { // less than 1 million
2013-03-11 17:04:30 +01:00
checkSleepTimeError ( numTok , numTok - > str ( ) ) ;
}
}
}
}
}
void CheckOther : : checkSleepTimeError ( const Token * tok , const std : : string & strDim )
{
reportError ( tok , Severity : : error ,
" tooBigSleepTime " , " The argument of usleep must be less than 1000000. \n "
" The argument of usleep must be less than 1000000, but " + strDim + " is provided. " ) ;
}
2012-09-02 13:09:32 +02:00
//---------------------------------------------------------------------------
// Detect redundant assignments: x = 0; x = 4;
//---------------------------------------------------------------------------
2013-02-15 18:01:10 +01:00
static bool nonLocal ( const Variable * var )
{
2013-02-15 18:40:34 +01:00
return ! var | | ( ! var - > isLocal ( ) & & ! var - > isArgument ( ) ) | | var - > isStatic ( ) | | var - > isReference ( ) ;
2013-02-15 18:01:10 +01:00
}
2012-09-03 10:46:51 +02:00
static void eraseNotLocalArg ( std : : map < unsigned int , const Token * > & container , const SymbolDatabase * symbolDatabase )
{
for ( std : : map < unsigned int , const Token * > : : iterator i = container . begin ( ) ; i ! = container . end ( ) ; ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( i - > first ) ;
2013-02-15 18:01:10 +01:00
if ( ! var | | nonLocal ( var ) ) {
2012-09-03 10:46:51 +02:00
container . erase ( i + + ) ;
if ( i = = container . end ( ) )
break ;
} else
+ + i ;
}
}
2012-09-02 13:09:32 +02:00
void CheckOther : : checkRedundantAssignment ( )
{
2013-03-03 11:41:59 +01:00
bool performance = _settings - > isEnabled ( " performance " ) ;
bool warning = _settings - > isEnabled ( " warning " ) ;
if ( ! warning & & ! performance )
2012-09-02 13:09:32 +02:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-09-02 17:00:16 +02:00
for ( std : : list < Scope > : : const_iterator scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
if ( ! scope - > isExecutable ( ) )
2012-09-02 13:09:32 +02:00
continue ;
std : : map < unsigned int , const Token * > varAssignments ;
std : : map < unsigned int , const Token * > memAssignments ;
2013-05-09 15:32:02 +02:00
std : : set < unsigned int > initialized ;
2012-09-02 13:09:32 +02:00
const Token * writtenArgumentsEnd = 0 ;
2012-09-02 17:00:16 +02:00
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-09-02 13:09:32 +02:00
if ( tok = = writtenArgumentsEnd )
writtenArgumentsEnd = 0 ;
if ( tok - > str ( ) = = " { " & & tok - > strAt ( - 1 ) ! = " { " & & tok - > strAt ( - 1 ) ! = " = " & & tok - > strAt ( - 4 ) ! = " case " & & tok - > strAt ( - 3 ) ! = " default " ) { // conditional or non-executable inner scope: Skip it and reset status
tok = tok - > link ( ) ;
varAssignments . clear ( ) ;
memAssignments . clear ( ) ;
} else if ( Token : : Match ( tok , " for|if|while ( " ) ) {
tok = tok - > linkAt ( 1 ) ;
} else if ( Token : : Match ( tok , " break|return|continue|throw|goto " ) ) {
varAssignments . clear ( ) ;
memAssignments . clear ( ) ;
} else if ( tok - > type ( ) = = Token : : eVariable ) {
2013-05-09 15:32:02 +02:00
// Set initialization flag
if ( ! Token : : Match ( tok , " %var% [ " ) )
initialized . insert ( tok - > varId ( ) ) ;
else {
const Token * tok2 = tok - > next ( ) ;
while ( tok2 & & tok2 - > str ( ) = = " [ " )
tok2 = tok2 - > link ( ) - > next ( ) ;
if ( tok2 & & tok2 - > str ( ) ! = " ; " )
initialized . insert ( tok - > varId ( ) ) ;
}
2012-09-02 13:09:32 +02:00
std : : map < unsigned int , const Token * > : : iterator it = varAssignments . find ( tok - > varId ( ) ) ;
if ( tok - > next ( ) - > isAssignmentOp ( ) & & Token : : Match ( tok - > previous ( ) , " [;{}] " ) ) { // Assignment
if ( it ! = varAssignments . end ( ) ) {
bool error = true ; // Ensure that variable is not used on right side
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ; " )
break ;
else if ( tok2 - > varId ( ) = = tok - > varId ( ) )
error = false ;
2013-02-15 18:40:34 +01:00
else if ( Token : : Match ( tok2 , " %var% ( " ) & & nonLocal ( tok - > variable ( ) ) ) { // Called function might use the variable
2013-04-04 19:53:55 +02:00
const Function * func = tok2 - > function ( ) ;
2013-02-15 18:40:34 +01:00
const Variable * var = tok - > variable ( ) ;
2013-02-16 10:43:16 +01:00
if ( ! var | | var - > isGlobal ( ) | | var - > isReference ( ) | | ( ( ! func | | func - > nestedIn ) & & tok2 - > strAt ( - 1 ) ! = " . " ) ) // Global variable, or member function
2013-02-15 18:40:34 +01:00
error = false ;
}
2012-09-02 13:09:32 +02:00
}
if ( error ) {
2013-05-09 15:25:36 +02:00
if ( scope - > type = = Scope : : eSwitch & & Token : : findmatch ( it - > second , " default|case " , tok ) & & warning )
redundantAssignmentInSwitchError ( it - > second , tok , tok - > str ( ) ) ;
2013-03-03 11:41:59 +01:00
else if ( performance )
2013-02-15 18:01:10 +01:00
redundantAssignmentError ( it - > second , tok , tok - > str ( ) , nonLocal ( it - > second - > variable ( ) ) ) ; // Inconclusive for non-local variables
2012-09-02 13:09:32 +02:00
}
it - > second = tok ;
}
2013-05-09 15:39:33 +02:00
if ( ! Token : : simpleMatch ( tok - > tokAt ( 2 ) , " 0 ; " ) | | ( tok - > variable ( ) & & tok - > variable ( ) - > nameToken ( ) ! = tok - > tokAt ( - 2 ) ) )
2012-11-15 07:48:45 +01:00
varAssignments [ tok - > varId ( ) ] = tok ;
2012-09-02 13:09:32 +02:00
memAssignments . erase ( tok - > varId ( ) ) ;
} else if ( tok - > next ( ) - > type ( ) = = Token : : eIncDecOp | | ( tok - > previous ( ) - > type ( ) = = Token : : eIncDecOp & & ! Token : : Match ( tok - > next ( ) , " .|[|( " ) ) ) { // Variable incremented/decremented
varAssignments [ tok - > varId ( ) ] = tok ;
memAssignments . erase ( tok - > varId ( ) ) ;
2012-09-07 11:59:20 +02:00
} else if ( ! Token : : simpleMatch ( tok - > tokAt ( - 2 ) , " sizeof ( " ) ) { // Other usage of variable
2012-09-02 13:09:32 +02:00
if ( it ! = varAssignments . end ( ) )
varAssignments . erase ( it ) ;
if ( ! writtenArgumentsEnd ) // Indicates that we are in the first argument of strcpy/memcpy/... function
memAssignments . erase ( tok - > varId ( ) ) ;
}
} else if ( Token : : Match ( tok , " %var% ( " ) ) { // Function call. Global variables might be used. Reset their status
2012-11-06 19:54:52 +01:00
bool memfunc = Token : : Match ( tok , " memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy|swprintf|wcscat|wcsncat " ) ;
2012-09-02 13:09:32 +02:00
if ( memfunc ) {
const Token * param1 = tok - > tokAt ( 2 ) ;
writtenArgumentsEnd = param1 - > next ( ) ;
2012-11-06 19:54:52 +01:00
if ( param1 - > varId ( ) & & param1 - > strAt ( 1 ) = = " , " & & ! Token : : Match ( tok , " strcat|strncat|wcscat|wcsncat " ) ) {
2013-05-09 15:32:02 +02:00
if ( tok - > str ( ) = = " memset " & & initialized . find ( param1 - > varId ( ) ) = = initialized . end ( ) & & param1 - > variable ( ) & & param1 - > variable ( ) - > isLocal ( ) & & param1 - > variable ( ) - > isArray ( ) )
initialized . insert ( param1 - > varId ( ) ) ;
else if ( memAssignments . find ( param1 - > varId ( ) ) = = memAssignments . end ( ) )
2012-09-02 13:09:32 +02:00
memAssignments [ param1 - > varId ( ) ] = tok ;
else {
2013-05-09 15:32:02 +02:00
const std : : map < unsigned int , const Token * > : : iterator it = memAssignments . find ( param1 - > varId ( ) ) ;
2013-03-03 11:41:59 +01:00
if ( scope - > type = = Scope : : eSwitch & & Token : : findmatch ( it - > second , " default|case " , tok ) & & warning )
2012-09-02 13:09:32 +02:00
redundantCopyInSwitchError ( it - > second , tok , param1 - > str ( ) ) ;
2013-03-03 11:41:59 +01:00
else if ( performance )
2012-09-02 13:09:32 +02:00
redundantCopyError ( it - > second , tok , param1 - > str ( ) ) ;
}
}
2012-09-03 12:03:30 +02:00
} else if ( scope - > type = = Scope : : eSwitch ) { // Avoid false positives if noreturn function is called in switch
2013-01-31 06:41:18 +01:00
const Function * func = tok - > function ( ) ;
2012-09-02 13:09:32 +02:00
if ( ! func | | ! func - > hasBody ) {
varAssignments . clear ( ) ;
memAssignments . clear ( ) ;
continue ;
}
const Token * funcEnd = func - > functionScope - > classEnd ;
bool noreturn ;
if ( ! _tokenizer - > IsScopeNoReturn ( funcEnd , & noreturn ) & & ! noreturn ) {
2012-09-03 10:46:51 +02:00
eraseNotLocalArg ( varAssignments , symbolDatabase ) ;
eraseNotLocalArg ( memAssignments , symbolDatabase ) ;
2012-09-02 13:09:32 +02:00
} else {
varAssignments . clear ( ) ;
memAssignments . clear ( ) ;
}
2012-09-03 12:03:30 +02:00
} else { // Noreturn functions outside switch don't cause problems
eraseNotLocalArg ( varAssignments , symbolDatabase ) ;
eraseNotLocalArg ( memAssignments , symbolDatabase ) ;
2012-09-02 13:09:32 +02:00
}
}
}
}
}
void CheckOther : : redundantCopyError ( const Token * tok1 , const Token * tok2 , const std : : string & var )
{
std : : list < const Token * > callstack ;
callstack . push_back ( tok1 ) ;
callstack . push_back ( tok2 ) ;
reportError ( callstack , Severity : : performance , " redundantCopy " ,
" Buffer ' " + var + " ' is being written before its old content has been used. " ) ;
}
void CheckOther : : redundantCopyInSwitchError ( const Token * tok1 , const Token * tok2 , const std : : string & var )
{
std : : list < const Token * > callstack ;
callstack . push_back ( tok1 ) ;
callstack . push_back ( tok2 ) ;
reportError ( callstack , Severity : : warning , " redundantCopyInSwitch " ,
2012-09-16 19:44:02 +02:00
" Buffer ' " + var + " ' is being written before its old content has been used. 'break;' missing? " ) ;
2012-09-02 13:09:32 +02:00
}
2013-02-15 18:01:10 +01:00
void CheckOther : : redundantAssignmentError ( const Token * tok1 , const Token * tok2 , const std : : string & var , bool inconclusive )
2012-09-02 13:09:32 +02:00
{
std : : list < const Token * > callstack ;
callstack . push_back ( tok1 ) ;
callstack . push_back ( tok2 ) ;
2013-02-15 18:01:10 +01:00
if ( inconclusive )
reportError ( callstack , Severity : : performance , " redundantAssignment " ,
" Variable ' " + var + " ' is reassigned a value before the old one has been used if variable is no semaphore variable. \n "
" Variable ' " + var + " ' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code. " , true ) ;
else
reportError ( callstack , Severity : : performance , " redundantAssignment " ,
" Variable ' " + var + " ' is reassigned a value before the old one has been used. " ) ;
2012-09-02 13:09:32 +02:00
}
void CheckOther : : redundantAssignmentInSwitchError ( const Token * tok1 , const Token * tok2 , const std : : string & var )
{
std : : list < const Token * > callstack ;
callstack . push_back ( tok1 ) ;
callstack . push_back ( tok2 ) ;
reportError ( callstack , Severity : : warning , " redundantAssignInSwitch " ,
2012-09-16 19:44:02 +02:00
" Variable ' " + var + " ' is reassigned a value before the old one has been used. 'break;' missing? " ) ;
2012-09-02 13:09:32 +02:00
}
2010-06-30 09:10:30 +02:00
//---------------------------------------------------------------------------
// switch (x)
// {
// case 2:
// y = a; // <- this assignment is redundant
// case 3:
// y = b; // <- case 2 falls through and sets y twice
// }
//---------------------------------------------------------------------------
2013-01-17 10:15:01 +01:00
static inline bool isFunctionOrBreakPattern ( const Token * tok )
{
if ( Token : : Match ( tok , " %var% ( " ) | | Token : : Match ( tok , " break|continue|return|exit|goto|throw " ) )
return true ;
return false ;
}
2010-06-30 09:10:30 +02:00
void CheckOther : : checkRedundantAssignmentInSwitch ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2011-12-16 19:21:14 +01:00
return ;
2012-03-03 21:14:20 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-06-30 09:10:30 +02:00
// Find the beginning of a switch. E.g.:
// switch (var) { ...
2012-03-03 21:14:20 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eSwitch | | ! i - > classStart )
continue ;
2010-06-30 09:10:30 +02:00
// Check the contents of the switch statement
2012-05-29 06:19:22 +02:00
std : : map < unsigned int , const Token * > varsWithBitsSet ;
std : : map < unsigned int , std : : string > bitOperations ;
2012-03-03 21:14:20 +01:00
for ( const Token * tok2 = i - > classStart - > next ( ) ; tok2 ! = i - > classEnd ; tok2 = tok2 - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( tok2 - > str ( ) = = " { " ) {
2010-06-30 09:10:30 +02:00
// Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.:
// case 3: b = 1;
// case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok2 - > previous ( ) , " )|else { " ) & & tok2 - > link ( ) ) {
2010-06-30 09:10:30 +02:00
const Token * endOfConditional = tok2 - > link ( ) ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok3 = tok2 ; tok3 ! = endOfConditional ; tok3 = tok3 - > next ( ) ) {
if ( tok3 - > varId ( ) ! = 0 ) {
2012-05-29 06:19:22 +02:00
varsWithBitsSet . erase ( tok3 - > varId ( ) ) ;
bitOperations . erase ( tok3 - > varId ( ) ) ;
2013-01-17 10:15:01 +01:00
} else if ( isFunctionOrBreakPattern ( tok3 ) ) {
2012-05-29 06:19:22 +02:00
varsWithBitsSet . clear ( ) ;
bitOperations . clear ( ) ;
2011-10-13 10:27:22 +02:00
}
2010-06-30 09:10:30 +02:00
}
tok2 = endOfConditional ;
2012-03-03 21:14:20 +01:00
}
2010-06-30 09:10:30 +02:00
}
// Variable assignment. Report an error if it's assigned to twice before a break. E.g.:
// case 3: b = 1; // <== redundant
// case 4: b = 2;
2012-08-22 14:40:57 +02:00
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok2 - > previous ( ) , " ;|{|}|: %var% = %any% ; " ) & & tok2 - > varId ( ) ! = 0 ) {
2012-05-29 06:19:22 +02:00
varsWithBitsSet . erase ( tok2 - > varId ( ) ) ;
bitOperations . erase ( tok2 - > varId ( ) ) ;
}
// Bitwise operation. Report an error if it's performed twice before a break. E.g.:
// case 3: b |= 1; // <== redundant
// case 4: b |= 1;
else if ( Token : : Match ( tok2 - > previous ( ) , " ; | { | } | : % var % = % var % % or % | & % num % ; " ) &&
tok2 - > varId ( ) ! = 0 & & tok2 - > varId ( ) = = tok2 - > tokAt ( 2 ) - > varId ( ) ) {
std : : string bitOp = tok2 - > strAt ( 3 ) + tok2 - > strAt ( 4 ) ;
std : : map < unsigned int , const Token * > : : iterator i2 = varsWithBitsSet . find ( tok2 - > varId ( ) ) ;
// This variable has not had a bit operation performed on it yet, so just make a note of it
if ( i2 = = varsWithBitsSet . end ( ) ) {
varsWithBitsSet [ tok2 - > varId ( ) ] = tok2 ;
bitOperations [ tok2 - > varId ( ) ] = bitOp ;
}
// The same bit operation has been performed on the same variable twice, so report an error
else if ( bitOperations [ tok2 - > varId ( ) ] = = bitOp )
redundantBitwiseOperationInSwitchError ( i2 - > second , i2 - > second - > str ( ) ) ;
// A different bit operation was performed on the variable, so clear it
else {
varsWithBitsSet . erase ( tok2 - > varId ( ) ) ;
bitOperations . erase ( tok2 - > varId ( ) ) ;
}
2011-10-13 10:27:22 +02:00
}
2012-05-29 06:19:22 +02:00
2010-06-30 09:10:30 +02:00
// Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.:
// case 3: b = 1;
// case 4: b++;
2012-05-29 06:19:22 +02:00
else if ( tok2 - > varId ( ) ! = 0 & & tok2 - > strAt ( 1 ) ! = " | " & & tok2 - > strAt ( 1 ) ! = " & " ) {
varsWithBitsSet . erase ( tok2 - > varId ( ) ) ;
bitOperations . erase ( tok2 - > varId ( ) ) ;
}
2010-06-30 09:10:30 +02:00
// Reset our record of assignments if there is a break or function call. E.g.:
// case 3: b = 1; break;
2013-01-17 10:15:01 +01:00
if ( isFunctionOrBreakPattern ( tok2 ) ) {
2012-05-29 06:19:22 +02:00
varsWithBitsSet . clear ( ) ;
bitOperations . clear ( ) ;
2011-10-13 10:27:22 +02:00
}
2010-06-30 09:10:30 +02:00
}
}
}
2012-05-29 06:19:22 +02:00
void CheckOther : : redundantBitwiseOperationInSwitchError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : warning ,
2012-09-16 19:44:02 +02:00
" redundantBitwiseOperationInSwitch " , " Redundant bitwise operation on ' " + varname + " ' in 'switch' statement. 'break;' missing? " ) ;
2012-05-29 06:19:22 +02:00
}
2011-10-13 10:27:22 +02:00
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2011-02-19 09:33:29 +01:00
void CheckOther : : checkSwitchCaseFallThrough ( )
{
2011-08-07 09:28:08 +02:00
if ( ! ( _settings - > isEnabled ( " style " ) & & _settings - > experimental ) )
2011-03-05 21:42:15 +01:00
return ;
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 ) {
2012-01-21 19:55:32 +01:00
if ( i - > type ! = Scope : : eSwitch | | ! i - > classStart ) // Find the beginning of a switch
2011-12-09 22:28:10 +01:00
continue ;
2011-02-19 09:33:29 +01:00
// Check the contents of the switch statement
2011-02-19 20:02:28 +01:00
std : : stack < std : : pair < Token * , bool > > ifnest ;
std : : stack < Token * > loopnest ;
std : : stack < Token * > scopenest ;
bool justbreak = true ;
2011-02-23 10:45:21 +01:00
bool firstcase = true ;
2012-01-21 19:55:32 +01:00
for ( const Token * tok2 = i - > classStart ; tok2 ! = i - > classEnd ; tok2 = tok2 - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok2 , " if ( " ) ) {
2011-11-20 15:09:57 +01:00
tok2 = tok2 - > next ( ) - > link ( ) - > next ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok2 - > link ( ) = = NULL ) {
2011-02-19 21:43:24 +01:00
std : : ostringstream errmsg ;
errmsg < < " unmatched if in switch: " < < tok2 - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
break ;
}
2011-02-19 20:02:28 +01:00
ifnest . push ( std : : make_pair ( tok2 - > link ( ) , false ) ) ;
justbreak = false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : simpleMatch ( tok2 , " while ( " ) ) {
2011-11-20 15:09:57 +01:00
tok2 = tok2 - > next ( ) - > link ( ) - > next ( ) ;
2011-02-19 21:43:24 +01:00
// skip over "do { } while ( ) ;" case
2011-10-13 20:53:06 +02:00
if ( tok2 - > str ( ) = = " { " ) {
if ( tok2 - > link ( ) = = NULL ) {
2011-02-19 21:43:24 +01:00
std : : ostringstream errmsg ;
errmsg < < " unmatched while in switch: " < < tok2 - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
break ;
}
loopnest . push ( tok2 - > link ( ) ) ;
}
2011-02-19 20:02:28 +01:00
justbreak = false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : simpleMatch ( tok2 , " do { " ) ) {
2011-11-20 15:09:57 +01:00
tok2 = tok2 - > next ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok2 - > link ( ) = = NULL ) {
2011-02-19 21:43:24 +01:00
std : : ostringstream errmsg ;
errmsg < < " unmatched do in switch: " < < tok2 - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
break ;
}
2011-02-19 20:02:28 +01:00
loopnest . push ( tok2 - > link ( ) ) ;
justbreak = false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : simpleMatch ( tok2 , " for ( " ) ) {
2011-11-20 15:09:57 +01:00
tok2 = tok2 - > next ( ) - > link ( ) - > next ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok2 - > link ( ) = = NULL ) {
2011-02-19 21:43:24 +01:00
std : : ostringstream errmsg ;
errmsg < < " unmatched for in switch: " < < tok2 - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
break ;
}
2011-02-19 20:02:28 +01:00
loopnest . push ( tok2 - > link ( ) ) ;
justbreak = false ;
2011-12-09 22:28:10 +01:00
} else if ( Token : : simpleMatch ( tok2 , " switch ( " ) ) {
2011-02-19 20:02:28 +01:00
// skip over nested switch, we'll come to that soon
2011-11-20 15:09:57 +01:00
tok2 = tok2 - > next ( ) - > link ( ) - > next ( ) - > link ( ) ;
2013-01-17 10:15:01 +01:00
} else if ( Token : : Match ( tok2 , " break|continue|return|exit|goto|throw " ) ) {
2011-10-13 20:53:06 +02:00
if ( loopnest . empty ( ) ) {
2011-02-19 20:02:28 +01:00
justbreak = true ;
}
2011-10-27 10:54:50 +02:00
tok2 = Token : : findsimplematch ( tok2 , " ; " ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok2 , " case|default " ) ) {
if ( ! justbreak & & ! firstcase ) {
2011-02-19 09:33:29 +01:00
switchCaseFallThrough ( tok2 ) ;
}
2011-10-27 10:54:50 +02:00
tok2 = Token : : findsimplematch ( tok2 , " : " ) ;
2011-02-19 20:02:28 +01:00
justbreak = true ;
2011-02-23 10:45:21 +01:00
firstcase = false ;
2011-10-13 20:53:06 +02:00
} else if ( tok2 - > str ( ) = = " { " ) {
2011-02-19 20:02:28 +01:00
scopenest . push ( tok2 - > link ( ) ) ;
2011-10-13 20:53:06 +02:00
} else if ( tok2 - > str ( ) = = " } " ) {
if ( ! ifnest . empty ( ) & & tok2 = = ifnest . top ( ) . first ) {
if ( tok2 - > next ( ) - > str ( ) = = " else " ) {
2011-02-19 20:02:28 +01:00
tok2 = tok2 - > tokAt ( 2 ) ;
ifnest . pop ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok2 - > link ( ) = = NULL ) {
2011-02-20 11:44:18 +01:00
std : : ostringstream errmsg ;
errmsg < < " unmatched if in switch: " < < tok2 - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
break ;
}
2011-02-19 20:02:28 +01:00
ifnest . push ( std : : make_pair ( tok2 - > link ( ) , justbreak ) ) ;
justbreak = false ;
2011-10-13 20:53:06 +02:00
} else {
2011-02-19 20:02:28 +01:00
justbreak & = ifnest . top ( ) . second ;
ifnest . pop ( ) ;
}
2011-10-13 20:53:06 +02:00
} else if ( ! loopnest . empty ( ) & & tok2 = = loopnest . top ( ) ) {
2011-02-19 20:02:28 +01:00
loopnest . pop ( ) ;
2011-10-13 20:53:06 +02:00
} else if ( ! scopenest . empty ( ) & & tok2 = = scopenest . top ( ) ) {
2011-02-19 20:02:28 +01:00
scopenest . pop ( ) ;
2011-10-13 20:53:06 +02:00
} else {
if ( ! ifnest . empty ( ) | | ! loopnest . empty ( ) | | ! scopenest . empty ( ) ) {
2011-02-19 21:43:24 +01:00
std : : ostringstream errmsg ;
errmsg < < " unexpected end of switch: " ;
errmsg < < " ifnest= " < < ifnest . size ( ) ;
if ( ! ifnest . empty ( ) )
errmsg < < " , " < < ifnest . top ( ) . first - > linenr ( ) ;
errmsg < < " , loopnest= " < < loopnest . size ( ) ;
if ( ! loopnest . empty ( ) )
errmsg < < " , " < < loopnest . top ( ) - > linenr ( ) ;
errmsg < < " , scopenest= " < < scopenest . size ( ) ;
if ( ! scopenest . empty ( ) )
errmsg < < " , " < < scopenest . top ( ) - > linenr ( ) ;
reportError ( _tokenizer - > tokens ( ) , Severity : : debug , " debug " , errmsg . str ( ) ) ;
}
2011-02-19 20:02:28 +01:00
// end of switch block
break ;
}
2011-10-13 20:53:06 +02:00
} else if ( tok2 - > str ( ) ! = " ; " ) {
2011-02-19 20:02:28 +01:00
justbreak = false ;
2011-02-19 09:33:29 +01:00
}
}
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : switchCaseFallThrough ( const Token * tok )
{
reportError ( tok , Severity : : style ,
2012-09-16 19:44:02 +02:00
" switchCaseFallThrough " , " Switch falls through case without comment. 'break;' missing? " ) ;
2011-08-19 17:53:43 +02:00
}
2011-02-19 09:33:29 +01:00
2012-12-07 21:27:32 +01:00
//---------------------------------------------------------------------------
// Check for statements like case A||B: in switch()
//---------------------------------------------------------------------------
void CheckOther : : checkSuspiciousCaseInSwitch ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > inconclusive | | ! _settings - > isEnabled ( " warning " ) )
2012-12-07 21:27:32 +01:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eSwitch )
continue ;
for ( const Token * tok = i - > classStart - > next ( ) ; tok ! = i - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " case " ) {
2012-12-07 21:44:30 +01:00
const Token * end = 0 ;
2012-12-07 21:27:32 +01:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
2012-12-07 21:44:30 +01:00
if ( tok2 - > str ( ) = = " : " ) {
end = tok2 ;
2012-12-07 21:27:32 +01:00
break ;
2012-12-07 21:44:30 +01:00
}
if ( Token : : Match ( tok2 , " [?;}{] " ) ) {
break ;
}
}
if ( end ) {
const Token * finding = Token : : findmatch ( tok - > next ( ) , " &&|%oror% " , end ) ;
if ( finding )
suspiciousCaseInSwitchError ( tok , finding - > str ( ) ) ;
2012-12-07 21:27:32 +01:00
}
}
}
}
}
void CheckOther : : suspiciousCaseInSwitchError ( const Token * tok , const std : : string & operatorString )
{
reportError ( tok , Severity : : warning , " suspiciousCase " ,
" Found suspicious case label in switch(). Operator ' " + operatorString + " ' probably doesn't work as intended. \n "
" Using an operator like ' " + operatorString + " ' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead? " , true ) ;
}
2013-01-18 08:03:04 +01:00
//---------------------------------------------------------------------------
// if (x == 1)
// x == 0; // <- suspicious equality comparison.
//---------------------------------------------------------------------------
void CheckOther : : checkSuspiciousEqualityComparison ( )
{
2013-05-20 16:05:21 +02:00
if ( ! _settings - > isEnabled ( " warning " ) | | ! _settings - > inconclusive )
2013-01-18 08:03:04 +01:00
return ;
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " for ( " ) ) {
2013-01-19 01:34:15 +01:00
const Token * const openParen = tok - > next ( ) ;
const Token * const closeParen = tok - > linkAt ( 1 ) ;
2013-01-18 08:03:04 +01:00
// Search for any suspicious equality comparison in the initialization
// or increment-decrement parts of the for() loop.
// For example:
// for (i == 2; i < 10; i++)
// or
// for (i = 0; i < 10; i == a)
2013-01-19 01:34:15 +01:00
const Token * tok2 = Token : : findmatch ( openParen , " [;(] %var% == %any% [;)] " , closeParen ) ;
if ( tok2 & & ( tok2 = = openParen | | tok2 - > tokAt ( 4 ) = = closeParen ) ) {
2013-01-18 08:03:04 +01:00
suspiciousEqualityComparisonError ( tok2 - > tokAt ( 2 ) ) ;
}
// Equality comparisons with 0 are simplified to negation. For instance,
// (x == 0) is simplified to (!x), so also check for suspicious negation
// in the initialization or increment-decrement parts of the for() loop.
// For example:
// for (!i; i < 10; i++)
2013-01-19 01:34:15 +01:00
const Token * tok3 = Token : : findmatch ( openParen , " [;(] ! %var% [;)] " , closeParen ) ;
if ( tok3 & & ( tok3 = = openParen | | tok3 - > tokAt ( 3 ) = = closeParen ) ) {
2013-01-18 08:03:04 +01:00
suspiciousEqualityComparisonError ( tok3 - > tokAt ( 2 ) ) ;
}
// Skip over for() loop conditions because "for (;running==1;)"
// is a bit strange, but not necessarily incorrect.
2013-01-19 01:34:15 +01:00
tok = closeParen ;
2013-03-03 11:41:59 +01:00
} else if ( Token : : Match ( tok , " [;{}] *| %var% == %any% ; " ) ) {
2013-01-18 08:03:04 +01:00
// Exclude compound statements surrounded by parentheses, such as
// printf("%i\n", ({x==0;}));
// because they may appear as an expression in GNU C/C++.
// See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
const Token * afterStatement = tok - > strAt ( 1 ) = = " * " ? tok - > tokAt ( 6 ) : tok - > tokAt ( 5 ) ;
if ( ! Token : : simpleMatch ( afterStatement , " } ) " ) )
suspiciousEqualityComparisonError ( tok - > next ( ) ) ;
}
}
}
void CheckOther : : suspiciousEqualityComparisonError ( const Token * tok )
{
reportError ( tok , Severity : : warning , " suspiciousEqualityComparison " ,
" Found suspicious equality comparison. Did you intend to assign a value instead? " , true ) ;
}
2012-12-07 21:27:32 +01:00
2010-08-15 06:28:22 +02:00
//---------------------------------------------------------------------------
// int x = 1;
// x = x; // <- redundant assignment to self
//
// int y = y; // <- redundant initialization to self
//---------------------------------------------------------------------------
2012-08-20 17:57:28 +02:00
static bool isTypeWithoutSideEffects ( const Tokenizer * tokenizer , const Variable * var )
2011-12-17 19:04:03 +01:00
{
2012-08-21 20:15:04 +02:00
return ( ( var & & ( ! var - > isClass ( ) | | var - > isPointer ( ) | | Token : : simpleMatch ( var - > typeStartToken ( ) , " std :: " ) ) ) | | ! tokenizer - > isCPP ( ) ) ;
2011-12-17 19:04:03 +01:00
}
2013-01-17 10:15:01 +01:00
static inline const Token * findSelfAssignPattern ( const Token * start )
{
return Token : : findmatch ( start , " %var% = %var% ;|=|) " ) ;
}
2010-08-15 06:28:22 +02:00
void CheckOther : : checkSelfAssignment ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2010-08-15 06:28:22 +02:00
return ;
2013-01-17 10:15:01 +01:00
const Token * tok = findSelfAssignPattern ( _tokenizer - > tokens ( ) ) ;
2011-10-13 20:53:06 +02:00
while ( tok ) {
2012-11-03 21:16:05 +01:00
if ( Token : : Match ( tok - > previous ( ) , " [;{}.] " ) & &
2011-02-12 14:27:07 +01:00
tok - > varId ( ) & & tok - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) & &
2013-02-06 06:39:58 +01:00
isTypeWithoutSideEffects ( _tokenizer , tok - > variable ( ) ) ) {
2011-08-19 07:23:11 +02:00
bool err = true ;
// no false positive for 'x = x ? x : 1;'
2011-08-19 07:28:15 +02:00
// it is simplified to 'if (x) { x=x; } else { x=1; }'. The simplification
// always write all tokens on 1 line (even if the statement is several lines), so
// check if the linenr is the same for all the tokens.
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " ) { %var% = %var% ; } else { %varid% = " , tok - > varId ( ) ) ) {
2011-08-19 07:23:11 +02:00
// Find the 'if' token
2011-11-20 14:22:39 +01:00
const Token * tokif = tok - > linkAt ( - 2 ) - > previous ( ) ;
2011-08-19 07:23:11 +02:00
// find the '}' that terminates the 'else'-block
2011-11-20 14:22:39 +01:00
const Token * else_end = tok - > linkAt ( 6 ) ;
2011-08-19 07:23:11 +02:00
if ( tokif & & else_end & & tokif - > linenr ( ) = = else_end - > linenr ( ) )
err = false ;
}
if ( err )
selfAssignmentError ( tok , tok - > str ( ) ) ;
2010-08-15 06:28:22 +02:00
}
2013-01-17 10:15:01 +01:00
tok = findSelfAssignPattern ( tok - > next ( ) ) ;
2010-08-15 06:28:22 +02:00
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : selfAssignmentError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : warning ,
2012-09-16 19:44:02 +02:00
" selfAssignment " , " Redundant assignment of ' " + varname + " ' to itself. " ) ;
2011-08-19 17:53:43 +02:00
}
2010-10-25 03:14:21 +02:00
//---------------------------------------------------------------------------
2011-08-19 19:28:37 +02:00
// if ((x != 1) || (x != 3)) // expression always true
// if ((x == 1) && (x == 3)) // expression always false
// if ((x < 1) && (x > 3)) // expression always false
// if ((x > 3) || (x < 10)) // expression always true
// if ((x > 5) && (x != 1)) // second comparison always true
//
// Check for suspect logic for an expression consisting of 2 comparison
// expressions with a shared variable and constants and a logical operator
// between them.
//
// Suggest a different logical operator when the logical operator between
// the comparisons is probably wrong.
//
// Inform that second comparison is always true when first comparison is true.
2010-10-25 03:14:21 +02:00
//---------------------------------------------------------------------------
2012-03-12 19:06:30 +01:00
enum Position { First , Second , NA } ;
enum Relation { Equal , NotEqual , Less , LessEqual , More , MoreEqual } ;
struct Condition {
Position position ;
const char * opTokStr ;
} ;
static std : : string invertOperatorForOperandSwap ( std : : string s )
{
for ( std : : string : : size_type i = 0 ; i < s . length ( ) ; i + + ) {
if ( s [ i ] = = ' > ' )
s [ i ] = ' < ' ;
else if ( s [ i ] = = ' < ' )
s [ i ] = ' > ' ;
}
return s ;
}
static bool analyzeLogicOperatorCondition ( const Condition & c1 , const Condition & c2 ,
bool inv1 , bool inv2 ,
bool varFirst1 , bool varFirst2 ,
const std : : string & firstConstant , const std : : string & secondConstant ,
const Token * op1Tok , const Token * op3Tok ,
Relation relation )
{
if ( ! ( c1 . position = = NA | | ( c1 . position = = First & & varFirst1 ) | | ( c1 . position = = Second & & ! varFirst1 ) ) )
return false ;
if ( ! ( c2 . position = = NA | | ( c2 . position = = First & & varFirst2 ) | | ( c2 . position = = Second & & ! varFirst2 ) ) )
return false ;
if ( ! Token : : Match ( op1Tok , inv1 ? invertOperatorForOperandSwap ( c1 . opTokStr ) . c_str ( ) : c1 . opTokStr ) )
return false ;
if ( ! Token : : Match ( op3Tok , inv2 ? invertOperatorForOperandSwap ( c2 . opTokStr ) . c_str ( ) : c2 . opTokStr ) )
return false ;
return ( relation = = Equal & & MathLib : : isEqual ( firstConstant , secondConstant ) ) | |
( relation = = NotEqual & & MathLib : : isNotEqual ( firstConstant , secondConstant ) ) | |
( relation = = Less & & MathLib : : isLess ( firstConstant , secondConstant ) ) | |
( relation = = LessEqual & & MathLib : : isLessEqual ( firstConstant , secondConstant ) ) | |
( relation = = More & & MathLib : : isGreater ( firstConstant , secondConstant ) ) | |
( relation = = MoreEqual & & MathLib : : isGreaterEqual ( firstConstant , secondConstant ) ) ;
}
2010-10-25 03:14:21 +02:00
void CheckOther : : checkIncorrectLogicOperator ( )
{
2013-03-03 11:41:59 +01:00
bool style = _settings - > isEnabled ( " style " ) ;
bool warning = _settings - > isEnabled ( " warning " ) ;
if ( ! style & & ! warning )
2010-10-25 03:14:21 +02:00
return ;
2012-11-13 18:30:33 +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 ( ) ) {
2013-01-16 15:37:07 +01:00
// Find a pair of comparison expressions with or without parentheses
2012-11-13 18:30:33 +01:00
// with a shared variable and constants and with a logical operator between them.
// e.g. if (x != 3 || x != 4)
const Token * term1Tok = NULL , * term2Tok = NULL ;
const Token * op1Tok = NULL , * op2Tok = NULL , * op3Tok = NULL , * nextTok = NULL ;
2012-12-01 01:31:35 +01:00
if ( Token : : Match ( tok , " ( %any% %comp% %any% ) &&|%oror% " ) ) {
2012-11-13 18:30:33 +01:00
term1Tok = tok - > next ( ) ;
op1Tok = tok - > tokAt ( 2 ) ;
op2Tok = tok - > tokAt ( 5 ) ;
2012-12-01 01:31:35 +01:00
} else if ( Token : : Match ( tok , " %any% %comp% %any% &&|%oror% " ) ) {
2012-11-13 18:30:33 +01:00
term1Tok = tok ;
op1Tok = tok - > next ( ) ;
op2Tok = tok - > tokAt ( 3 ) ;
}
if ( op2Tok ) {
2012-12-01 01:31:35 +01:00
if ( Token : : Match ( op2Tok - > next ( ) , " ( %any% %comp% %any% ) %any% " ) ) {
2012-11-13 18:30:33 +01:00
term2Tok = op2Tok - > tokAt ( 2 ) ;
op3Tok = op2Tok - > tokAt ( 3 ) ;
nextTok = op2Tok - > tokAt ( 6 ) ;
2012-12-01 01:31:35 +01:00
} else if ( Token : : Match ( op2Tok - > next ( ) , " %any% %comp% %any% %any% " ) ) {
2012-11-13 18:30:33 +01:00
term2Tok = op2Tok - > next ( ) ;
op3Tok = op2Tok - > tokAt ( 2 ) ;
nextTok = op2Tok - > tokAt ( 4 ) ;
}
2012-03-12 19:06:30 +01:00
}
2010-10-25 03:14:21 +02:00
2012-11-13 18:30:33 +01:00
if ( nextTok ) {
// Find the common variable and the two different-valued constants
std : : string firstConstant , secondConstant ;
bool varFirst1 , varFirst2 ;
unsigned int varId ;
const Token * var1Tok = NULL , * var2Tok = NULL ;
if ( Token : : Match ( term1Tok , " %var% %any% %num% " ) ) {
var1Tok = term1Tok ;
varId = var1Tok - > varId ( ) ;
if ( ! varId ) {
continue ;
}
varFirst1 = true ;
firstConstant = term1Tok - > strAt ( 2 ) ;
} else if ( Token : : Match ( term1Tok , " %num% %any% %var% " ) ) {
var1Tok = term1Tok - > tokAt ( 2 ) ;
varId = var1Tok - > varId ( ) ;
if ( ! varId ) {
continue ;
}
varFirst1 = false ;
firstConstant = term1Tok - > str ( ) ;
} else {
2010-12-29 10:07:28 +01:00
continue ;
2011-01-16 22:57:29 +01:00
}
2012-11-13 18:30:33 +01:00
if ( Token : : Match ( term2Tok , " %var% %any% %num% " ) ) {
var2Tok = term2Tok ;
varFirst2 = true ;
secondConstant = term2Tok - > strAt ( 2 ) ;
} else if ( Token : : Match ( term2Tok , " %num% %any% %var% " ) ) {
var2Tok = term2Tok - > tokAt ( 2 ) ;
varFirst2 = false ;
secondConstant = term2Tok - > str ( ) ;
} else {
2011-07-17 04:06:23 +02:00
continue ;
2010-11-21 09:06:43 +01:00
}
2012-11-13 18:30:33 +01:00
if ( varId ! = var2Tok - > varId ( ) | | firstConstant . empty ( ) | | secondConstant . empty ( ) ) {
2011-07-17 04:06:23 +02:00
continue ;
2012-11-13 18:30:33 +01:00
}
2011-07-17 04:06:23 +02:00
2012-11-13 18:30:33 +01:00
enum LogicError { AlwaysFalse , AlwaysTrue , FirstTrue , FirstFalse , SecondTrue , SecondFalse } ;
static const struct LinkedConditions {
const char * before ;
Condition c1 ;
const char * op2TokStr ;
Condition c2 ;
const char * after ;
Relation relation ;
LogicError error ;
} conditions [ ] = {
{ " !!&& " , { NA , " != " } , " %oror% " , { NA , " != " } , " !!&& " , NotEqual , AlwaysTrue } , // (x != 1) || (x != 3) <- always true
{ 0 , { NA , " == " } , " && " , { NA , " == " } , 0 , NotEqual , AlwaysFalse } , // (x == 1) && (x == 3) <- always false
{ " !!&& " , { First , " > " } , " %oror% " , { First , " < " } , " !!&& " , Less , AlwaysTrue } , // (x > 3) || (x < 10) <- always true
{ " !!&& " , { First , " >= " } , " %oror% " , { First , " <|<= " } , " !!&& " , LessEqual , AlwaysTrue } , // (x >= 3) || (x < 10) <- always true
{ " !!&& " , { First , " > " } , " %oror% " , { First , " <= " } , " !!&& " , LessEqual , AlwaysTrue } , // (x > 3) || (x <= 10) <- always true
{ 0 , { First , " < " } , " && " , { First , " > " } , 0 , LessEqual , AlwaysFalse } , // (x < 1) && (x > 3) <- always false
{ 0 , { First , " <= " } , " && " , { First , " >|>= " } , 0 , Less , AlwaysFalse } , // (x <= 1) && (x > 3) <- always false
{ 0 , { First , " < " } , " && " , { First , " >= " } , 0 , Less , AlwaysFalse } , // (x < 1) && (x >= 3) <- always false
{ 0 , { First , " > " } , " && " , { NA , " == " } , 0 , MoreEqual , AlwaysFalse } , // (x > 5) && (x == 1) <- always false
{ 0 , { First , " < " } , " && " , { NA , " == " } , 0 , LessEqual , AlwaysFalse } , // (x < 1) && (x == 3) <- always false
{ 0 , { First , " >= " } , " && " , { NA , " == " } , 0 , More , AlwaysFalse } , // (x >= 5) && (x == 1) <- always false
{ 0 , { First , " <= " } , " && " , { NA , " == " } , 0 , Less , AlwaysFalse } , // (x <= 1) && (x == 3) <- always false
{ " !!&& " , { NA , " == " } , " %oror% " , { First , " > " } , " !!&& " , More , SecondTrue } , // (x == 4) || (x > 3) <- second expression always true
{ " !!&& " , { NA , " == " } , " %oror% " , { First , " < " } , " !!&& " , Less , SecondTrue } , // (x == 4) || (x < 5) <- second expression always true
{ " !!&& " , { NA , " == " } , " %oror% " , { First , " >= " } , " !!&& " , MoreEqual , SecondTrue } , // (x == 4) || (x >= 3) <- second expression always true
{ " !!&& " , { NA , " == " } , " %oror% " , { First , " <= " } , " !!&& " , LessEqual , SecondTrue } , // (x == 4) || (x <= 5) <- second expression always true
{ " !!&& " , { First , " > " } , " %oror% " , { NA , " != " } , " !!&& " , MoreEqual , SecondTrue } , // (x > 5) || (x != 1) <- second expression always true
{ " !!&& " , { First , " < " } , " %oror% " , { NA , " != " } , " !!&& " , LessEqual , SecondTrue } , // (x < 1) || (x != 3) <- second expression always true
{ " !!&& " , { First , " >= " } , " %oror% " , { NA , " != " } , " !!&& " , More , SecondTrue } , // (x >= 5) || (x != 1) <- second expression always true
{ " !!&& " , { First , " <= " } , " %oror% " , { NA , " != " } , " !!&& " , Less , SecondTrue } , // (x <= 1) || (x != 3) <- second expression always true
{ 0 , { First , " > " } , " && " , { NA , " != " } , 0 , MoreEqual , SecondTrue } , // (x > 5) && (x != 1) <- second expression always true
{ 0 , { First , " < " } , " && " , { NA , " != " } , 0 , LessEqual , SecondTrue } , // (x < 1) && (x != 3) <- second expression always true
{ 0 , { First , " >= " } , " && " , { NA , " != " } , 0 , More , SecondTrue } , // (x >= 5) && (x != 1) <- second expression always true
{ 0 , { First , " <= " } , " && " , { NA , " != " } , 0 , Less , SecondTrue } , // (x <= 1) && (x != 3) <- second expression always true
{ " !!&& " , { First , " >|>= " } , " %oror% " , { First , " >|>= " } , " !!&& " , LessEqual , SecondTrue } , // (x > 4) || (x > 5) <- second expression always true
{ " !!&& " , { First , " <|<= " } , " %oror% " , { First , " <|<= " } , " !!&& " , MoreEqual , SecondTrue } , // (x < 5) || (x < 4) <- second expression always true
{ 0 , { First , " >|>= " } , " && " , { First , " >|>= " } , 0 , MoreEqual , SecondTrue } , // (x > 4) && (x > 5) <- second expression always true
{ 0 , { First , " <|<= " } , " && " , { First , " <|<= " } , 0 , MoreEqual , SecondTrue } , // (x < 5) && (x < 4) <- second expression always true
{ 0 , { NA , " == " } , " && " , { NA , " != " } , 0 , NotEqual , SecondTrue } , // (x == 3) && (x != 4) <- second expression always true
{ " !!&& " , { NA , " == " } , " %oror% " , { NA , " != " } , " !!&& " , NotEqual , SecondTrue } , // (x == 3) || (x != 4) <- second expression always true
{ 0 , { NA , " != " } , " && " , { NA , " == " } , 0 , Equal , AlwaysFalse } , // (x != 3) && (x == 3) <- expression always false
{ " !!&& " , { NA , " != " } , " %oror% " , { NA , " == " } , " !!&& " , Equal , AlwaysTrue } , // (x != 3) || (x == 3) <- expression always true
} ;
for ( unsigned int i = 0 ; i < ( sizeof ( conditions ) / sizeof ( conditions [ 0 ] ) ) ; i + + ) {
if ( ! Token : : Match ( op2Tok , conditions [ i ] . op2TokStr ) )
continue ;
2011-07-17 04:06:23 +02:00
2012-11-13 18:30:33 +01:00
if ( conditions [ i ] . before ! = 0 & & ! Token : : Match ( tok - > previous ( ) , conditions [ i ] . before ) )
continue ;
2011-07-17 04:06:23 +02:00
2012-11-13 18:30:33 +01:00
if ( conditions [ i ] . after ! = 0 & & ! Token : : Match ( nextTok , conditions [ i ] . after ) )
continue ;
2012-04-02 11:21:04 +02:00
2012-11-13 18:30:33 +01:00
if ( tok - > previous ( ) - > isArithmeticalOp ( ) | | nextTok - > isArithmeticalOp ( ) )
continue ;
std : : string cond1str = var1Tok - > str ( ) + " " + ( varFirst1 ? op1Tok - > str ( ) : invertOperatorForOperandSwap ( op1Tok - > str ( ) ) ) + " " + firstConstant ;
std : : string cond2str = var2Tok - > str ( ) + " " + ( varFirst2 ? op3Tok - > str ( ) : invertOperatorForOperandSwap ( op3Tok - > str ( ) ) ) + " " + secondConstant ;
// cond1 op cond2
bool error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , false , false ,
varFirst1 , varFirst2 , firstConstant , secondConstant ,
op1Tok , op3Tok ,
conditions [ i ] . relation ) ;
// inv(cond1) op cond2 // invert first condition
if ( ! error & & conditions [ i ] . c1 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , true , false ,
! varFirst1 , varFirst2 , firstConstant , secondConstant ,
op1Tok , op3Tok ,
conditions [ i ] . relation ) ;
// cond1 op inv(cond2) // invert second condition
if ( ! error & & conditions [ i ] . c2 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , false , true ,
varFirst1 , ! varFirst2 , firstConstant , secondConstant ,
op1Tok , op3Tok ,
conditions [ i ] . relation ) ;
// inv(cond1) op inv(cond2) // invert both conditions
if ( ! error & & conditions [ i ] . c1 . position ! = NA & & conditions [ i ] . c2 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , true , true ,
! varFirst1 , ! varFirst2 , firstConstant , secondConstant ,
op1Tok , op3Tok ,
conditions [ i ] . relation ) ;
if ( ! error )
std : : swap ( cond1str , cond2str ) ;
// cond2 op cond1 // swap conditions
if ( ! error )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , false , false ,
varFirst2 , varFirst1 , secondConstant , firstConstant ,
op3Tok , op1Tok ,
conditions [ i ] . relation ) ;
// cond2 op inv(cond1) // swap conditions; invert first condition
if ( ! error & & conditions [ i ] . c1 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , true , false ,
! varFirst2 , varFirst1 , secondConstant , firstConstant ,
op3Tok , op1Tok ,
conditions [ i ] . relation ) ;
// inv(cond2) op cond1 // swap conditions; invert second condition
if ( ! error & & conditions [ i ] . c2 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , false , true ,
varFirst2 , ! varFirst1 , secondConstant , firstConstant ,
op3Tok , op1Tok ,
conditions [ i ] . relation ) ;
// inv(cond2) op inv(cond1) // swap conditions; invert both conditions
if ( ! error & & conditions [ i ] . c1 . position ! = NA & & conditions [ i ] . c2 . position ! = NA )
error = analyzeLogicOperatorCondition ( conditions [ i ] . c1 , conditions [ i ] . c2 , true , true ,
! varFirst2 , ! varFirst1 , secondConstant , firstConstant ,
op3Tok , op1Tok ,
conditions [ i ] . relation ) ;
if ( error ) {
if ( conditions [ i ] . error = = AlwaysFalse | | conditions [ i ] . error = = AlwaysTrue ) {
2013-03-03 11:41:59 +01:00
if ( warning ) {
const std : : string text = cond1str + " " + op2Tok - > str ( ) + " " + cond2str ;
incorrectLogicOperatorError ( term1Tok , text , conditions [ i ] . error = = AlwaysTrue ) ;
}
2012-11-13 18:30:33 +01:00
} else {
2013-03-03 11:41:59 +01:00
if ( style ) {
const std : : string text = " If " + cond1str + " , the comparison " + cond2str +
" is always " + ( ( conditions [ i ] . error = = SecondTrue | | conditions [ i ] . error = = AlwaysTrue ) ? " true " : " false " ) + " . " ;
redundantConditionError ( term1Tok , text ) ;
}
2012-11-13 18:30:33 +01:00
}
break ;
2011-08-19 19:28:37 +02:00
}
}
2010-10-25 03:14:21 +02:00
}
}
}
}
2012-03-15 20:38:28 +01:00
void CheckOther : : incorrectLogicOperatorError ( const Token * tok , const std : : string & condition , bool always )
2011-08-19 17:53:43 +02:00
{
if ( always )
2012-03-15 20:38:28 +01:00
reportError ( tok , Severity : : warning , " incorrectLogicOperator " ,
" Logical disjunction always evaluates to true: " + condition + " . \n "
2012-11-10 11:31:36 +01:00
" Logical disjunction always evaluates to true: " + condition + " . "
2012-03-15 20:38:28 +01:00
" Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables? " ) ;
2011-08-19 17:53:43 +02:00
else
2012-03-15 20:38:28 +01:00
reportError ( tok , Severity : : warning , " incorrectLogicOperator " ,
" Logical conjunction always evaluates to false: " + condition + " . \n "
2012-11-10 11:31:36 +01:00
" Logical conjunction always evaluates to false: " + condition + " . "
2012-03-15 20:38:28 +01:00
" Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables? " ) ;
2011-08-19 17:53:43 +02:00
}
2012-03-15 20:38:28 +01:00
void CheckOther : : redundantConditionError ( const Token * tok , const std : : string & text )
2011-08-19 19:28:37 +02:00
{
2012-03-15 20:38:28 +01:00
reportError ( tok , Severity : : style , " redundantCondition " , " Redundant condition: " + text ) ;
2011-08-19 19:28:37 +02:00
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// strtol(str, 0, radix) <- radix must be 0 or 2-36
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : invalidFunctionUsage ( )
2009-01-26 20:14:46 +01:00
{
// strtol and strtoul..
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2012-11-06 19:54:52 +01:00
if ( ! Token : : Match ( tok , " strtol|strtoul|strtoll|strtoull|wcstol|wcstoul|wcstoll|wcstoull ( " ) )
2009-01-26 20:14:46 +01:00
continue ;
2012-09-16 19:44:02 +02:00
const std : : string & funcname = tok - > str ( ) ;
2012-03-03 21:14:20 +01:00
tok = tok - > tokAt ( 2 ) ;
2009-01-26 20:14:46 +01:00
// Locate the third parameter of the function call..
2012-03-03 21:14:20 +01:00
for ( int i = 0 ; i < 2 & & tok ; i + + )
tok = tok - > nextArgument ( ) ;
if ( Token : : Match ( tok , " %num% ) " ) ) {
const MathLib : : bigint radix = MathLib : : toLongNumber ( tok - > str ( ) ) ;
if ( ! ( radix = = 0 | | ( radix > = 2 & & radix < = 36 ) ) ) {
2012-09-16 19:44:02 +02:00
dangerousUsageStrtolError ( tok , funcname ) ;
2009-01-26 20:14:46 +01:00
}
2012-03-03 21:14:20 +01:00
} else
break ;
2009-01-26 20:14:46 +01:00
}
// sprintf|snprintf overlapping data
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2009-01-26 20:14:46 +01:00
// Get variable id of target buffer..
unsigned int varid = 0 ;
2012-11-06 19:54:52 +01:00
if ( Token : : Match ( tok , " sprintf|snprintf|swprintf ( %var% , " ) )
2009-01-26 20:14:46 +01:00
varid = tok - > tokAt ( 2 ) - > varId ( ) ;
2012-11-06 19:54:52 +01:00
else if ( Token : : Match ( tok , " sprintf|snprintf|swprintf ( %var% . %var% , " ) )
2009-01-26 20:14:46 +01:00
varid = tok - > tokAt ( 4 ) - > varId ( ) ;
2010-04-02 07:30:58 +02:00
if ( varid = = 0 )
2009-01-26 20:14:46 +01:00
continue ;
// goto ","
const Token * tok2 = tok - > tokAt ( 3 ) ;
2011-11-26 21:15:16 +01:00
while ( tok2 - > str ( ) ! = " , " )
2009-01-26 20:14:46 +01:00
tok2 = tok2 - > next ( ) ;
2011-11-26 21:15:16 +01:00
tok2 = tok2 - > next ( ) ; // Jump behind ","
2012-11-06 19:54:52 +01:00
if ( tok - > str ( ) = = " snprintf " | | tok - > str ( ) = = " swprintf " ) { // Jump over second parameter for snprintf and swprintf
2011-11-26 21:15:16 +01:00
tok2 = tok2 - > nextArgument ( ) ;
if ( ! tok2 )
continue ;
}
2009-01-26 20:14:46 +01:00
// is any source buffer overlapping the target buffer?
2011-11-26 21:15:16 +01:00
do {
if ( Token : : Match ( tok2 , " %varid% [,)] " , varid ) ) {
2012-09-16 19:44:02 +02:00
sprintfOverlappingDataError ( tok2 , tok2 - > str ( ) ) ;
2009-01-26 20:14:46 +01:00
break ;
}
2011-12-01 10:48:14 +01:00
} while ( NULL ! = ( tok2 = tok2 - > nextArgument ( ) ) ) ;
2009-01-26 20:14:46 +01:00
}
}
2012-09-16 19:44:02 +02:00
void CheckOther : : dangerousUsageStrtolError ( const Token * tok , const std : : string & funcname )
2011-08-19 17:53:43 +02:00
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " dangerousUsageStrtol " , " Invalid radix in call to " + funcname + " (). It must be 0 or 2-36. " ) ;
2011-08-19 17:53:43 +02:00
}
void CheckOther : : sprintfOverlappingDataError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error , " sprintfOverlappingData " ,
2012-09-16 19:44:02 +02:00
" Undefined behavior: Variable ' " + varname + " ' is used as parameter and destination in s[n]printf(). \n "
" The variable ' " + varname + " ' is used both as a parameter and as destination in "
2011-08-19 17:53:43 +02:00
" s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) "
" documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): "
2012-09-16 19:44:02 +02:00
" \" If copying takes place between objects that overlap as a result of a call "
" to sprintf() or snprintf(), the results are undefined. \" " ) ;
2011-08-19 17:53:43 +02:00
}
2011-07-15 02:12:56 +02:00
//---------------------------------------------------------------------------
2011-12-03 11:43:23 +01:00
// Find consecutive return, break, continue, goto or throw statements. e.g.:
// break; break;
// Detect dead code, that follows such a statement. e.g.:
// return(0); foo();
2011-07-15 02:12:56 +02:00
//---------------------------------------------------------------------------
2011-12-03 11:43:23 +01:00
void CheckOther : : checkUnreachableCode ( )
2011-07-15 02:12:56 +02:00
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-07-15 02:12:56 +02:00
return ;
2011-12-03 11:43:23 +01:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
const Token * secondBreak = 0 ;
2011-12-31 23:11:03 +01:00
const Token * labelName = 0 ;
2011-12-07 18:20:52 +01:00
if ( tok - > str ( ) = = " ( " )
tok = tok - > link ( ) ;
else if ( Token : : Match ( tok , " break|continue ; " ) )
2011-12-03 11:43:23 +01:00
secondBreak = tok - > tokAt ( 2 ) ;
2011-12-07 18:20:52 +01:00
else if ( Token : : Match ( tok , " [;{}:] return|throw " ) ) {
tok = tok - > next ( ) ; // tok should point to return or throw
2013-05-11 17:50:59 +02:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ( " )
tok2 = tok2 - > link ( ) ;
2011-12-03 11:43:23 +01:00
if ( tok2 - > str ( ) = = " ; " ) {
2011-12-07 18:20:52 +01:00
secondBreak = tok2 - > next ( ) ;
2011-12-03 11:43:23 +01:00
break ;
}
2013-05-11 17:50:59 +02:00
}
2011-12-31 23:11:03 +01:00
} else if ( Token : : Match ( tok , " goto %any% ; " ) ) {
2011-12-03 11:43:23 +01:00
secondBreak = tok - > tokAt ( 3 ) ;
2012-05-19 10:51:47 +02:00
labelName = tok - > next ( ) ;
2011-12-31 23:11:03 +01:00
}
2011-12-03 11:43:23 +01:00
2012-04-05 10:38:29 +02:00
// Statements follow directly, no line between them. (#3383)
// TODO: Try to find a better way to avoid false positives due to preprocessor configurations.
bool inconclusive = secondBreak & & ( secondBreak - > linenr ( ) - 1 > secondBreak - > previous ( ) - > linenr ( ) ) ;
if ( secondBreak & & ( _settings - > inconclusive | | ! inconclusive ) ) {
2011-12-03 11:43:23 +01:00
if ( Token : : Match ( secondBreak , " continue|goto|throw " ) | |
( secondBreak - > str ( ) = = " return " & & ( tok - > str ( ) = = " return " | | secondBreak - > strAt ( 1 ) = = " ; " ) ) ) { // return with value after statements like throw can be necessary to make a function compile
2012-04-05 10:38:29 +02:00
duplicateBreakError ( secondBreak , inconclusive ) ;
2011-12-03 11:43:23 +01:00
tok = Token : : findmatch ( secondBreak , " [}:] " ) ;
} else if ( secondBreak - > str ( ) = = " break " ) { // break inside switch as second break statement should not issue a warning
if ( tok - > str ( ) = = " break " ) // If the previous was a break, too: Issue warning
2012-04-05 10:38:29 +02:00
duplicateBreakError ( secondBreak , inconclusive ) ;
2011-12-03 11:43:23 +01:00
else {
2012-08-12 12:13:07 +02:00
if ( tok - > scope ( ) - > type ! = Scope : : eSwitch ) // Check, if the enclosing scope is a switch
duplicateBreakError ( secondBreak , inconclusive ) ;
2011-12-03 11:43:23 +01:00
}
tok = Token : : findmatch ( secondBreak , " [}:] " ) ;
} else if ( ! Token : : Match ( secondBreak , " return|}|case|default " ) & & secondBreak - > strAt ( 1 ) ! = " : " ) { // TODO: No bailout for unconditional scopes
2011-12-31 23:11:03 +01:00
// If the goto label is followed by a loop construct in which the label is defined it's quite likely
// that the goto jump was intended to skip some code on the first loop iteration.
bool labelInFollowingLoop = false ;
if ( labelName & & Token : : Match ( secondBreak , " while|do|for " ) ) {
2012-01-21 19:55:32 +01:00
const Token * scope = Token : : findsimplematch ( secondBreak , " { " ) ;
2011-12-31 23:11:03 +01:00
if ( scope ) {
for ( const Token * tokIter = scope ; tokIter ! = scope - > link ( ) & & tokIter ; tokIter = tokIter - > next ( ) ) {
if ( Token : : Match ( tokIter , " [;{}] %any% : " ) & & labelName - > str ( ) = = tokIter - > strAt ( 1 ) ) {
labelInFollowingLoop = true ;
break ;
}
}
}
}
if ( ! labelInFollowingLoop )
2012-04-05 10:38:29 +02:00
unreachableCodeError ( secondBreak , inconclusive ) ;
2011-12-03 11:43:23 +01:00
tok = Token : : findmatch ( secondBreak , " [}:] " ) ;
} else
tok = secondBreak ;
2011-12-23 12:46:59 +01:00
if ( ! tok )
break ;
2011-12-03 11:43:23 +01:00
}
2011-07-15 02:12:56 +02:00
}
}
2012-04-05 10:38:29 +02:00
void CheckOther : : duplicateBreakError ( const Token * tok , bool inconclusive )
2010-08-14 15:15:12 +02:00
{
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : style , " duplicateBreak " ,
" Consecutive return, break, continue, goto or throw statements are unnecessary. \n "
2012-09-16 19:44:02 +02:00
" Consecutive return, break, continue, goto or throw statements are unnecessary. "
" The second statement can never be executed, and so should be removed. " , inconclusive ) ;
2010-08-14 15:15:12 +02:00
}
2009-01-26 20:14:46 +01:00
2012-04-05 10:38:29 +02:00
void CheckOther : : unreachableCodeError ( const Token * tok , bool inconclusive )
2011-12-03 11:43:23 +01:00
{
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : style , " unreachableCode " ,
" Statements following return, break, continue, goto or throw will never be executed. " , inconclusive ) ;
2011-12-03 11:43:23 +01:00
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// Check for unsigned divisions
//---------------------------------------------------------------------------
2013-01-30 16:50:12 +01:00
bool CheckOther : : isUnsigned ( const Variable * var ) const
2011-12-17 19:04:03 +01:00
{
2013-01-30 16:50:12 +01:00
return ( var & & var - > typeStartToken ( ) - > isUnsigned ( ) & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & _tokenizer - > sizeOfType ( var - > typeStartToken ( ) ) > = _settings - > sizeof_int ) ;
2012-01-28 12:32:28 +01:00
}
2013-03-12 15:42:00 +01:00
bool CheckOther : : isSigned ( const Variable * var )
2012-01-28 12:32:28 +01:00
{
return ( var & & ! var - > typeStartToken ( ) - > isUnsigned ( ) & & Token : : Match ( var - > typeEndToken ( ) , " int|char|short|long " ) & & ! var - > isPointer ( ) & & ! var - > isArray ( ) ) ;
2011-12-17 19:04:03 +01:00
}
2009-07-05 22:16:43 +02:00
void CheckOther : : checkUnsignedDivision ( )
2009-01-26 20:14:46 +01:00
{
2013-03-03 11:41:59 +01:00
bool warning = _settings - > isEnabled ( " warning " ) ;
2011-12-17 19:04:03 +01:00
2012-11-15 07:11:45 +01:00
const SymbolDatabase * 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 ] ;
const Token * ifTok = 0 ;
// Check for "ivar / uvar" and "uvar / ivar"
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-01-28 12:32:28 +01:00
2012-11-15 07:11:45 +01:00
if ( Token : : Match ( tok , " [).] " ) ) // Don't check members or casted variables
continue ;
if ( Token : : Match ( tok - > next ( ) , " %var% / %num% " ) ) {
2013-02-06 06:39:58 +01:00
if ( tok - > strAt ( 3 ) [ 0 ] = = ' - ' & & isUnsigned ( tok - > next ( ) - > variable ( ) ) ) {
2012-11-15 07:11:45 +01:00
udivError ( tok - > next ( ) , false ) ;
}
} else if ( Token : : Match ( tok - > next ( ) , " %num% / %var% " ) ) {
2013-02-06 06:39:58 +01:00
if ( tok - > strAt ( 1 ) [ 0 ] = = ' - ' & & isUnsigned ( tok - > tokAt ( 3 ) - > variable ( ) ) ) {
2012-11-15 07:11:45 +01:00
udivError ( tok - > next ( ) , false ) ;
}
2013-03-03 11:41:59 +01:00
} else if ( Token : : Match ( tok - > next ( ) , " %var% / %var% " ) & & _settings - > inconclusive & & warning & & ! ifTok ) {
2013-02-06 06:39:58 +01:00
const Variable * var1 = tok - > next ( ) - > variable ( ) ;
const Variable * var2 = tok - > tokAt ( 3 ) - > variable ( ) ;
2012-11-15 07:11:45 +01:00
if ( ( isUnsigned ( var1 ) & & isSigned ( var2 ) ) | | ( isUnsigned ( var2 ) & & isSigned ( var1 ) ) ) {
udivError ( tok - > next ( ) , true ) ;
}
} else if ( ! ifTok & & Token : : simpleMatch ( tok , " if ( " ) )
ifTok = tok - > next ( ) - > link ( ) - > next ( ) - > link ( ) ;
else if ( ifTok = = tok )
ifTok = 0 ;
}
2009-01-26 20:14:46 +01:00
}
}
2011-01-06 11:31:58 +01:00
2012-01-28 12:32:28 +01:00
void CheckOther : : udivError ( const Token * tok , bool inconclusive )
2011-08-19 17:53:43 +02:00
{
2012-01-28 12:32:28 +01:00
if ( inconclusive )
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : warning , " udivError " , " Division with signed and unsigned operators. The result might be wrong. " , true ) ;
2012-01-28 12:32:28 +01:00
else
reportError ( tok , Severity : : error , " udivError " , " Unsigned division. The result will be wrong. " ) ;
2011-08-19 17:53:43 +02:00
}
2011-01-06 11:31:58 +01:00
//---------------------------------------------------------------------------
// memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted
//---------------------------------------------------------------------------
void CheckOther : : checkMemsetZeroBytes ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2011-12-16 19:21:14 +01:00
return ;
2012-11-15 07:11:45 +01:00
const SymbolDatabase * 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 : : simpleMatch ( tok , " memset ( " ) ) {
const Token * lastParamTok = tok - > next ( ) - > link ( ) - > previous ( ) ;
if ( lastParamTok - > str ( ) = = " 0 " )
memsetZeroBytesError ( tok , tok - > strAt ( 2 ) ) ;
}
2011-10-22 22:34:10 +02:00
}
2011-01-06 11:31:58 +01:00
}
}
2009-01-26 20:14:46 +01:00
2011-08-19 17:53:43 +02:00
void CheckOther : : memsetZeroBytesError ( const Token * tok , const std : : string & varname )
{
2012-09-16 19:44:02 +02:00
const std : : string summary ( " memset() called to fill 0 bytes of ' " + varname + " '. " ) ;
2013-03-29 19:27:54 +01:00
const std : : string verbose ( summary + " Second and third arguments might be inverted. " ) ;
2011-08-19 17:53:43 +02:00
reportError ( tok , Severity : : warning , " memsetZeroBytes " , summary + " \n " + verbose ) ;
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// Check scope of variables..
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : checkVariableScope ( )
2009-01-26 20:14:46 +01:00
{
2011-10-30 10:54:33 +01:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-04-21 08:38:25 +02:00
return ;
2011-01-16 18:13:54 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-12-16 19:04:47 +01:00
2012-05-24 15:34:59 +02:00
for ( unsigned int i = 1 ; i < symbolDatabase - > getVariableListSize ( ) ; i + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( i ) ;
2012-08-20 17:57:28 +02:00
if ( ! var | | ! var - > isLocal ( ) | | ( ! var - > isPointer ( ) & & ! var - > typeStartToken ( ) - > isStandardType ( ) & & ! var - > typeStartToken ( ) - > next ( ) - > isStandardType ( ) ) )
2010-12-16 19:04:47 +01:00
continue ;
2012-05-24 15:34:59 +02:00
bool forHead = false ; // Don't check variables declared in header of a for loop
for ( const Token * tok = var - > typeStartToken ( ) ; tok ; tok = tok - > previous ( ) ) {
if ( tok - > str ( ) = = " ( " ) {
forHead = true ;
break ;
} else if ( tok - > str ( ) = = " { " || tok->str() == " ; " || tok->str() == " } " )
break ;
2009-01-26 20:14:46 +01:00
}
2012-05-24 15:34:59 +02:00
if ( forHead )
continue ;
const Token * tok = var - > nameToken ( ) - > next ( ) ;
if ( Token : : Match ( tok , " ; %varid% = %any% ; " , var - > varId ( ) ) ) {
tok = tok - > tokAt ( 3 ) ;
if ( ! tok - > isNumber ( ) & & tok - > type ( ) ! = Token : : eString & & tok - > type ( ) ! = Token : : eChar & & ! tok - > isBoolean ( ) )
continue ;
2012-07-29 15:50:05 +02:00
} else if ( ( tok - > str ( ) = = " = " | | tok - > str ( ) = = " ( " ) & &
2012-05-24 15:34:59 +02:00
( ( ! tok - > next ( ) - > isNumber ( ) & & tok - > next ( ) - > type ( ) ! = Token : : eString & & tok - > next ( ) - > type ( ) ! = Token : : eChar & & ! tok - > next ( ) - > isBoolean ( ) ) | | tok - > strAt ( 2 ) ! = " ; " ) )
continue ;
2009-01-26 20:14:46 +01:00
2013-03-03 19:35:33 +01:00
bool reduce = true ;
bool used = false ; // Don't warn about unused variables
for ( ; tok ! = var - > scope ( ) - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " { " & & tok - > strAt ( - 1 ) ! = " = " ) {
2013-03-18 16:17:53 +01:00
if ( used ) {
bool used2 = false ;
if ( ! checkInnerScope ( tok , var , used2 ) | | used2 ) {
reduce = false ;
break ;
}
} else if ( ! checkInnerScope ( tok , var , used ) ) {
2013-03-03 19:35:33 +01:00
reduce = false ;
break ;
2011-01-17 20:51:15 +01:00
}
2013-03-18 16:17:53 +01:00
2011-01-17 20:51:15 +01:00
tok = tok - > link ( ) ;
2013-03-03 19:35:33 +01:00
} else if ( tok - > varId ( ) = = var - > varId ( ) | | tok - > str ( ) = = " goto " ) {
reduce = false ;
2009-06-12 16:17:51 +02:00
break ;
2009-01-26 20:14:46 +01:00
}
}
2013-03-03 19:35:33 +01:00
if ( reduce & & used )
variableScopeError ( var - > nameToken ( ) , var - > name ( ) ) ;
}
}
2009-01-26 20:14:46 +01:00
2013-03-03 19:35:33 +01:00
bool CheckOther : : checkInnerScope ( const Token * tok , const Variable * var , bool & used )
{
const Scope * scope = tok - > next ( ) - > scope ( ) ;
bool loopVariable = scope - > type = = Scope : : eFor | | scope - > type = = Scope : : eWhile | | scope - > type = = Scope : : eDo ;
bool noContinue = true ;
const Token * forHeadEnd = 0 ;
const Token * end = tok - > link ( ) ;
if ( scope - > type = = Scope : : eUnconditional & & ( tok - > strAt ( - 1 ) = = " ) " | | tok - > previous ( ) - > isName ( ) ) ) // Might be an unknown macro like BOOST_FOREACH
loopVariable = true ;
if ( scope - > type = = Scope : : eDo ) {
end = end - > linkAt ( 2 ) ;
} else if ( loopVariable & & tok - > strAt ( - 1 ) = = " ) " ) {
tok = tok - > linkAt ( - 1 ) ; // Jump to opening ( of for/while statement
2013-03-23 11:28:33 +01:00
} else if ( scope - > type = = Scope : : eSwitch ) {
for ( std : : list < Scope * > : : const_iterator i = scope - > nestedList . begin ( ) ; i ! = scope - > nestedList . end ( ) ; + + i ) {
if ( used ) {
bool used2 = false ;
if ( ! checkInnerScope ( ( * i ) - > classStart , var , used2 ) | | used2 ) {
return false ;
}
} else if ( ! checkInnerScope ( ( * i ) - > classStart , var , used ) ) {
return false ;
}
}
}
2013-03-03 19:35:33 +01:00
for ( ; tok ! = end ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " goto " )
return false ;
if ( tok - > str ( ) = = " continue " )
noContinue = false ;
if ( Token : : simpleMatch ( tok , " for ( " ) )
forHeadEnd = tok - > linkAt ( 1 ) ;
if ( tok = = forHeadEnd )
forHeadEnd = 0 ;
2013-03-15 13:00:51 +01:00
if ( loopVariable & & noContinue & & tok - > scope ( ) = = scope & & ! forHeadEnd & & scope - > type ! = Scope : : eSwitch & & Token : : Match ( tok , " %varid% = " , var - > varId ( ) ) ) { // Assigned in outer scope.
2013-03-03 19:35:33 +01:00
loopVariable = false ;
unsigned int indent = 0 ;
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > next ( ) ) { // Ensure that variable isn't used on right side of =, too
if ( tok2 - > str ( ) = = " ( " )
indent + + ;
else if ( tok2 - > str ( ) = = " ) " ) {
if ( indent = = 0 )
break ;
indent - - ;
} else if ( tok2 - > str ( ) = = " ; " )
break ;
else if ( tok2 - > varId ( ) = = var - > varId ( ) ) {
loopVariable = true ;
break ;
}
2009-09-29 23:56:43 +02:00
}
2013-03-03 19:35:33 +01:00
}
2009-09-29 23:56:43 +02:00
2013-03-03 19:35:33 +01:00
if ( loopVariable & & Token : : Match ( tok , " %varid% !!= " , var - > varId ( ) ) ) // Variable used in loop
return false ;
2009-09-29 23:56:43 +02:00
2013-03-03 19:35:33 +01:00
if ( Token : : Match ( tok , " & %varid% " , var - > varId ( ) ) ) // Taking address of variable
return false ;
2010-08-26 21:57:48 +02:00
2013-03-03 19:35:33 +01:00
if ( Token : : Match ( tok , " = %varid% " , var - > varId ( ) ) & & ( var - > isArray ( ) | | var - > isPointer ( ) ) ) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope
return false ;
2009-01-26 20:14:46 +01:00
2013-03-23 11:28:33 +01:00
if ( tok - > varId ( ) = = var - > varId ( ) ) {
2013-03-03 19:35:33 +01:00
used = true ;
2013-03-23 11:28:33 +01:00
if ( scope - > type = = Scope : : eSwitch & & scope = = tok - > scope ( ) )
return false ; // Used in outer switch scope - unsafe or impossible to reduce scope
}
2009-01-26 20:14:46 +01:00
}
2013-03-03 19:35:33 +01:00
return true ;
2009-01-26 20:14:46 +01:00
}
2011-08-19 17:53:43 +02:00
void CheckOther : : variableScopeError ( const Token * tok , const std : : string & varname )
{
reportError ( tok ,
2011-10-05 19:44:00 +02:00
Severity : : style ,
2011-08-19 17:53:43 +02:00
" variableScope " ,
2012-09-16 19:44:02 +02:00
" The scope of the variable ' " + varname + " ' can be reduced. \n "
" The scope of the variable ' " + varname + " ' can be reduced. Warning: Be careful "
" when fixing this message, especially when there are inner loops. Here is an "
2011-08-19 17:53:43 +02:00
" example where cppcheck will write that the scope for 'i' can be reduced: \n "
" void f(int x) \n "
" { \n "
" int i = 0; \n "
" if (x) { \n "
2012-09-16 19:44:02 +02:00
" // it's safe to move 'int i = 0;' here \n "
2011-08-19 17:53:43 +02:00
" for (int n = 0; n < 10; ++n) { \n "
2012-09-16 19:44:02 +02:00
" // it is possible but not safe to move 'int i = 0;' here \n "
2011-08-19 17:53:43 +02:00
" do_something(&i); \n "
" } \n "
" } \n "
" } \n "
" When you see this message it is always safe to reduce the variable scope 1 level. " ) ;
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// Check for constant function parameters
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : checkConstantFunctionParameter ( )
2009-01-26 20:14:46 +01:00
{
2012-09-10 19:02:32 +02:00
if ( ! _settings - > isEnabled ( " performance " ) | | _tokenizer - > isC ( ) )
2010-04-21 08:38:25 +02:00
return ;
2011-01-16 19:57:29 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-05-24 17:40:43 +02:00
for ( unsigned int i = 1 ; i < symbolDatabase - > getVariableListSize ( ) ; i + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( i ) ;
if ( ! var | | ! var - > isArgument ( ) | | ! var - > isClass ( ) | | ! var - > isConst ( ) | | var - > isPointer ( ) | | var - > isArray ( ) | | var - > isReference ( ) )
continue ;
2012-06-08 18:05:02 +02:00
const Token * const tok = var - > typeStartToken ( ) ;
2012-05-24 17:40:43 +02:00
// TODO: False negatives. This pattern only checks for string.
// Investigate if there are other classes in the std
// namespace and add them to the pattern. There are
// streams for example (however it seems strange with
// const stream parameter).
if ( Token : : Match ( tok , " std :: string|wstring " ) ) {
passedByValueError ( tok , var - > name ( ) ) ;
2012-09-07 11:59:20 +02:00
} else if ( Token : : Match ( tok , " std :: %type% < " ) & & ! Token : : simpleMatch ( tok - > linkAt ( 3 ) , " > :: " ) ) {
2012-05-24 17:40:43 +02:00
passedByValueError ( tok , var - > name ( ) ) ;
2013-03-05 15:55:31 +01:00
} else if ( var - > type ( ) ) { // Check if type is a struct or class.
2012-05-24 17:40:43 +02:00
passedByValueError ( tok , var - > name ( ) ) ;
2009-01-26 20:14:46 +01:00
}
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : passedByValueError ( const Token * tok , const std : : string & parname )
{
reportError ( tok , Severity : : performance , " passedByValue " ,
" Function parameter ' " + parname + " ' should be passed by reference. \n "
2012-09-16 19:44:02 +02:00
" Parameter ' " + parname + " ' is passed by value. It could be passed "
2011-08-19 17:53:43 +02:00
" as a (const) reference which is usually faster and recommended in C++. " ) ;
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// Check usage of char variables..
//---------------------------------------------------------------------------
2012-01-28 12:32:28 +01:00
static bool isChar ( const Variable * var )
{
2012-06-08 18:05:02 +02:00
return ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & var - > typeStartToken ( ) - > str ( ) = = " char " ) ;
2012-01-28 12:32:28 +01:00
}
2011-12-17 19:04:03 +01:00
static bool isSignedChar ( const Variable * var )
{
2012-08-20 17:57:28 +02:00
return ( isChar ( var ) & & ! var - > typeStartToken ( ) - > isUnsigned ( ) ) ;
2011-12-17 19:04:03 +01:00
}
2009-07-05 22:16:43 +02:00
void CheckOther : : checkCharVariable ( )
2009-01-26 20:14:46 +01:00
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2010-04-21 08:38:25 +02:00
return ;
2011-08-14 16:39:45 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-15 07:11:45 +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 - > str ( ) ! = " . " ) & & Token : : Match ( tok - > next ( ) , " %var% [ %var% ] " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * arrayvar = tok - > next ( ) - > variable ( ) ;
const Variable * indexvar = tok - > tokAt ( 3 ) - > variable ( ) ;
2012-11-15 07:11:45 +01:00
const MathLib : : bigint arraysize = ( arrayvar & & arrayvar - > isArray ( ) ) ? arrayvar - > dimension ( 0U ) : 0 ;
if ( isSignedChar ( indexvar ) & & arraysize > 0x80 )
charArrayIndexError ( tok - > next ( ) ) ;
}
else if ( Token : : Match ( tok , " [;{}] %var% = %any% [&^|] %any% ; " ) ) {
// is a char variable used in the calculation?
2013-02-06 06:39:58 +01:00
if ( ! isSignedChar ( tok - > tokAt ( 3 ) - > variable ( ) ) & &
! isSignedChar ( tok - > tokAt ( 5 ) - > variable ( ) ) )
2012-11-15 07:11:45 +01:00
continue ;
2011-08-14 16:39:45 +02:00
2012-11-15 07:11:45 +01:00
// it's ok with a bitwise and where the other operand is 0xff or less..
if ( tok - > strAt ( 4 ) = = " & " ) {
if ( tok - > tokAt ( 3 ) - > isNumber ( ) & & MathLib : : isGreater ( " 0x100 " , tok - > strAt ( 3 ) ) )
continue ;
if ( tok - > tokAt ( 5 ) - > isNumber ( ) & & MathLib : : isGreater ( " 0x100 " , tok - > strAt ( 5 ) ) )
continue ;
}
2011-11-06 11:26:28 +01:00
2012-11-15 07:11:45 +01:00
// is the result stored in a short|int|long?
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2012-11-15 07:11:45 +01:00
if ( var & & Token : : Match ( var - > typeStartToken ( ) , " short|int|long " ) & & ! var - > isPointer ( ) & & ! var - > isArray ( ) )
charBitOpError ( tok - > tokAt ( 4 ) ) ; // This is an error..
2011-12-17 19:04:03 +01:00
}
2009-06-20 19:24:58 +02:00
2012-11-15 07:11:45 +01:00
else if ( Token : : Match ( tok , " [;{}] %var% = %any% [&^|] ( * %var% ) ; " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > tokAt ( 7 ) - > variable ( ) ;
2012-11-15 07:11:45 +01:00
if ( ! var | | ! var - > isPointer ( ) | | var - > typeStartToken ( ) - > str ( ) ! = " char " | | var - > typeStartToken ( ) - > isUnsigned ( ) )
continue ;
// it's ok with a bitwise and where the other operand is 0xff or less..
if ( tok - > strAt ( 4 ) = = " & " & & tok - > tokAt ( 3 ) - > isNumber ( ) & & MathLib : : isGreater ( " 0x100 " , tok - > strAt ( 3 ) ) )
continue ;
2011-07-05 18:41:27 +02:00
2012-11-15 07:11:45 +01:00
// is the result stored in a short|int|long?
2013-02-06 06:39:58 +01:00
var = tok - > next ( ) - > variable ( ) ;
2012-11-15 07:11:45 +01:00
if ( var & & Token : : Match ( var - > typeStartToken ( ) , " short|int|long " ) & & ! var - > isPointer ( ) & & ! var - > isArray ( ) )
charBitOpError ( tok - > tokAt ( 4 ) ) ; // This is an error..
}
2009-01-26 20:14:46 +01:00
}
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : charArrayIndexError ( const Token * tok )
{
reportError ( tok ,
Severity : : warning ,
" charArrayIndex " ,
2012-09-16 19:44:02 +02:00
" Signed 'char' type used as array index. \n "
" Signed 'char' type used as array index. If the value "
" can be greater than 127 there will be a buffer underflow "
" because of sign extension. " ) ;
2011-08-19 17:53:43 +02:00
}
2009-01-26 20:14:46 +01:00
2011-08-19 17:53:43 +02:00
void CheckOther : : charBitOpError ( const Token * tok )
{
reportError ( tok ,
Severity : : warning ,
" charBitOp " ,
2012-09-16 19:44:02 +02:00
" When using 'char' variables in bit operations, sign extension can generate unexpected results. \n "
" When using 'char' variables in bit operations, sign extension can generate unexpected results. For example: \n "
2011-08-19 17:53:43 +02:00
" char c = 0x80; \n "
" int i = 0 | c; \n "
" if (i & 0x8000) \n "
" printf( \" not expected \" ); \n "
2012-09-16 19:44:02 +02:00
" The \" not expected \" will be printed on the screen. " ) ;
2011-08-19 17:53:43 +02:00
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// Incomplete statement..
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : checkIncompleteStatement ( )
2009-01-26 20:14:46 +01:00
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2010-04-21 08:38:25 +02:00
return ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " ( " ) {
2011-01-13 20:12:57 +01:00
tok = tok - > link ( ) ;
2011-03-30 21:57:01 +02:00
if ( Token : : simpleMatch ( tok , " ) { " ) & & Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " } ; " ) )
tok = tok - > next ( ) - > link ( ) ;
}
2009-01-26 20:14:46 +01:00
2011-01-13 20:12:57 +01:00
else if ( Token : : simpleMatch ( tok , " = { " ) )
tok = tok - > next ( ) - > link ( ) ;
2009-01-26 20:14:46 +01:00
2013-02-23 16:26:25 +01:00
// C++11 struct/array initialization in initializer list
else if ( tok - > str ( ) = = " { " && Token::Match(tok->tokAt(-2), " , | : % var % " ) && Token::Match(tok->link(), " } [ , { ] " ))
2011-01-19 18:37:33 +01:00
tok = tok - > link ( ) ;
2013-04-20 07:51:44 +02:00
// C++11 vector initialization / return { .. }
else if ( Token : : Match ( tok , " > %var% { " ) | | Token : : Match ( tok , " [ ; { } ] return { " ))
2013-02-24 10:00:03 +01:00
tok = tok - > linkAt ( 2 ) ;
2011-11-05 20:28:52 +01:00
else if ( Token : : Match ( tok , " [;{}] %str% " ) | | Token : : Match ( tok , " [;{}] %num% " ) ) {
// No warning if numeric constant is followed by a "." or ","
if ( Token : : Match ( tok - > next ( ) , " %num% [,.] " ) )
continue ;
2011-01-13 20:12:57 +01:00
// bailout if there is a "? :" in this statement
bool bailout = false ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
2011-01-13 20:12:57 +01:00
if ( tok2 - > str ( ) = = " ? " )
bailout = true ;
else if ( tok2 - > str ( ) = = " ; " )
break ;
2009-02-12 13:59:43 +01:00
}
2011-01-13 20:12:57 +01:00
if ( bailout )
continue ;
2009-02-12 13:59:43 +01:00
2011-01-13 20:12:57 +01:00
constStatementError ( tok - > next ( ) , tok - > next ( ) - > isNumber ( ) ? " numeric " : " string " ) ;
2009-01-26 20:14:46 +01:00
}
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : constStatementError ( const Token * tok , const std : : string & type )
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : warning , " constStatement " , " Redundant code: Found a statement that begins with " + type + " constant. " ) ;
2011-08-19 17:53:43 +02:00
}
2009-01-26 20:14:46 +01:00
//---------------------------------------------------------------------------
// str plus char
//---------------------------------------------------------------------------
2011-12-17 19:04:03 +01:00
2009-01-26 20:14:46 +01:00
void CheckOther : : strPlusChar ( )
{
2011-12-17 19:04:03 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-15 07:11:45 +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 - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " [=(] %str% + %any% " ) ) {
// char constant..
if ( tok - > tokAt ( 3 ) - > type ( ) = = Token : : eChar )
strPlusCharError ( tok - > next ( ) ) ;
2009-01-26 20:14:46 +01:00
2012-11-15 07:11:45 +01:00
// char variable..
2013-02-06 06:39:58 +01:00
if ( isChar ( tok - > tokAt ( 3 ) - > variable ( ) ) )
2012-11-15 07:11:45 +01:00
strPlusCharError ( tok - > next ( ) ) ;
}
2009-01-26 20:14:46 +01:00
}
}
}
2009-02-04 19:49:19 +01:00
2011-08-19 17:53:43 +02:00
void CheckOther : : strPlusCharError ( const Token * tok )
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " strPlusChar " , " Unusual pointer arithmetic. A value of type 'char' is added to a string literal. " ) ;
2011-08-19 17:53:43 +02:00
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2009-07-05 22:16:43 +02:00
void CheckOther : : checkZeroDivision ( )
2009-03-28 07:49:47 +01:00
{
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2011-09-10 16:12:53 +02:00
if ( Token : : Match ( tok , " [/%] %num% " ) & &
2010-04-02 07:30:58 +02:00
MathLib : : isInt ( tok - > next ( ) - > str ( ) ) & &
2011-10-13 20:53:06 +02:00
MathLib : : toLongNumber ( tok - > next ( ) - > str ( ) ) = = 0L ) {
2009-03-29 18:47:05 +02:00
zerodivError ( tok ) ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " div|ldiv|lldiv|imaxdiv ( %num% , %num% ) " ) & &
2011-11-13 13:10:59 +01:00
MathLib : : isInt ( tok - > strAt ( 4 ) ) & &
MathLib : : toLongNumber ( tok - > strAt ( 4 ) ) = = 0L ) {
2009-08-23 05:34:19 +02:00
zerodivError ( tok ) ;
}
2009-03-28 07:49:47 +01:00
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : zerodivError ( const Token * tok )
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " zerodiv " , " Division by zero. " ) ;
2011-08-19 17:53:43 +02:00
}
2009-03-28 07:49:47 +01:00
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2010-04-02 02:19:38 +02:00
void CheckOther : : checkMathFunctions ( )
{
2012-11-15 07:11:45 +01:00
const SymbolDatabase * 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 ] ;
2013-05-09 15:25:36 +02:00
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-01-28 12:32:28 +01:00
if ( tok - > varId ( ) )
continue ;
if ( Token : : Match ( tok , " log|log10 ( %num% ) " ) ) {
2012-01-08 12:07:25 +01:00
bool isNegative = MathLib : : isNegative ( tok - > strAt ( 2 ) ) ;
bool isInt = MathLib : : isInt ( tok - > strAt ( 2 ) ) ;
bool isFloat = MathLib : : isFloat ( tok - > strAt ( 2 ) ) ;
if ( isNegative & & isInt & & MathLib : : toLongNumber ( tok - > strAt ( 2 ) ) < = 0 ) {
mathfunctionCallError ( tok ) ; // case log(-2)
} else if ( isNegative & & isFloat & & MathLib : : toDoubleNumber ( tok - > strAt ( 2 ) ) < = 0. ) {
mathfunctionCallError ( tok ) ; // case log(-2.0)
} else if ( ! isNegative & & isFloat & & MathLib : : toDoubleNumber ( tok - > strAt ( 2 ) ) < = 0. ) {
mathfunctionCallError ( tok ) ; // case log(0.0)
} else if ( ! isNegative & & isInt & & MathLib : : toLongNumber ( tok - > strAt ( 2 ) ) < = 0 ) {
mathfunctionCallError ( tok ) ; // case log(0)
}
2011-10-22 12:31:47 +02:00
}
2010-04-02 07:32:03 +02:00
2013-01-16 15:37:07 +01:00
// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
2012-01-28 12:32:28 +01:00
else if ( Token : : Match ( tok , " acos|asin ( %num% ) " ) & &
2012-01-08 12:07:25 +01:00
std : : fabs ( MathLib : : toDoubleNumber ( tok - > strAt ( 2 ) ) ) > 1.0 ) {
mathfunctionCallError ( tok ) ;
}
// sqrt( x ): if x is negative the result is undefined
2012-01-28 12:32:28 +01:00
else if ( Token : : Match ( tok , " sqrt|sqrtf|sqrtl ( %num% ) " ) & &
2012-01-08 12:07:25 +01:00
MathLib : : isNegative ( tok - > strAt ( 2 ) ) ) {
mathfunctionCallError ( tok ) ;
}
// atan2 ( x , y): x and y can not be zero, because this is mathematically not defined
2012-01-28 12:32:28 +01:00
else if ( Token : : Match ( tok , " atan2 ( %num% , %num% ) " ) & &
2012-01-08 12:07:25 +01:00
MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & &
MathLib : : isNullValue ( tok - > strAt ( 4 ) ) ) {
mathfunctionCallError ( tok , 2 ) ;
}
// fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined).
2012-01-28 12:32:28 +01:00
else if ( Token : : Match ( tok , " fmod ( %any% " ) ) {
const Token * nextArg = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
if ( nextArg & & nextArg - > isNumber ( ) & & MathLib : : isNullValue ( nextArg - > str ( ) ) )
mathfunctionCallError ( tok , 2 ) ;
2012-01-08 12:07:25 +01:00
}
// pow ( x , y) If x is zero, and y is negative --> division by zero
2012-01-28 12:32:28 +01:00
else if ( Token : : Match ( tok , " pow ( %num% , %num% ) " ) & &
2012-01-08 12:07:25 +01:00
MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & &
MathLib : : isNegative ( tok - > strAt ( 4 ) ) ) {
mathfunctionCallError ( tok , 2 ) ;
}
2010-04-05 19:45:33 +02:00
}
2010-04-02 02:19:38 +02:00
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : mathfunctionCallError ( const Token * tok , const unsigned int numParam )
{
2011-10-13 20:53:06 +02:00
if ( tok ) {
2011-08-19 17:53:43 +02:00
if ( numParam = = 1 )
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " wrongmathcall " , " Passing value " + tok - > strAt ( 2 ) + " to " + tok - > str ( ) + " () leads to undefined result. " ) ;
2011-08-19 17:53:43 +02:00
else if ( numParam = = 2 )
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " wrongmathcall " , " Passing values " + tok - > strAt ( 2 ) + " and " + tok - > strAt ( 4 ) + " to " + tok - > str ( ) + " () leads to undefined result. " ) ;
2011-10-13 20:53:06 +02:00
} else
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " wrongmathcall " , " Passing value '#' to #() leads to undefined result. " ) ;
2011-08-19 17:53:43 +02:00
}
2012-05-24 17:40:43 +02:00
2011-11-17 16:31:16 +01:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void CheckOther : : checkCCTypeFunctions ( )
{
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) = = 0 & &
2011-12-29 08:08:37 +01:00
Token : : Match ( tok , " isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|isupper|isxdigit ( %num% ,|) " ) & &
2011-11-17 16:31:16 +01:00
MathLib : : isNegative ( tok - > strAt ( 2 ) ) ) {
2012-09-07 12:36:40 +02:00
cctypefunctionCallError ( tok , tok - > str ( ) , tok - > strAt ( 2 ) ) ;
2011-11-17 16:31:16 +01:00
}
}
}
void CheckOther : : cctypefunctionCallError ( const Token * tok , const std : : string & functionName , const std : : string & value )
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : error , " wrongcctypecall " , " Passing value " + value + " to " + functionName + " () causes undefined behavior which may lead to a crash. " ) ;
2011-11-17 16:31:16 +01:00
}
2012-05-24 17:40:43 +02:00
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2010-10-01 17:23:22 +02:00
void CheckOther : : checkMisusedScopedObject ( )
{
2010-12-25 08:43:52 +01:00
// Skip this check for .c files
2011-12-28 23:36:16 +01:00
if ( _tokenizer - > isC ( ) ) {
2011-08-19 18:06:28 +02:00
return ;
2010-12-25 08:43:52 +01:00
}
2011-01-16 19:57:29 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-15 07:11:45 +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 ] ;
2012-01-21 19:55:32 +01:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2010-12-18 20:35:40 +01:00
if ( Token : : Match ( tok , " [;{}] %var% ( " )
2011-11-20 14:22:39 +01:00
& & Token : : simpleMatch ( tok - > linkAt ( 2 ) , " ) ; " )
2011-01-16 19:57:29 +01:00
& & symbolDatabase - > isClassOrStruct ( tok - > next ( ) - > str ( ) )
2013-01-31 06:41:18 +01:00
& & ! tok - > next ( ) - > function ( ) ) {
2010-10-01 17:23:22 +02:00
tok = tok - > next ( ) ;
misusedScopeObjectError ( tok , tok - > str ( ) ) ;
tok = tok - > next ( ) ;
}
}
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : misusedScopeObjectError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error ,
2012-09-16 19:44:02 +02:00
" unusedScopedObject " , " Instance of ' " + varname + " ' object is destroyed immediately. " ) ;
2011-08-19 17:53:43 +02:00
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
2011-02-08 19:49:29 +01:00
void CheckOther : : checkIncorrectStringCompare ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2011-12-16 19:21:14 +01:00
return ;
2012-11-15 07:11:45 +01:00
const SymbolDatabase * 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 ( ) ) {
2012-12-20 20:48:48 +01:00
// skip "assert(str && ..)" and "assert(.. && str)"
if ( Token : : Match ( tok , " %var% ( " ) & &
( Token : : Match ( tok - > tokAt ( 2 ) , " %str% && " ) | | Token : : Match ( tok - > next ( ) - > link ( ) - > tokAt ( - 2 ) , " && %str% ) " ) ) & &
( tok - > str ( ) . find ( " assert " ) + 6U = = tok - > str ( ) . size ( ) | | tok - > str ( ) . find ( " ASSERT " ) + 6U = = tok - > str ( ) . size ( ) ) )
tok = tok - > next ( ) - > link ( ) ;
2013-04-09 18:16:35 +02:00
if ( Token : : simpleMatch ( tok , " . substr ( " ) & & Token : : Match ( tok - > tokAt ( 3 ) - > nextArgument ( ) , " %num% ) " ) ) {
MathLib : : bigint clen = MathLib : : toLongNumber ( tok - > linkAt ( 2 ) - > strAt ( - 1 ) ) ;
const Token * begin = tok - > previous ( ) ;
for ( ; ; ) { // Find start of statement
while ( begin - > link ( ) & & Token : : Match ( begin , " ]|)|> " ) )
begin = begin - > link ( ) - > previous ( ) ;
if ( Token : : Match ( begin - > previous ( ) , " .|:: " ) )
begin = begin - > tokAt ( - 2 ) ;
else
break ;
2012-11-15 07:11:45 +01:00
}
2013-04-09 18:16:35 +02:00
begin = begin - > previous ( ) ;
const Token * end = tok - > linkAt ( 2 ) - > next ( ) ;
if ( Token : : Match ( begin - > previous ( ) , " %str% ==|!= " ) & & begin - > strAt ( - 2 ) ! = " + " ) {
std : : size_t slen = Token : : getStrLength ( begin - > previous ( ) ) ;
if ( clen ! = ( int ) slen ) {
incorrectStringCompareError ( tok - > next ( ) , " substr " , begin - > strAt ( - 1 ) ) ;
}
} else if ( Token : : Match ( end , " ==|!= %str% !!+ " ) ) {
std : : size_t slen = Token : : getStrLength ( end - > next ( ) ) ;
if ( clen ! = ( int ) slen ) {
incorrectStringCompareError ( tok - > next ( ) , " substr " , end - > strAt ( 1 ) ) ;
}
2012-11-15 07:11:45 +01:00
}
2012-12-20 20:48:48 +01:00
} else if ( Token : : Match ( tok , " &&|%oror%|( %str% &&|%oror%|) " ) && !Token::Match(tok, " ( % str % ) " )) {
2012-11-15 07:11:45 +01:00
incorrectStringBooleanError ( tok - > next ( ) , tok - > strAt ( 1 ) ) ;
2012-12-20 20:48:48 +01:00
} else if ( Token : : Match ( tok , " if|while ( %str% ) " )) {
2012-11-15 07:11:45 +01:00
incorrectStringBooleanError ( tok - > tokAt ( 2 ) , tok - > strAt ( 2 ) ) ;
}
2011-10-18 21:37:03 +02:00
}
2011-02-08 19:49:29 +01:00
}
}
2012-09-16 19:44:02 +02:00
void CheckOther : : incorrectStringCompareError ( const Token * tok , const std : : string & func , const std : : string & string )
2011-08-19 17:53:43 +02:00
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : warning , " incorrectStringCompare " , " String literal " + string + " doesn't match length argument for " + func + " (). " ) ;
2011-08-19 17:53:43 +02:00
}
2011-10-18 21:37:03 +02:00
void CheckOther : : incorrectStringBooleanError ( const Token * tok , const std : : string & string )
{
2012-09-16 19:44:02 +02:00
reportError ( tok , Severity : : warning , " incorrectStringBooleanError " , " Conversion of string literal " + string + " to bool always evaluates to true. " ) ;
2011-10-18 21:37:03 +02:00
}
2011-04-09 21:14:01 +02:00
//-----------------------------------------------------------------------------
// check for duplicate expressions in if statements
// if (a) { } else if (a) { }
2012-08-24 00:03:22 +02:00
//-----------------------------------------------------------------------------
2011-04-09 21:14:01 +02:00
2012-08-24 00:03:22 +02:00
static bool expressionHasSideEffects ( const Token * first , const Token * last )
{
for ( const Token * tok = first ; tok ! = last - > next ( ) ; tok = tok - > next ( ) ) {
// check for assignment
if ( tok - > isAssignmentOp ( ) )
return true ;
2011-04-09 21:14:01 +02:00
2012-08-24 00:03:22 +02:00
// check for inc/dec
else if ( tok - > type ( ) = = Token : : eIncDecOp )
return true ;
2011-04-09 21:14:01 +02:00
2012-08-24 00:03:22 +02:00
// check for function call
else if ( Token : : Match ( tok , " %var% ( " ) & &
! ( Token : : Match ( tok , " c_str|string " ) | | tok - > isStandardType ( ) ) )
return true ;
2012-08-23 21:28:40 +02:00
}
2012-08-24 00:03:22 +02:00
return false ;
2011-04-09 21:14:01 +02:00
}
2012-08-24 00:03:22 +02:00
void CheckOther : : checkDuplicateIf ( )
{
if ( ! _settings - > isEnabled ( " style " ) )
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
const Token * const tok = scope - > classDef ;
// only check if statements
if ( scope - > type ! = Scope : : eIf | | ! tok )
continue ;
std : : map < std : : string , const Token * > expressionMap ;
// get the expression from the token stream
std : : string expression = tok - > tokAt ( 2 ) - > stringifyList ( tok - > next ( ) - > link ( ) ) ;
// save the expression and its location
expressionMap . insert ( std : : make_pair ( expression , tok ) ) ;
2013-02-12 16:13:08 +01:00
// find the next else if (...) statement
2012-08-20 17:57:28 +02:00
const Token * tok1 = scope - > classEnd ;
2011-04-09 21:14:01 +02:00
2013-02-12 16:13:08 +01:00
// check all the else if (...) statements
2013-02-12 21:43:12 +01:00
while ( ( Token : : simpleMatch ( tok1 , " } else if ( " ) & &
Token : : simpleMatch ( tok1 - > linkAt ( 3 ) , " ) { " ) ) | |
( Token : : simpleMatch ( tok1 , " } else { if ( " ) & &
Token : : simpleMatch ( tok1 - > linkAt ( 4 ) , " ) { " ) ) ) {
int conditionIndex = ( tok1 - > strAt ( 3 ) = = " ( " ) ? 3 : 4 ;
2011-12-09 22:28:10 +01:00
// get the expression from the token stream
2013-02-12 21:43:12 +01:00
expression = tok1 - > tokAt ( conditionIndex + 1 ) - > stringifyList ( tok1 - > linkAt ( conditionIndex ) ) ;
2011-04-09 21:14:01 +02:00
2011-12-09 22:28:10 +01:00
// try to look up the expression to check for duplicates
std : : map < std : : string , const Token * > : : iterator it = expressionMap . find ( expression ) ;
2011-04-09 21:14:01 +02:00
2011-12-09 22:28:10 +01:00
// found a duplicate
if ( it ! = expressionMap . end ( ) ) {
// check for expressions that have side effects and ignore them
2013-02-12 21:43:12 +01:00
if ( ! expressionHasSideEffects ( tok1 - > tokAt ( conditionIndex + 1 ) , tok1 - > linkAt ( conditionIndex ) - > previous ( ) ) )
2011-12-09 22:28:10 +01:00
duplicateIfError ( it - > second , tok1 - > next ( ) ) ;
}
2011-04-09 21:14:01 +02:00
2011-12-09 22:28:10 +01:00
// not a duplicate expression so save it and its location
else
expressionMap . insert ( std : : make_pair ( expression , tok1 - > next ( ) ) ) ;
2011-04-09 21:14:01 +02:00
2013-02-12 16:13:08 +01:00
// find the next else if (...) statement
2013-02-12 21:43:12 +01:00
tok1 = tok1 - > linkAt ( conditionIndex ) - > next ( ) - > link ( ) ;
2011-04-09 21:14:01 +02:00
}
}
}
void CheckOther : : duplicateIfError ( const Token * tok1 , const Token * tok2 )
{
std : : list < const Token * > toks ;
toks . push_back ( tok2 ) ;
toks . push_back ( tok1 ) ;
2012-09-16 19:44:02 +02:00
reportError ( toks , Severity : : style , " duplicateIf " , " Duplicate conditions in 'if' and related 'else if'. \n "
" Duplicate conditions in 'if' and related 'else if'. This is suspicious and might indicate "
2011-04-09 21:14:01 +02:00
" a cut and paste or logic error. Please examine this code carefully to determine "
" if it is correct. " ) ;
}
2011-04-09 23:05:27 +02:00
//-----------------------------------------------------------------------------
// check for duplicate code in if and else branches
// if (a) { b = true; } else { b = true; }
//-----------------------------------------------------------------------------
void CheckOther : : checkDuplicateBranch ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-04-09 23:05:27 +02:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
std : : list < Scope > : : const_iterator scope ;
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2012-05-24 17:40:43 +02:00
if ( scope - > type ! = Scope : : eIf & & scope - > type ! = Scope : : eElseIf )
2011-12-13 21:42:38 +01:00
continue ;
2011-04-09 23:05:27 +02:00
// check all the code in the function for if (..) else
2012-05-24 17:40:43 +02:00
if ( Token : : simpleMatch ( scope - > classEnd , " } else { " ) ) {
2013-02-18 17:18:33 +01:00
// Make sure there are no macros (different macros might be expanded
// to the same code)
bool macro = false ;
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd - > linkAt ( 2 ) ; tok = tok - > next ( ) ) {
if ( tok - > isExpandedMacro ( ) ) {
macro = true ;
break ;
}
}
if ( macro )
continue ;
2011-12-09 22:28:10 +01:00
// save if branch code
2012-05-24 17:40:43 +02:00
std : : string branch1 = scope - > classStart - > next ( ) - > stringifyList ( scope - > classEnd ) ;
2011-04-09 23:05:27 +02:00
2011-12-09 22:28:10 +01:00
// save else branch code
2012-05-24 17:40:43 +02:00
std : : string branch2 = scope - > classEnd - > tokAt ( 3 ) - > stringifyList ( scope - > classEnd - > linkAt ( 2 ) ) ;
2011-04-09 23:05:27 +02:00
2011-12-09 22:28:10 +01:00
// check for duplicates
if ( branch1 = = branch2 )
2012-06-08 19:35:08 +02:00
duplicateBranchError ( scope - > classDef , scope - > classEnd - > next ( ) ) ;
2011-04-09 23:05:27 +02:00
}
}
}
2012-09-16 19:44:02 +02:00
void CheckOther : : duplicateBranchError ( const Token * tok1 , const Token * tok2 )
{
std : : list < const Token * > toks ;
toks . push_back ( tok2 ) ;
toks . push_back ( tok1 ) ;
reportError ( toks , Severity : : style , " duplicateBranch " , " Found duplicate branches for 'if' and 'else'. \n "
" Finding the same code in an 'if' and related 'else' branch is suspicious and "
" might indicate a cut and paste or logic error. Please examine this code "
" carefully to determine if it is correct. " ) ;
}
2012-09-05 08:31:23 +02:00
//-----------------------------------------------------------------------------
// Check for a free() of an invalid address
// char* p = malloc(100);
// free(p + 10);
//-----------------------------------------------------------------------------
void CheckOther : : checkInvalidFree ( )
{
2012-11-06 06:02:51 +01:00
std : : map < unsigned int , bool > allocatedVariables ;
2012-09-05 08:31:23 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
// Keep track of which variables were assigned addresses to newly-allocated memory
if ( Token : : Match ( tok , " %var% = malloc|g_malloc|new " ) ) {
2012-11-06 06:02:51 +01:00
allocatedVariables . insert ( std : : make_pair ( tok - > varId ( ) , false ) ) ;
2012-09-05 08:31:23 +02:00
}
2012-11-06 06:02:51 +01:00
// If a previously-allocated pointer is incremented or decremented, any subsequent
// free involving pointer arithmetic may or may not be invalid, so we should only
// report an inconclusive result.
else if ( Token : : Match ( tok , " %var% = %var% +|- " ) & &
tok - > varId ( ) = = tok - > tokAt ( 2 ) - > varId ( ) & &
allocatedVariables . find ( tok - > varId ( ) ) ! = allocatedVariables . end ( ) ) {
if ( _settings - > inconclusive )
allocatedVariables [ tok - > varId ( ) ] = true ;
else
allocatedVariables . erase ( tok - > varId ( ) ) ;
}
// If a previously-allocated pointer is assigned a completely new value,
// we can't know if any subsequent free() on that pointer is valid or not.
else if ( Token : : Match ( tok , " %var% = " ) ) {
2012-09-05 08:31:23 +02:00
allocatedVariables . erase ( tok - > varId ( ) ) ;
}
// If a variable that was previously assigned a newly-allocated memory location is
// added or subtracted from when used to free the memory, report an error.
else if ( Token : : Match ( tok , " free|g_free|delete ( %any% +|- %any% " ) | |
Token : : Match ( tok , " delete [ ] ( %any% +|- %any% " ) | |
Token : : Match ( tok , " delete %any% +|- %any% " ) ) {
2012-11-06 06:02:51 +01:00
const int varIdx = tok - > strAt ( 1 ) = = " ( " ? 2 :
tok - > strAt ( 3 ) = = " ( " ? 4 : 1 ;
const unsigned int var1 = tok - > tokAt ( varIdx ) - > varId ( ) ;
const unsigned int var2 = tok - > tokAt ( varIdx + 2 ) - > varId ( ) ;
const std : : map < unsigned int , bool > : : iterator alloc1 = allocatedVariables . find ( var1 ) ;
const std : : map < unsigned int , bool > : : iterator alloc2 = allocatedVariables . find ( var2 ) ;
if ( alloc1 ! = allocatedVariables . end ( ) ) {
invalidFreeError ( tok , alloc1 - > second ) ;
} else if ( alloc2 ! = allocatedVariables . end ( ) ) {
invalidFreeError ( tok , alloc2 - > second ) ;
2012-09-05 08:31:23 +02:00
}
}
// If the previously-allocated variable is passed in to another function
// as a parameter, it might be modified, so we shouldn't report an error
// if it is later used to free memory
else if ( Token : : Match ( tok , " %var% ( " ) ) {
2013-03-12 15:42:00 +01:00
const Token * tok2 = Token : : findmatch ( tok - > next ( ) , " %var% " , tok - > linkAt ( 1 ) ) ;
2012-09-05 08:31:23 +02:00
while ( tok2 ! = NULL ) {
allocatedVariables . erase ( tok2 - > varId ( ) ) ;
tok2 = Token : : findmatch ( tok2 - > next ( ) , " %var% " , tok - > linkAt ( 1 ) ) ;
}
}
}
}
2012-11-06 06:02:51 +01:00
void CheckOther : : invalidFreeError ( const Token * tok , bool inconclusive )
2012-09-05 08:31:23 +02:00
{
2012-11-06 06:02:51 +01:00
reportError ( tok , Severity : : error , " invalidFree " , " Invalid memory address freed. " , inconclusive ) ;
2012-09-05 08:31:23 +02:00
}
2012-01-15 01:19:34 +01:00
//-----------------------------------------------------------------------------
// Check for double free
// free(p); free(p);
//-----------------------------------------------------------------------------
void CheckOther : : checkDoubleFree ( )
{
2012-01-21 19:55:32 +01:00
std : : set < unsigned int > freedVariables ;
std : : set < unsigned int > closeDirVariables ;
2012-01-15 01:19:34 +01:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
// Keep track of any variables passed to "free()", "g_free()" or "closedir()",
// and report an error if the same variable is passed twice.
if ( Token : : Match ( tok , " free|g_free|closedir ( %var% ) " ) ) {
2012-01-21 19:55:32 +01:00
unsigned int var = tok - > tokAt ( 2 ) - > varId ( ) ;
2012-01-15 01:19:34 +01:00
if ( var ) {
if ( Token : : Match ( tok , " free|g_free " ) ) {
if ( freedVariables . find ( var ) ! = freedVariables . end ( ) )
2012-09-07 12:36:40 +02:00
doubleFreeError ( tok , tok - > strAt ( 2 ) ) ;
2012-01-15 01:19:34 +01:00
else
freedVariables . insert ( var ) ;
} else if ( tok - > str ( ) = = " closedir " ) {
if ( closeDirVariables . find ( var ) ! = closeDirVariables . end ( ) )
2012-09-07 12:36:40 +02:00
doubleCloseDirError ( tok , tok - > strAt ( 2 ) ) ;
2012-01-15 01:19:34 +01:00
else
closeDirVariables . insert ( var ) ;
}
}
}
// Keep track of any variables operated on by "delete" or "delete[]"
// and report an error if the same variable is delete'd twice.
else if ( Token : : Match ( tok , " delete %var% ; " ) | | Token : : Match ( tok , " delete [ ] %var% ; " ) ) {
int varIdx = ( tok - > strAt ( 1 ) = = " [ " ) ? 3 : 1 ;
2012-01-21 19:55:32 +01:00
unsigned int var = tok - > tokAt ( varIdx ) - > varId ( ) ;
2012-01-15 01:19:34 +01:00
if ( var ) {
if ( freedVariables . find ( var ) ! = freedVariables . end ( ) )
2012-09-07 12:36:40 +02:00
doubleFreeError ( tok , tok - > strAt ( varIdx ) ) ;
2012-01-15 01:19:34 +01:00
else
freedVariables . insert ( var ) ;
}
}
// If this scope doesn't return, clear the set of previously freed variables
2013-01-06 04:02:19 +01:00
else if ( tok - > str ( ) = = " } " & & _tokenizer - > IsScopeNoReturn ( tok ) ) {
2012-01-15 01:19:34 +01:00
freedVariables . clear ( ) ;
closeDirVariables . clear ( ) ;
}
2012-06-16 22:03:51 +02:00
// If this scope is a "for" or "while" loop that contains "break" or "continue",
// give up on trying to figure out the flow of execution and just clear the set
// of previously freed variables.
// TODO: There are false negatives. This bailout is only needed when the
// loop will exit without free()'ing the memory on the last iteration.
2013-03-12 15:42:00 +01:00
else if ( tok - > str ( ) = = " } " & & tok - > link ( ) & & tok - > link ( ) - > previous ( ) & &
2012-12-28 20:16:51 +01:00
tok - > link ( ) - > linkAt ( - 1 ) & &
2012-06-16 22:03:51 +02:00
Token : : Match ( tok - > link ( ) - > linkAt ( - 1 ) - > previous ( ) , " while|for " ) & &
Token : : findmatch ( tok - > link ( ) - > linkAt ( - 1 ) , " break|continue ; " , tok ) ! = NULL ) {
2012-06-11 02:38:31 +02:00
freedVariables . clear ( ) ;
closeDirVariables . clear ( ) ;
}
2012-01-25 07:43:44 +01:00
2012-01-15 01:19:34 +01:00
// If a variable is passed to a function, remove it from the set of previously freed variables
2012-11-06 19:54:52 +01:00
else if ( Token : : Match ( tok , " %var% ( " ) & & ! Token : : Match ( tok , " printf|sprintf|snprintf|fprintf|wprintf|swprintf|fwprintf " ) ) {
2012-01-25 07:43:44 +01:00
// If this is a new function definition, clear all variables
2012-01-28 20:31:12 +01:00
if ( Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) { " ) ) {
2012-01-25 07:43:44 +01:00
freedVariables . clear ( ) ;
closeDirVariables . clear ( ) ;
}
// If it is a function call, then clear those variables in its argument list
2012-01-28 20:31:12 +01:00
else if ( Token : : simpleMatch ( tok - > next ( ) - > link ( ) , " ) ; " )) {
2012-01-25 07:43:44 +01:00
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ! = tok - > linkAt ( 1 ) ; tok2 = tok2 - > next ( ) ) {
2013-03-01 12:42:04 +01:00
if ( tok2 - > varId ( ) ) {
2012-01-25 07:43:44 +01:00
unsigned int var = tok2 - > varId ( ) ;
2013-03-01 12:42:04 +01:00
freedVariables . erase ( var ) ;
closeDirVariables . erase ( var ) ;
2012-01-15 01:19:34 +01:00
}
}
}
}
// If a pointer is assigned a new value, remove it from the set of previously freed variables
else if ( Token : : Match ( tok , " %var% = " ) ) {
2012-01-21 19:55:32 +01:00
unsigned int var = tok - > varId ( ) ;
2012-01-15 01:19:34 +01:00
if ( var ) {
freedVariables . erase ( var ) ;
closeDirVariables . erase ( var ) ;
}
}
// Any control statements in-between delete, free() or closedir() statements
// makes it unclear whether any subsequent statements would be redundant.
2012-02-17 06:03:38 +01:00
if ( Token : : Match ( tok , " if|else|for|while|break|continue|goto|return|throw|switch " ) ) {
2012-01-15 01:19:34 +01:00
freedVariables . clear ( ) ;
closeDirVariables . clear ( ) ;
}
}
}
void CheckOther : : doubleFreeError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error , " doubleFree " , " Memory pointed to by ' " + varname + " ' is freed twice. " ) ;
}
void CheckOther : : doubleCloseDirError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : error , " doubleCloseDir " , " Directory handle ' " + varname + " ' closed twice. " ) ;
}
2011-11-08 21:22:31 +01:00
namespace {
2011-11-10 21:49:14 +01:00
struct ExpressionTokens {
const Token * start ;
const Token * end ;
int count ;
2011-12-01 19:46:57 +01:00
bool inconclusiveFunction ;
ExpressionTokens ( const Token * s , const Token * e ) : start ( s ) , end ( e ) , count ( 1 ) , inconclusiveFunction ( false ) { }
2011-11-10 21:49:14 +01:00
} ;
struct FuncFilter {
FuncFilter ( const Scope * scope , const Token * tok ) : _scope ( scope ) , _tok ( tok ) { }
2012-05-24 17:40:43 +02:00
bool operator ( ) ( const Function * func ) const {
bool matchingFunc = func - > type = = Function : : eFunction & &
_tok - > str ( ) = = func - > token - > str ( ) ;
2011-11-10 21:49:14 +01:00
// either a class function, or a global function with the same name
2012-05-24 17:40:43 +02:00
return ( _scope & & _scope = = func - > nestedIn & & matchingFunc ) | |
2011-11-10 21:49:14 +01:00
( ! _scope & & matchingFunc ) ;
}
const Scope * _scope ;
const Token * _tok ;
2011-11-08 21:22:31 +01:00
} ;
2011-11-10 21:49:14 +01:00
2013-02-06 06:39:58 +01:00
bool inconclusiveFunctionCall ( const std : : list < const Function * > & constFunctions ,
2011-11-10 21:49:14 +01:00
const ExpressionTokens & tokens )
{
const Token * start = tokens . start ;
const Token * end = tokens . end ;
// look for function calls between start and end...
for ( const Token * tok = start ; tok & & tok ! = end ; tok = tok - > next ( ) ) {
if ( tok ! = start & & tok - > str ( ) = = " ( " ) {
// go back to find the function call.
const Token * prev = tok - > previous ( ) ;
2011-12-01 19:50:26 +01:00
if ( ! prev )
continue ;
2011-11-10 21:49:14 +01:00
if ( prev - > str ( ) = = " > " ) {
// ignore template functions like boo<double>()
return true ;
}
2011-12-01 19:50:26 +01:00
if ( prev - > isName ( ) ) {
2011-11-10 21:49:14 +01:00
const Variable * v = 0 ;
if ( Token : : Match ( prev - > tokAt ( - 2 ) , " %var% . " ) ) {
const Token * scope = prev - > tokAt ( - 2 ) ;
2013-02-06 06:39:58 +01:00
v = scope - > variable ( ) ;
2011-11-10 21:49:14 +01:00
}
// hard coded list of safe, no-side-effect functions
2012-11-06 19:54:52 +01:00
if ( v = = 0 & & Token : : Match ( prev , " strcmp|strncmp|strlen|wcscmp|wcsncmp|wcslen|memcmp|strcasecmp|strncasecmp " ) )
2011-11-10 21:49:14 +01:00
return false ;
2012-05-24 17:40:43 +02:00
std : : list < const Function * > : : const_iterator it = std : : find_if ( constFunctions . begin ( ) ,
2011-11-29 20:44:23 +01:00
constFunctions . end ( ) ,
2013-03-05 13:33:38 +01:00
FuncFilter ( v ? v - > typeScope ( ) : 0 , prev ) ) ;
2011-11-10 21:49:14 +01:00
if ( it = = constFunctions . end ( ) )
return true ;
}
}
}
return false ;
}
2011-12-01 19:46:57 +01:00
class Expressions {
public :
2013-02-08 11:43:35 +01:00
Expressions ( const std : : list < const Function * > & constFunctions )
2011-12-01 19:46:57 +01:00
: _start ( 0 ) ,
2011-12-01 19:49:04 +01:00
_lastTokens ( 0 ) ,
2011-12-01 19:46:57 +01:00
_constFunctions ( constFunctions ) { }
void endExpr ( const Token * end ) {
const std : : string & e = _expression . str ( ) ;
if ( ! e . empty ( ) ) {
std : : map < std : : string , ExpressionTokens > : : iterator it = _expressions . find ( e ) ;
2011-12-01 19:49:04 +01:00
bool lastInconclusive = _lastTokens & & _lastTokens - > inconclusiveFunction ;
2011-12-01 19:46:57 +01:00
if ( it = = _expressions . end ( ) ) {
ExpressionTokens exprTokens ( _start , end ) ;
2011-12-01 19:49:04 +01:00
exprTokens . inconclusiveFunction = lastInconclusive | | inconclusiveFunctionCall (
2013-02-06 06:39:58 +01:00
_constFunctions , exprTokens ) ;
2011-12-01 19:46:57 +01:00
_expressions . insert ( std : : make_pair ( e , exprTokens ) ) ;
2011-12-01 19:49:04 +01:00
_lastTokens = & _expressions . find ( e ) - > second ;
2011-12-01 19:46:57 +01:00
} else {
2011-12-01 19:49:04 +01:00
ExpressionTokens & expr = it - > second ;
expr . count + = 1 ;
expr . inconclusiveFunction = expr . inconclusiveFunction | | lastInconclusive ;
_lastTokens = & expr ;
2011-12-01 19:46:57 +01:00
}
}
_expression . str ( " " ) ;
_start = 0 ;
}
void append ( const Token * tok ) {
if ( ! _start )
_start = tok ;
_expression < < tok - > str ( ) ;
}
std : : map < std : : string , ExpressionTokens > & getMap ( ) {
return _expressions ;
}
private :
std : : map < std : : string , ExpressionTokens > _expressions ;
std : : ostringstream _expression ;
const Token * _start ;
2011-12-01 19:49:04 +01:00
ExpressionTokens * _lastTokens ;
2012-05-24 17:40:43 +02:00
const std : : list < const Function * > & _constFunctions ;
2011-12-01 19:46:57 +01:00
} ;
2012-05-24 17:40:43 +02:00
bool notconst ( const Function * func )
2011-11-10 21:49:14 +01:00
{
2012-05-24 17:40:43 +02:00
return ! func - > isConst ;
2011-11-10 21:49:14 +01:00
}
2012-05-24 17:40:43 +02:00
void getConstFunctions ( const SymbolDatabase * symbolDatabase , std : : list < const Function * > & constFunctions )
2011-11-10 21:49:14 +01:00
{
std : : list < Scope > : : const_iterator scope ;
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
std : : list < Function > : : const_iterator func ;
// only add const functions that do not have a non-const overloaded version
// since it is pretty much impossible to tell which is being called.
2012-05-24 17:40:43 +02:00
typedef std : : map < std : : string , std : : list < const Function * > > StringFunctionMap ;
2011-11-10 21:49:14 +01:00
StringFunctionMap functionsByName ;
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2012-05-24 17:40:43 +02:00
functionsByName [ func - > tokenDef - > str ( ) ] . push_back ( & * func ) ;
2011-11-10 21:49:14 +01:00
}
for ( StringFunctionMap : : iterator it = functionsByName . begin ( ) ;
it ! = functionsByName . end ( ) ; + + it ) {
2012-05-24 17:40:43 +02:00
std : : list < const Function * > : : const_iterator nc = std : : find_if ( it - > second . begin ( ) , it - > second . end ( ) , notconst ) ;
2011-11-10 21:49:14 +01:00
if ( nc = = it - > second . end ( ) ) {
// ok to add all of them
constFunctions . splice ( constFunctions . end ( ) , it - > second ) ;
}
}
}
}
2011-11-08 21:22:31 +01:00
}
2012-05-24 17:40:43 +02:00
void CheckOther : : checkExpressionRange ( const std : : list < const Function * > & constFunctions ,
2011-11-10 21:49:14 +01:00
const Token * start ,
const Token * end ,
const std : : string & toCheck )
2011-11-08 21:22:31 +01:00
{
if ( ! start | | ! end )
return ;
2013-02-08 11:43:35 +01:00
Expressions expressions ( constFunctions ) ;
2011-11-08 21:22:31 +01:00
std : : string opName ;
2011-11-12 10:23:34 +01:00
int level = 0 ;
2011-11-08 21:22:31 +01:00
for ( const Token * tok = start - > next ( ) ; tok & & tok ! = end ; tok = tok - > next ( ) ) {
2011-11-12 10:23:34 +01:00
if ( tok - > str ( ) = = " ) " )
level - - ;
else if ( tok - > str ( ) = = " ( " )
level + + ;
if ( level = = 0 & & Token : : Match ( tok , toCheck . c_str ( ) ) ) {
2011-11-08 21:22:31 +01:00
opName = tok - > str ( ) ;
2011-11-10 21:49:14 +01:00
expressions . endExpr ( tok ) ;
2011-11-08 21:22:31 +01:00
} else {
2011-11-10 21:49:14 +01:00
expressions . append ( tok ) ;
2011-11-08 21:22:31 +01:00
}
}
2011-11-10 21:49:14 +01:00
expressions . endExpr ( end ) ;
std : : map < std : : string , ExpressionTokens > : : const_iterator it = expressions . getMap ( ) . begin ( ) ;
2011-11-08 21:22:31 +01:00
for ( ; it ! = expressions . getMap ( ) . end ( ) ; + + it ) {
2011-12-01 17:07:55 +01:00
// check expression..
bool valid = true ;
2013-01-16 15:37:07 +01:00
unsigned int parentheses = 0 ; // ()
2011-12-01 17:07:55 +01:00
unsigned int brackets = 0 ; // []
2011-12-01 17:43:29 +01:00
2011-12-23 16:08:53 +01:00
// taking address?
if ( Token : : Match ( it - > second . end - > previous ( ) , " %op% & " ) ) {
continue ;
}
2011-12-01 17:43:29 +01:00
for ( const Token * tok = it - > second . start ; tok & & tok ! = it - > second . end ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " ( " ) {
2013-01-16 15:37:07 +01:00
+ + parentheses ;
2011-12-01 17:43:29 +01:00
} else if ( tok - > str ( ) = = " ) " ) {
2013-01-16 15:37:07 +01:00
if ( parentheses = = 0 ) {
2011-12-01 17:07:55 +01:00
valid = false ;
break ;
}
2013-01-16 15:37:07 +01:00
- - parentheses ;
2011-12-01 17:43:29 +01:00
} else if ( tok - > str ( ) = = " [ " ) {
2011-12-01 17:07:55 +01:00
+ + brackets ;
2011-12-01 17:43:29 +01:00
} else if ( tok - > str ( ) = = " ] " ) {
2011-12-01 17:07:55 +01:00
if ( brackets = = 0 ) {
valid = false ;
break ;
}
- - brackets ;
2012-04-25 09:56:07 +02:00
} else if ( tok - > type ( ) = = Token : : eIncDecOp ) {
2011-12-01 17:46:33 +01:00
valid = false ;
break ;
2011-12-01 17:07:55 +01:00
}
}
2013-01-16 15:37:07 +01:00
if ( ! valid | | parentheses ! = 0 | | brackets ! = 0 )
2011-12-01 17:07:55 +01:00
continue ;
2011-12-01 19:46:57 +01:00
const ExpressionTokens & expr = it - > second ;
if ( expr . count > 1 & & ! expr . inconclusiveFunction ) {
duplicateExpressionError ( expr . start , expr . start , opName ) ;
2011-11-10 21:49:14 +01:00
}
2011-11-08 21:22:31 +01:00
}
}
2012-05-24 17:40:43 +02:00
void CheckOther : : complexDuplicateExpressionCheck ( const std : : list < const Function * > & constFunctions ,
2011-11-29 20:44:23 +01:00
const Token * classStart ,
const std : : string & toCheck ,
const std : : string & alt )
2011-11-08 21:22:31 +01:00
{
2012-06-23 19:54:15 +02:00
std : : string statementStart ( " ,|=|?|:|return " ) ;
2011-11-08 21:22:31 +01:00
if ( ! alt . empty ( ) )
statementStart + = " | " + alt ;
2012-06-23 19:54:15 +02:00
std : : string statementEnd ( " ;|,|?|: " ) ;
2011-11-08 21:22:31 +01:00
if ( ! alt . empty ( ) )
statementEnd + = " | " + alt ;
for ( const Token * tok = classStart ; tok & & tok ! = classStart - > link ( ) ; tok = tok - > next ( ) ) {
if ( ! Token : : Match ( tok , toCheck . c_str ( ) ) )
continue ;
// look backward for the start of the statement
const Token * start = 0 ;
int level = 0 ;
for ( const Token * tok1 = tok - > previous ( ) ; tok1 & & tok1 ! = classStart ; tok1 = tok1 - > previous ( ) ) {
if ( tok1 - > str ( ) = = " ) " )
level + + ;
else if ( tok1 - > str ( ) = = " ( " )
level - - ;
2011-11-12 10:23:34 +01:00
if ( level < 0 | | ( level = = 0 & & Token : : Match ( tok1 , statementStart . c_str ( ) ) ) ) {
2011-11-08 21:22:31 +01:00
start = tok1 ;
break ;
}
}
const Token * end = 0 ;
level = 0 ;
// look for the end of the statement
for ( const Token * tok1 = tok - > next ( ) ; tok1 & & tok1 ! = classStart - > link ( ) ; tok1 = tok1 - > next ( ) ) {
if ( tok1 - > str ( ) = = " ) " )
level - - ;
else if ( tok1 - > str ( ) = = " ( " )
level + + ;
2011-11-12 10:23:34 +01:00
if ( level < 0 | | ( level = = 0 & & Token : : Match ( tok1 , statementEnd . c_str ( ) ) ) ) {
2011-11-08 21:22:31 +01:00
end = tok1 ;
break ;
}
}
2011-11-10 21:49:14 +01:00
checkExpressionRange ( constFunctions , start , end , toCheck ) ;
2011-11-08 21:22:31 +01:00
}
}
2011-04-10 16:25:02 +02:00
//---------------------------------------------------------------------------
// check for the same expression on both sides of an operator
// (x == x), (x && x), (x || x)
// (x.y == x.y), (x.y && x.y), (x.y || x.y)
//---------------------------------------------------------------------------
void CheckOther : : checkDuplicateExpression ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-04-10 16:25:02 +02:00
return ;
// Parse all executing scopes..
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
std : : list < Scope > : : const_iterator scope ;
2012-05-24 17:40:43 +02:00
std : : list < const Function * > constFunctions ;
2011-11-10 21:49:14 +01:00
getConstFunctions ( symbolDatabase , constFunctions ) ;
2011-04-10 16:25:02 +02:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2011-04-10 16:25:02 +02:00
// only check functions
if ( scope - > type ! = Scope : : eFunction )
continue ;
2011-11-10 21:49:14 +01:00
complexDuplicateExpressionCheck ( constFunctions , scope - > classStart , " %or% " , " " ) ;
complexDuplicateExpressionCheck ( constFunctions , scope - > classStart , " %oror% " , " " ) ;
complexDuplicateExpressionCheck ( constFunctions , scope - > classStart , " & " , " %oror%|%or% " ) ;
complexDuplicateExpressionCheck ( constFunctions , scope - > classStart , " && " , " %oror%|%or% " ) ;
2011-11-08 21:22:31 +01:00
2011-10-13 20:53:06 +02:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classStart - > link ( ) ; tok = tok - > next ( ) ) {
2012-12-01 01:31:35 +01:00
if ( Token : : Match ( tok , " ,|=|return|(|&&|%oror% %var% %comp%|- %var% )|&&|%oror%|;|, " ) & &
2011-10-13 20:53:06 +02:00
tok - > strAt ( 1 ) = = tok - > strAt ( 3 ) ) {
2011-04-19 01:20:27 +02:00
// float == float and float != float are valid NaN checks
2013-01-31 17:29:31 +01:00
// float - float is a valid Inf check
if ( Token : : Match ( tok - > tokAt ( 2 ) , " ==|!=|- " ) & & tok - > next ( ) - > varId ( ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > next ( ) - > variable ( ) ;
2011-10-13 20:53:06 +02:00
if ( var & & var - > typeStartToken ( ) = = var - > typeEndToken ( ) ) {
2011-04-19 01:20:27 +02:00
if ( Token : : Match ( var - > typeStartToken ( ) , " float|double " ) )
continue ;
}
}
2012-05-17 07:26:57 +02:00
// If either variable token is an expanded macro then
// don't write the warning
if ( tok - > next ( ) - > isExpandedMacro ( ) | | tok - > tokAt ( 3 ) - > isExpandedMacro ( ) )
continue ;
2011-04-10 16:25:02 +02:00
duplicateExpressionError ( tok - > next ( ) , tok - > tokAt ( 3 ) , tok - > strAt ( 2 ) ) ;
2012-12-01 01:31:35 +01:00
} else if ( Token : : Match ( tok , " ,|=|return|(|&&|%oror% %var% . %var% %comp%|- %var% . %var% ) | & & | % oror % | ; | , " ) &&
2011-10-13 20:53:06 +02:00
tok - > strAt ( 1 ) = = tok - > strAt ( 5 ) & & tok - > strAt ( 3 ) = = tok - > strAt ( 7 ) ) {
2012-05-17 07:26:57 +02:00
// If either variable token is an expanded macro then
// don't write the warning
if ( tok - > next ( ) - > isExpandedMacro ( ) | | tok - > tokAt ( 6 ) - > isExpandedMacro ( ) )
continue ;
2011-04-10 16:25:02 +02:00
duplicateExpressionError ( tok - > next ( ) , tok - > tokAt ( 6 ) , tok - > strAt ( 4 ) ) ;
}
}
}
}
void CheckOther : : duplicateExpressionError ( const Token * tok1 , const Token * tok2 , const std : : string & op )
{
std : : list < const Token * > toks ;
toks . push_back ( tok2 ) ;
toks . push_back ( tok1 ) ;
reportError ( toks , Severity : : style , " duplicateExpression " , " Same expression on both sides of \' " + op + " \' . \n "
" Finding the same expression on both sides of an operator is suspicious and might "
" indicate a cut and paste or logic error. Please examine this code carefully to "
" determine if it is correct. " ) ;
}
2011-04-26 07:45:27 +02:00
//---------------------------------------------------------------------------
// Check for string comparison involving two static strings.
// if(strcmp("00FF00","00FF00")==0) // <- statement is always true
//---------------------------------------------------------------------------
void CheckOther : : checkAlwaysTrueOrFalseStringCompare ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2011-04-26 07:45:27 +02:00
return ;
const Token * tok = _tokenizer - > tokens ( ) ;
2013-01-16 22:05:28 +01:00
while ( tok & & ( tok = Token : : findmatch ( tok , " strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %str% , %str% " ) ) ! = NULL ) {
2012-03-11 11:01:39 +01:00
const std : : string & str1 = tok - > strAt ( 2 ) ;
const std : : string & str2 = tok - > strAt ( 4 ) ;
2012-03-15 20:38:28 +01:00
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
2011-04-26 07:45:27 +02:00
tok = tok - > tokAt ( 5 ) ;
}
tok = _tokenizer - > tokens ( ) ;
2013-01-16 22:05:28 +01:00
while ( tok & & ( tok = Token : : findmatch ( tok , " QString :: compare ( %str% , %str% ) " ) ) ! = NULL ) {
2012-03-11 11:01:39 +01:00
const std : : string & str1 = tok - > strAt ( 4 ) ;
const std : : string & str2 = tok - > strAt ( 6 ) ;
2012-03-15 20:38:28 +01:00
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
2011-04-26 07:45:27 +02:00
tok = tok - > tokAt ( 7 ) ;
}
2011-10-28 22:05:11 +02:00
tok = _tokenizer - > tokens ( ) ;
2013-01-16 22:05:28 +01:00
while ( tok & & ( tok = Token : : findmatch ( tok , " strncmp|strcmp|stricmp|strcmpi|strcasecmp|wcscmp|wcsncmp ( %var% , %var% " ) ) ! = NULL ) {
2012-03-11 11:01:39 +01:00
const std : : string & str1 = tok - > strAt ( 2 ) ;
const std : : string & str2 = tok - > strAt ( 4 ) ;
2011-10-29 11:52:19 +02:00
if ( str1 = = str2 )
alwaysTrueStringVariableCompareError ( tok , str1 , str2 ) ;
2011-10-28 22:05:11 +02:00
tok = tok - > tokAt ( 5 ) ;
}
2012-03-11 11:01:39 +01:00
tok = _tokenizer - > tokens ( ) ;
while ( tok & & ( tok = Token : : findmatch ( tok , " !!+ %str% ==|!= %str% !!+ " ) ) ! = NULL ) {
const std : : string & str1 = tok - > strAt ( 1 ) ;
const std : : string & str2 = tok - > strAt ( 3 ) ;
2012-03-15 20:38:28 +01:00
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
2012-03-11 11:01:39 +01:00
tok = tok - > tokAt ( 5 ) ;
}
2011-04-26 07:45:27 +02:00
}
2012-03-15 20:38:28 +01:00
void CheckOther : : alwaysTrueFalseStringCompareError ( const Token * tok , const std : : string & str1 , const std : : string & str2 )
2011-04-26 07:45:27 +02:00
{
2011-11-22 21:14:14 +01:00
const std : : size_t stringLen = 10 ;
2011-04-26 07:45:27 +02:00
const std : : string string1 = ( str1 . size ( ) < stringLen ) ? str1 : ( str1 . substr ( 0 , stringLen - 2 ) + " .. " ) ;
const std : : string string2 = ( str2 . size ( ) < stringLen ) ? str2 : ( str2 . substr ( 0 , stringLen - 2 ) + " .. " ) ;
2012-03-15 20:38:28 +01:00
reportError ( tok , Severity : : warning , " staticStringCompare " ,
2012-04-26 23:04:55 +02:00
" Unnecessary comparison of static strings. \n "
2012-03-15 20:38:28 +01:00
" The compared strings, ' " + string1 + " ' and ' " + string2 + " ', are always " + ( str1 = = str2 ? " identical " : " unequal " ) + " . "
2012-04-26 23:04:55 +02:00
" Therefore the comparison is unnecessary and looks suspicious. " ) ;
2011-04-26 07:45:27 +02:00
}
2011-10-28 22:05:11 +02:00
void CheckOther : : alwaysTrueStringVariableCompareError ( const Token * tok , const std : : string & str1 , const std : : string & str2 )
{
2011-10-29 11:52:19 +02:00
reportError ( tok , Severity : : warning , " stringCompare " ,
" Comparison of identical string variables. \n "
" The compared strings, ' " + str1 + " ' and ' " + str2 + " ', are identical. "
" This could be a logic bug. " ) ;
2011-10-28 22:05:11 +02:00
}
2012-08-24 14:25:17 +02:00
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckOther : : checkSuspiciousStringCompare ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2012-08-24 14:25:17 +02:00
return ;
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 - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > next ( ) - > type ( ) ! = Token : : eComparisonOp )
continue ;
2012-08-24 14:25:17 +02:00
2012-11-16 06:50:49 +01:00
const Token * varTok = tok ;
const Token * litTok = tok - > tokAt ( 2 ) ;
2012-08-24 14:25:17 +02:00
2012-11-16 06:50:49 +01:00
if ( varTok - > strAt ( - 1 ) = = " + " | | litTok - > strAt ( 1 ) = = " + " )
continue ;
2012-08-24 14:25:17 +02:00
2012-11-16 06:50:49 +01:00
if ( ( varTok - > type ( ) = = Token : : eString | | varTok - > type ( ) = = Token : : eVariable ) & & ( litTok - > type ( ) = = Token : : eString | | litTok - > type ( ) = = Token : : eVariable ) & & litTok - > type ( ) ! = varTok - > type ( ) ) {
if ( varTok - > type ( ) = = Token : : eString )
std : : swap ( varTok , litTok ) ;
2012-08-24 14:25:17 +02:00
2013-02-06 06:39:58 +01:00
const Variable * var = varTok - > variable ( ) ;
2012-11-16 06:50:49 +01:00
if ( var ) {
if ( _tokenizer - > isC ( ) | |
( var - > isPointer ( ) & & varTok - > strAt ( - 1 ) ! = " * " & & ! Token : : Match ( varTok - > next ( ) , " [.([] " ) ) )
suspiciousStringCompareError ( tok , var - > name ( ) ) ;
}
2012-10-07 16:26:03 +02:00
}
2012-08-24 14:25:17 +02:00
}
}
}
void CheckOther : : suspiciousStringCompareError ( const Token * tok , const std : : string & var )
{
reportError ( tok , Severity : : warning , " literalWithCharPtrCompare " ,
" String literal compared with variable ' " + var + " '. Did you intend to use strcmp() instead? " ) ;
}
2012-04-26 15:23:47 +02:00
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CheckOther : : checkModuloAlwaysTrueFalse ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
2012-11-15 07:11:45 +01:00
const SymbolDatabase * 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 ( ) ) {
2012-12-01 01:31:35 +01:00
if ( ( Token : : Match ( tok , " % %num% %comp% %num% " ) ) & &
2012-11-15 07:11:45 +01:00
( ! tok - > tokAt ( 4 ) | | ! tok - > tokAt ( 4 ) - > isArithmeticalOp ( ) ) ) {
if ( MathLib : : isLessEqual ( tok - > strAt ( 1 ) , tok - > strAt ( 3 ) ) )
moduloAlwaysTrueFalseError ( tok , tok - > strAt ( 1 ) ) ;
}
2012-04-26 15:23:47 +02:00
}
}
}
void CheckOther : : moduloAlwaysTrueFalseError ( const Token * tok , const std : : string & maxVal )
{
reportError ( tok , Severity : : warning , " moduloAlwaysTrueFalse " ,
2012-04-26 23:04:55 +02:00
" Comparison of modulo result is predetermined, because it is always less than " + maxVal + " . " ) ;
2012-04-26 15:23:47 +02:00
}
2013-02-11 20:26:27 +01:00
//-----------------------------------------------------------------------------
// Check for code like:
// seteuid(geteuid()) or setuid(getuid()), which first gets and then sets the
// (effective) user id to itself. Very often this indicates a copy and paste
// error.
//-----------------------------------------------------------------------------
void CheckOther : : redundantGetAndSetUserId ( )
{
if ( _settings - > isEnabled ( " warning " )
& & _settings - > standards . posix ) {
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " setuid ( getuid ( ) ) " )
| | Token : : simpleMatch ( tok , " seteuid ( geteuid ( ) ) " )
| | Token : : simpleMatch ( tok , " setgid ( getgid ( ) ) " )
| | Token : : simpleMatch ( tok , " setegid ( getegid ( ) ) " ) ) {
redundantGetAndSetUserIdError ( tok ) ;
}
}
}
}
void CheckOther : : redundantGetAndSetUserIdError ( const Token * tok )
{
reportError ( tok , Severity : : warning ,
" redundantGetAndSetUserId " , " Redundant get and set of user id. \n "
" Redundant statement without any effect. First the user id is retrieved "
" by get(e)uid() and then set with set(e)uid(). " , false ) ;
}
2011-10-10 19:11:17 +02:00
2011-08-07 01:23:09 +02:00
//---------------------------------------------------------------------------
2012-08-21 12:28:02 +02:00
// Check testing sign of unsigned variables and pointers.
2011-08-07 01:23:09 +02:00
//---------------------------------------------------------------------------
void CheckOther : : checkSignOfUnsignedVariable ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-08-07 01:23:09 +02:00
return ;
2011-11-06 18:24:37 +01:00
const bool inconclusive = _tokenizer - > codeWithTemplates ( ) ;
if ( inconclusive & & ! _settings - > inconclusive )
return ;
2011-08-07 01:23:09 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2012-11-15 07:11:45 +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 ] ;
2011-08-07 01:23:09 +02:00
// check all the code in the function
2012-11-15 07:11:45 +01:00
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-03-25 11:32:00 +02:00
if ( Token : : Match ( tok , " %var% <|<= 0 " ) & & tok - > varId ( ) & & ! Token : : Match ( tok - > previous ( ) , " ++|--|)|+|-|*|/|~|<<|>> " ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " +|- " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2011-08-07 01:23:09 +02:00
if ( var & & var - > typeEndToken ( ) - > isUnsigned ( ) )
2012-08-21 12:28:02 +02:00
unsignedLessThanZeroError ( tok , var - > name ( ) , inconclusive ) ;
else if ( var & & var - > isPointer ( ) & & tok - > strAt ( - 1 ) ! = " * " )
pointerLessThanZeroError ( tok , inconclusive ) ;
2012-03-25 11:32:00 +02:00
} else if ( Token : : Match ( tok , " 0 >|>= %var% " ) & & tok - > tokAt ( 2 ) - > varId ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " +|-|*|/ " ) & & ! Token : : Match ( tok - > previous ( ) , " +|-|<<|>>|~ " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > tokAt ( 2 ) - > variable ( ) ;
2011-08-07 01:23:09 +02:00
if ( var & & var - > typeEndToken ( ) - > isUnsigned ( ) )
2012-08-21 12:28:02 +02:00
unsignedLessThanZeroError ( tok , var - > name ( ) , inconclusive ) ;
else if ( var & & var - > isPointer ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " [.[] " ) )
pointerLessThanZeroError ( tok , inconclusive ) ;
2012-03-25 11:32:00 +02:00
} else if ( Token : : Match ( tok , " 0 <= %var% " ) & & tok - > tokAt ( 2 ) - > varId ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " +|-|*|/ " ) & & ! Token : : Match ( tok - > previous ( ) , " +|-|<<|>>|~ " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > tokAt ( 2 ) - > variable ( ) ;
2011-08-07 01:23:09 +02:00
if ( var & & var - > typeEndToken ( ) - > isUnsigned ( ) )
2012-08-21 12:28:02 +02:00
unsignedPositiveError ( tok , var - > name ( ) , inconclusive ) ;
else if ( var & & var - > isPointer ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " [.[] " ) )
pointerPositiveError ( tok , inconclusive ) ;
2012-03-25 11:32:00 +02:00
} else if ( Token : : Match ( tok , " %var% >= 0 " ) & & tok - > varId ( ) & & ! Token : : Match ( tok - > previous ( ) , " ++|--|)|+|-|*|/|~|<<|>> " ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " +|- " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2011-09-27 03:24:34 +02:00
if ( var & & var - > typeEndToken ( ) - > isUnsigned ( ) )
2012-08-21 12:28:02 +02:00
unsignedPositiveError ( tok , var - > name ( ) , inconclusive ) ;
else if ( var & & var - > isPointer ( ) & & tok - > strAt ( - 1 ) ! = " * " )
pointerPositiveError ( tok , inconclusive ) ;
2011-09-27 03:24:34 +02:00
}
2011-08-07 01:23:09 +02:00
}
}
}
2011-11-06 18:24:37 +01:00
void CheckOther : : unsignedLessThanZeroError ( const Token * tok , const std : : string & varname , bool inconclusive )
{
if ( inconclusive ) {
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : style , " unsignedLessThanZero " ,
" Checking if unsigned variable ' " + varname + " ' is less than zero. This might be a false warning. \n "
" Checking if unsigned variable ' " + varname + " ' is less than zero. An unsigned "
" variable will never be negative so it is either pointless or an error to check if it is. "
" It's not known if the used constant is a template parameter or not and therefore "
2012-09-16 19:44:02 +02:00
" this message might be a false warning. " , true ) ;
2011-11-06 18:24:37 +01:00
} else {
reportError ( tok , Severity : : style , " unsignedLessThanZero " ,
" Checking if unsigned variable ' " + varname + " ' is less than zero. \n "
2012-06-23 10:08:18 +02:00
" The unsigned variable ' " + varname + " ' will never be negative so it "
2012-03-25 11:32:00 +02:00
" is either pointless or an error to check if it is. " ) ;
2011-11-06 18:24:37 +01:00
}
2011-08-07 01:23:09 +02:00
}
2012-08-21 12:28:02 +02:00
void CheckOther : : pointerLessThanZeroError ( const Token * tok , bool inconclusive )
{
reportError ( tok , Severity : : style , " pointerLessThanZero " ,
" A pointer can not be negative so it is either pointless or an error to check if it is. " , inconclusive ) ;
}
2011-11-06 18:24:37 +01:00
void CheckOther : : unsignedPositiveError ( const Token * tok , const std : : string & varname , bool inconclusive )
{
if ( inconclusive ) {
2012-05-06 19:37:41 +02:00
reportError ( tok , Severity : : style , " unsignedPositive " ,
2012-09-16 19:44:02 +02:00
" Unsigned variable ' " + varname + " ' can't be negative so it is unnecessary to test it. \n "
" The unsigned variable ' " + varname + " ' can't be negative so it is unnecessary to test it. "
2012-05-06 19:37:41 +02:00
" It's not known if the used constant is a "
" template parameter or not and therefore this message might be a false warning " , true ) ;
2011-11-06 18:24:37 +01:00
} else {
reportError ( tok , Severity : : style , " unsignedPositive " ,
2012-09-16 19:44:02 +02:00
" Unsigned variable ' " + varname + " ' can't be negative so it is unnecessary to test it. " ) ;
2011-11-06 18:24:37 +01:00
}
2011-08-07 01:23:09 +02:00
}
2012-07-22 09:17:00 +02:00
2012-08-21 12:28:02 +02:00
void CheckOther : : pointerPositiveError ( const Token * tok , bool inconclusive )
{
reportError ( tok , Severity : : style , " pointerPositive " ,
" A pointer can not be negative so it is either pointless or an error to check if it is not. " , inconclusive ) ;
}
2012-07-22 09:17:00 +02:00
/*
This check rule works for checking the " const A a = getA() " usage when getA ( ) returns " const A & " or " A & " .
In most scenarios , " const A & a = getA() " will be more efficient .
*/
void CheckOther : : checkRedundantCopy ( )
{
2012-09-10 19:02:32 +02:00
if ( ! _settings - > isEnabled ( " performance " ) | | _tokenizer - > isC ( ) )
2012-07-22 09:17:00 +02:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2013-03-05 15:53:09 +01:00
for ( std : : size_t i = 0 ; i < symbolDatabase - > getVariableListSize ( ) ; i + + ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( i ) ;
2012-08-24 00:03:22 +02:00
2013-03-05 15:53:09 +01:00
if ( ! var | | var - > isReference ( ) | | ! var - > isConst ( ) | | var - > isPointer ( ) | | ! var - > type ( ) ) // bailout if var is of standard type, if it is a pointer or non-const
2012-08-24 00:03:22 +02:00
continue ;
2013-03-05 15:53:09 +01:00
const Token * startTok = var - > nameToken ( ) ;
const Token * endToken ;
if ( startTok - > strAt ( 1 ) = = " = " ) // %type% %var% = ... ;
endToken = Token : : findsimplematch ( startTok - > tokAt ( 2 ) , " ; " ) ;
else if ( startTok - > strAt ( 1 ) = = " ( " ) // %type% %var%(...)
endToken = startTok - > linkAt ( 1 ) ;
else
2012-08-24 00:03:22 +02:00
continue ;
2013-03-05 15:53:09 +01:00
const Token * tok = startTok - > tokAt ( 2 ) ;
while ( tok & & Token : : Match ( tok , " %var% .|:: " ) )
2012-08-24 00:03:22 +02:00
tok = tok - > tokAt ( 2 ) ;
if ( ! Token : : Match ( tok , " %var% ( " ) )
2013-03-05 15:53:09 +01:00
continue ;
if ( tok - > linkAt ( 1 ) - > next ( ) ! = endToken ) // bailout for usage like "const A a = getA()+3"
continue ;
2013-01-31 06:41:18 +01:00
const Function * func = tok - > function ( ) ;
2013-03-05 15:53:09 +01:00
if ( func & & func - > tokenDef - > strAt ( - 1 ) = = " & " ) {
redundantCopyError ( startTok , startTok - > str ( ) ) ;
2012-08-24 00:03:22 +02:00
}
}
}
void CheckOther : : redundantCopyError ( const Token * tok , const std : : string & varname )
2012-07-22 09:17:00 +02:00
{
2013-03-05 15:53:09 +01:00
reportError ( tok , Severity : : performance , " redundantCopyLocalConst " ,
2012-09-16 19:44:02 +02:00
" Use const reference for ' " + varname + " ' to avoid unnecessary data copying. \n "
" The const variable ' " + varname + " ' is assigned a copy of the data. You can avoid "
" the unnecessary data copying by converting ' " + varname + " ' to const reference. " ) ;
2012-08-24 00:03:22 +02:00
}
2012-08-22 15:44:20 +02:00
2012-08-24 00:03:22 +02:00
//---------------------------------------------------------------------------
// Checking for shift by negative values
//---------------------------------------------------------------------------
2012-08-22 15:44:20 +02:00
2012-08-24 00:03:22 +02:00
void CheckOther : : checkNegativeBitwiseShift ( )
{
2012-09-04 12:36:00 +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 - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( ( Token : : Match ( tok , " %var% >>|<< %num% " ) | | Token : : Match ( tok , " %num% >>|<< %num% " ) ) & & ! Token : : Match ( tok - > previous ( ) , " >>|<< " ) ) {
if ( tok - > isName ( ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2012-11-16 06:50:49 +01:00
if ( var & & var - > typeStartToken ( ) - > isStandardType ( ) & & ( tok - > strAt ( 2 ) ) [ 0 ] = = ' - ' )
negativeBitwiseShiftError ( tok ) ;
} else {
if ( ( tok - > strAt ( 2 ) ) [ 0 ] = = ' - ' )
negativeBitwiseShiftError ( tok ) ;
}
2012-09-04 12:36:00 +02:00
}
2012-08-22 15:44:20 +02:00
}
}
}
2012-08-24 00:03:22 +02:00
void CheckOther : : negativeBitwiseShiftError ( const Token * tok )
2012-08-22 15:44:20 +02:00
{
2012-08-24 00:03:22 +02:00
reportError ( tok , Severity : : error , " shiftNegative " , " Shifting by a negative value. " ) ;
2012-08-22 15:44:20 +02:00
}
2012-08-24 11:28:50 +02:00
//---------------------------------------------------------------------------
// Check for incompletely filled buffers.
//---------------------------------------------------------------------------
void CheckOther : : checkIncompleteArrayFill ( )
{
2013-03-03 11:41:59 +01:00
bool warning = _settings - > isEnabled ( " warning " ) ;
bool portability = _settings - > isEnabled ( " portability " ) ;
if ( ! _settings - > inconclusive | | ( ! portability & & ! warning ) )
2012-08-24 11:28:50 +02:00
return ;
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 - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " memset|memcpy|memmove ( %var% , " ) & & Token : : Match ( tok - > linkAt ( 1 ) - > tokAt ( - 2 ) , " , %num% ) " ) ) {
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > tokAt ( 2 ) - > variable ( ) ;
2012-11-16 06:50:49 +01:00
if ( ! var | | ! var - > isArray ( ) | | var - > dimensions ( ) . empty ( ) | | ! var - > dimension ( 0 ) )
continue ;
2012-08-24 11:28:50 +02:00
2012-11-16 06:50:49 +01:00
if ( MathLib : : toLongNumber ( tok - > linkAt ( 1 ) - > strAt ( - 1 ) ) = = var - > dimension ( 0 ) ) {
unsigned int size = _tokenizer - > sizeOfType ( var - > typeStartToken ( ) ) ;
2013-04-10 18:25:50 +02:00
if ( ( size ! = 1 & & size ! = 100 & & size ! = 0 ) | | var - > isPointer ( ) ) {
2013-03-03 11:41:59 +01:00
if ( warning )
incompleteArrayFillError ( tok , var - > name ( ) , tok - > str ( ) , false ) ;
} else if ( var - > typeStartToken ( ) - > str ( ) = = " bool " & & portability ) // sizeof(bool) is not 1 on all platforms
2012-11-16 06:50:49 +01:00
incompleteArrayFillError ( tok , var - > name ( ) , tok - > str ( ) , true ) ;
}
2012-08-24 11:28:50 +02:00
}
}
}
}
void CheckOther : : incompleteArrayFillError ( const Token * tok , const std : : string & buffer , const std : : string & function , bool boolean )
{
if ( boolean )
reportError ( tok , Severity : : portability , " incompleteArrayFill " ,
" Array ' " + buffer + " ' might be filled incompletely. Did you forget to multiply the size given to ' " + function + " ()' with 'sizeof(* " + buffer + " )'? \n "
" The array ' " + buffer + " ' is filled incompletely. The function ' " + function + " ()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(* " + buffer + " )'? " , true ) ;
else
reportError ( tok , Severity : : warning , " incompleteArrayFill " ,
" Array ' " + buffer + " ' is filled incompletely. Did you forget to multiply the size given to ' " + function + " ()' with 'sizeof(* " + buffer + " )'? \n "
" The array ' " + buffer + " ' is filled incompletely. The function ' " + function + " ()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(* " + buffer + " )'? " , true ) ;
}
2012-09-05 13:48:00 +02:00
2012-10-21 18:18:29 +02:00
void CheckOther : : oppositeInnerCondition ( )
2012-09-05 13:48:00 +02:00
{
2012-10-21 18:18:29 +02:00
// FIXME: This check is experimental because of #4170 and #4186. Fix those tickets and remove the "experimental".
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) | | ! _settings - > inconclusive | | ! _settings - > experimental )
2012-09-05 13:48:00 +02:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
const Token * const toke = scope - > classDef ;
if ( scope - > type = = Scope : : eIf & & toke ) {
const Token * op1Tok , * op2Tok ;
op1Tok = scope - > classDef - > tokAt ( 2 ) ;
op2Tok = scope - > classDef - > tokAt ( 4 ) ;
if ( scope - > classDef - > strAt ( 6 ) = = " { " ) {
2012-12-24 12:21:48 +01:00
const char * oppositeCondition = NULL ;
if ( scope - > classDef - > strAt ( 3 ) = = " == " )
oppositeCondition = " if ( %any% !=|<|>|<=|>= %any% ) " ;
else if ( scope - > classDef - > strAt ( 3 ) = = " != " )
oppositeCondition = " if ( %any% ==|>=|<= %any% ) " ;
else if ( scope - > classDef - > strAt ( 3 ) = = " < " )
oppositeCondition = " if ( %any% >|>=|== %any% ) " ;
else if ( scope - > classDef - > strAt ( 3 ) = = " <= " )
oppositeCondition = " if ( %any% > %any% ) " ;
else if ( scope - > classDef - > strAt ( 3 ) = = " > " )
oppositeCondition = " if ( %any% <|<=|== %any% ) " ;
else if ( scope - > classDef - > strAt ( 3 ) = = " >= " )
oppositeCondition = " if ( %any% < %any% ) " ;
if ( oppositeCondition ) {
2012-09-15 15:54:52 +02:00
int flag = 0 ;
2012-09-05 13:48:00 +02:00
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd & & flag = = 0 ; tok = tok - > next ( ) ) {
if ( ( tok - > str ( ) = = op1Tok - > str ( ) | | tok - > str ( ) = = op2Tok - > str ( ) ) & & tok - > strAt ( 1 ) = = " = " )
break ;
else if ( Token : : Match ( tok , " %any% ( %any% ) " )) {
if ( ( tok - > strAt ( 2 ) = = op1Tok - > str ( ) | | tok - > strAt ( 2 ) = = op2Tok - > str ( ) ) )
break ;
} else if ( Token : : Match ( tok , " %any% ( %any% , %any% " ) ) {
for ( const Token * tok2 = tok - > next ( ) ; tok2 ! = tok - > linkAt ( 1 ) ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = op1Tok - > str ( ) ) {
flag = 1 ;
break ;
}
}
2012-12-24 12:21:48 +01:00
} else if ( Token : : Match ( tok , oppositeCondition ) ) {
2012-09-05 13:48:00 +02:00
if ( ( tok - > strAt ( 2 ) = = op1Tok - > str ( ) & & tok - > strAt ( 4 ) = = op2Tok - > str ( ) ) | | ( tok - > strAt ( 2 ) = = op2Tok - > str ( ) & & tok - > strAt ( 4 ) = = op1Tok - > str ( ) ) )
2012-10-21 18:18:29 +02:00
oppositeInnerConditionError ( toke ) ;
2012-09-05 13:48:00 +02:00
}
}
}
}
}
}
}
2012-10-21 18:18:29 +02:00
void CheckOther : : oppositeInnerConditionError ( const Token * tok )
2012-09-05 13:48:00 +02:00
{
2012-10-21 18:18:29 +02:00
reportError ( tok , Severity : : warning , " oppositeInnerCondition " , " Opposite conditions in nested 'if' blocks lead to a dead code block. " , true ) ;
2012-09-05 13:48:00 +02:00
}
2013-01-13 12:02:10 +01:00
void CheckOther : : checkVarFuncNullUB ( )
{
if ( ! _settings - > isEnabled ( " portability " ) )
return ;
const SymbolDatabase * 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 ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
// Is NULL passed to a function?
if ( Token : : Match ( tok , " [(,] NULL [,)] " ) ) {
// Locate function name in this function call.
const Token * ftok = tok ;
2013-01-16 15:37:07 +01:00
std : : size_t argnr = 1 ;
2013-01-13 12:02:10 +01:00
while ( ftok & & ftok - > str ( ) ! = " ( " ) {
if ( ftok - > str ( ) = = " ) " )
ftok = ftok - > link ( ) ;
2013-01-14 06:44:52 +01:00
else if ( ftok - > str ( ) = = " , " )
+ + argnr ;
2013-01-13 12:02:10 +01:00
ftok = ftok - > previous ( ) ;
}
ftok = ftok ? ftok - > previous ( ) : NULL ;
if ( ftok & & ftok - > isName ( ) ) {
// If this is a variadic function then report error
2013-01-31 06:41:18 +01:00
const Function * f = ftok - > function ( ) ;
2013-01-14 06:44:52 +01:00
if ( f & & f - > argCount ( ) < = argnr ) {
2013-01-13 12:02:10 +01:00
const Token * tok2 = f - > argDef ;
tok2 = tok2 ? tok2 - > link ( ) : NULL ; // goto ')'
if ( Token : : simpleMatch ( tok2 - > tokAt ( - 3 ) , " . . . " ) )
varFuncNullUBError ( tok ) ;
}
}
}
}
}
}
void CheckOther : : varFuncNullUBError ( const Token * tok )
{
reportError ( tok ,
Severity : : portability ,
" varFuncNullUB " ,
2013-01-14 18:11:16 +01:00
" Passing NULL after the last typed argument to a variadic function leads to undefined behaviour. \n "
" Passing NULL after the last typed argument to a variadic function leads to undefined behaviour. \n "
" The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined. \n "
" The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL. \n "
" In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int. \n "
" To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \" ERROR \" , the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \" ERROR \" output go away. \n "
2013-01-13 12:02:10 +01:00
" #include <stdarg.h> \n "
" #include <stdio.h> \n "
" \n "
" void f(char *s, ...) { \n "
" va_list ap; \n "
" va_start(ap,s); \n "
" for (;;) { \n "
" char *p = va_arg(ap,char*); \n "
" printf( \" %018p, %s \n \" , p, (long)p & 255 ? p : \" \" ); \n "
" if(!p) break; \n "
" } \n "
" va_end(ap); \n "
" } \n "
" \n "
" void g() { \n "
" char *s2 = \" x \" ; \n "
2013-01-14 18:11:16 +01:00
" char *s3 = \" ERROR \" ; \n "
2013-01-13 12:02:10 +01:00
" \n "
2013-01-14 18:11:16 +01:00
" // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64 \n "
2013-01-13 12:02:10 +01:00
" f( \" first \" , s2, s2, s2, s2, s2, 0, s3, (char*)0); \n "
" } \n "
" \n "
" void h() { \n "
" int i; \n "
" volatile unsigned char a[1000]; \n "
" for (i = 0; i<sizeof(a); i++) \n "
" a[i] = -1; \n "
" } \n "
" \n "
" int main() { \n "
" h(); \n "
" g(); \n "
" return 0; \n "
" } " ) ;
}