2009-01-26 20:14:46 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and Cppcheck team .
2009-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
}
2014-08-29 17:06:46 +02:00
bool astIsFloat ( const Token * tok , bool unknown )
2014-02-04 06:49:49 +01:00
{
2014-08-17 20:35:24 +02:00
if ( tok - > astOperand2 ( ) & & ( tok - > str ( ) = = " . " | | tok - > str ( ) = = " :: " ) )
2014-08-15 16:48:53 +02:00
return astIsFloat ( tok - > astOperand2 ( ) , unknown ) ;
2014-07-26 09:09:58 +02:00
if ( tok - > astOperand1 ( ) & & tok - > str ( ) ! = " ? " & & astIsFloat ( tok - > astOperand1 ( ) , unknown ) )
2014-02-04 06:49:49 +01:00
return true ;
2014-03-22 18:42:29 +01:00
if ( tok - > astOperand2 ( ) & & astIsFloat ( tok - > astOperand2 ( ) , unknown ) )
2014-02-04 06:49:49 +01:00
return true ;
2014-03-22 18:42:29 +01:00
if ( tok - > isNumber ( ) )
return MathLib : : isFloat ( tok - > str ( ) ) ;
if ( tok - > isName ( ) ) {
// TODO: check function calls, struct members, arrays, etc also
if ( ! tok - > variable ( ) )
return unknown ;
2014-08-09 11:44:55 +02:00
return tok - > variable ( ) - > isFloatingType ( ) ;
2014-03-22 18:42:29 +01:00
}
2014-05-22 21:46:48 +02:00
if ( tok - > isOp ( ) )
return false ;
2014-03-22 18:42:29 +01:00
return unknown ;
2014-02-04 06:49:49 +01:00
}
2013-12-24 07:21:46 +01:00
static bool isConstExpression ( const Token * tok , const std : : set < std : : string > & constFunctions )
{
if ( ! tok )
return true ;
if ( tok - > isName ( ) & & tok - > next ( ) - > str ( ) = = " ( " ) {
if ( ! tok - > function ( ) & & ! Token : : Match ( tok - > previous ( ) , " .|:: " ) & & constFunctions . find ( tok - > str ( ) ) = = constFunctions . end ( ) )
return false ;
else if ( tok - > function ( ) & & ! tok - > function ( ) - > isConst )
return false ;
}
2014-05-11 13:37:53 +02:00
if ( tok - > type ( ) = = Token : : eIncDecOp )
2013-12-24 07:21:46 +01:00
return false ;
// bailout when we see ({..})
if ( tok - > str ( ) = = " { " )
return false ;
return isConstExpression ( tok - > astOperand1 ( ) , constFunctions ) & & isConstExpression ( tok - > astOperand2 ( ) , constFunctions ) ;
}
2013-11-07 14:38:08 +01:00
2014-04-03 19:35:50 +02:00
bool isSameExpression ( const Token * tok1 , const Token * tok2 , const std : : set < std : : string > & constFunctions )
2013-11-07 14:38:08 +01:00
{
2014-02-15 08:46:28 +01:00
if ( tok1 = = nullptr & & tok2 = = nullptr )
2013-11-07 14:38:08 +01:00
return true ;
2014-02-15 08:46:28 +01:00
if ( tok1 = = nullptr | | tok2 = = nullptr )
2013-11-07 14:38:08 +01:00
return false ;
2014-05-21 18:19:42 +02:00
if ( tok1 - > str ( ) = = " . " & & tok1 - > astOperand1 ( ) & & tok1 - > astOperand1 ( ) - > str ( ) = = " this " )
tok1 = tok1 - > astOperand2 ( ) ;
if ( tok2 - > str ( ) = = " . " & & tok2 - > astOperand1 ( ) & & tok2 - > astOperand1 ( ) - > str ( ) = = " this " )
tok2 = tok2 - > astOperand2 ( ) ;
2014-03-17 12:01:39 +01:00
if ( tok1 - > str ( ) ! = tok2 - > str ( ) | | tok1 - > varId ( ) ! = tok2 - > varId ( ) )
2013-11-07 14:38:08 +01:00
return false ;
2014-04-12 12:01:54 +02:00
if ( tok1 - > str ( ) = = " . " & & tok1 - > originalName ( ) ! = tok2 - > originalName ( ) )
return false ;
2013-11-07 14:38:08 +01:00
if ( tok1 - > isExpandedMacro ( ) | | tok2 - > isExpandedMacro ( ) )
return false ;
if ( tok1 - > isName ( ) & & tok1 - > next ( ) - > str ( ) = = " ( " ) {
2014-03-14 06:38:45 +01:00
if ( ! tok1 - > function ( ) & & ! Token : : Match ( tok1 - > previous ( ) , " .|:: " ) & & constFunctions . find ( tok1 - > str ( ) ) = = constFunctions . end ( ) & & ! tok1 - > isAttributeConst ( ) & & ! tok1 - > isAttributePure ( ) )
2013-11-07 14:38:08 +01:00
return false ;
2014-03-14 06:38:45 +01:00
else if ( tok1 - > function ( ) & & ! tok1 - > function ( ) - > isConst & & ! tok1 - > function ( ) - > isAttributeConst ( ) & & ! tok1 - > function ( ) - > isAttributePure ( ) )
2013-11-07 14:38:08 +01:00
return false ;
}
2014-01-12 07:40:56 +01:00
// templates/casts
2014-01-11 15:15:01 +01:00
if ( ( Token : : Match ( tok1 , " %var% < " ) & & tok1 - > next ( ) - > link ( ) ) | |
2014-01-12 07:40:56 +01:00
( Token : : Match ( tok2 , " %var% < " ) & & tok2 - > next ( ) - > link ( ) ) ) {
// non-const template function that is not a dynamic_cast => return false
2014-03-14 16:26:37 +01:00
if ( Token : : simpleMatch ( tok1 - > next ( ) - > link ( ) , " > ( " ) & &
2014-01-12 07:40:56 +01:00
! ( tok1 - > function ( ) & & tok1 - > function ( ) - > isConst ) & &
tok1 - > str ( ) ! = " dynamic_cast " )
return false ;
// some template/cast stuff.. check that the template arguments are same
const Token * t1 = tok1 - > next ( ) ;
const Token * t2 = tok2 - > next ( ) ;
const Token * end1 = tok1 - > next ( ) - > link ( ) ;
const Token * end2 = tok2 - > next ( ) - > link ( ) ;
while ( t1 & & t2 & & t1 ! = end1 & & t2 ! = end2 ) {
if ( t1 - > str ( ) ! = t2 - > str ( ) )
return false ;
t1 = t1 - > next ( ) ;
t2 = t2 - > next ( ) ;
}
if ( t1 ! = end1 | | t2 ! = end2 )
return false ;
}
2014-05-11 13:37:53 +02:00
if ( tok1 - > type ( ) = = Token : : eIncDecOp | | tok1 - > isAssignmentOp ( ) )
2013-11-11 16:39:34 +01:00
return false ;
2013-11-24 15:17:08 +01:00
if ( tok1 - > str ( ) = = " ( " & & tok1 - > previous ( ) & & ! tok1 - > previous ( ) - > isName ( ) ) { // cast => assert that the casts are equal
const Token * t1 = tok1 - > next ( ) ;
const Token * t2 = tok2 - > next ( ) ;
while ( t1 & & t2 & & t1 - > str ( ) = = t2 - > str ( ) & & ( t1 - > isName ( ) | | t1 - > str ( ) = = " * " ) ) {
t1 = t1 - > next ( ) ;
t2 = t2 - > next ( ) ;
}
if ( ! t1 | | ! t2 | | t1 - > str ( ) ! = " ) " | | t2 - > str ( ) ! = " ) " )
return false ;
}
2013-11-25 04:08:28 +01:00
// bailout when we see ({..})
if ( tok1 - > str ( ) = = " { " )
return false ;
2014-05-22 21:46:48 +02:00
bool noncommuative_equals =
isSameExpression ( tok1 - > astOperand1 ( ) , tok2 - > astOperand1 ( ) , constFunctions ) ;
noncommuative_equals = noncommuative_equals & &
isSameExpression ( tok1 - > astOperand2 ( ) , tok2 - > astOperand2 ( ) , constFunctions ) ;
if ( noncommuative_equals )
return true ;
2014-07-01 15:55:29 +02:00
bool commutative = tok1 - > astOperand1 ( ) & & tok1 - > astOperand2 ( ) & & Token : : Match ( tok1 , " %or%|%oror%|+|*|&|&&|^|==|!= " ) ;
2014-05-22 21:46:48 +02:00
bool commuative_equals = commutative & &
isSameExpression ( tok1 - > astOperand2 ( ) , tok2 - > astOperand1 ( ) , constFunctions ) ;
commuative_equals = commuative_equals & &
isSameExpression ( tok1 - > astOperand1 ( ) , tok2 - > astOperand2 ( ) , constFunctions ) ;
return commuative_equals ;
2013-11-07 14:38:08 +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 ) ;
2013-07-29 12:01:52 +02:00
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
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 ) ;
2013-07-29 12:01:52 +02:00
const Variable * var = tok - > variable ( ) ;
if ( var & & var - > typeEndToken ( ) - > str ( ) = = " char " & & ! var - > typeEndToken ( ) - > isSigned ( ) ) {
checkCastIntToCharAndBackError ( tok , " cin.get " ) ;
2013-03-04 14:25:35 +01:00
}
} 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
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Clarify calculation precedence for ternary operators.
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 ( ) ) {
2013-12-24 10:07:20 +01:00
// ? operator where lhs is arithmetical expression
2013-12-28 11:02:39 +01:00
if ( tok - > str ( ) ! = " ? " | | ! tok - > astOperand1 ( ) | | ! tok - > astOperand1 ( ) - > isArithmeticalOp ( ) | | ! tok - > astOperand1 ( ) - > isCalculation ( ) )
2013-12-24 10:07:20 +01:00
continue ;
// Is code clarified by parentheses already?
const Token * tok2 = tok - > astOperand1 ( ) ;
for ( ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ( " )
tok2 = tok2 - > link ( ) ;
else if ( tok2 - > str ( ) = = " ) " | | tok2 - > str ( ) = = " ? " )
break ;
2011-01-24 21:40:49 +01:00
}
2013-12-24 10:07:20 +01:00
if ( tok2 & & tok2 - > str ( ) = = " ? " )
clarifyCalculationError ( tok , tok - > astOperand1 ( ) - > str ( ) ) ;
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
}
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 ] ;
2014-02-27 18:24:51 +01:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2014-05-18 13:35:00 +02:00
if ( Token : : Match ( tok , " * %var% " ) & & tok - > astOperand1 ( ) ) {
2012-11-14 18:12:33 +01:00
const Token * tok2 = tok - > previous ( ) ;
while ( tok2 & & tok2 - > str ( ) = = " * " )
2014-05-18 13:35:00 +02:00
tok2 = tok2 - > previous ( ) ;
2012-11-14 18:12:33 +01:00
if ( Token : : Match ( tok2 , " [{};] " ) ) {
2014-05-18 13:35:00 +02:00
tok2 = tok - > astOperand1 ( ) ;
if ( Token : : Match ( tok2 , " ++|-- [;,] " ) )
clarifyStatementError ( tok2 ) ;
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
}
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Check for suspicious occurences of 'if(); {}'.
//---------------------------------------------------------------------------
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 ) {
2014-07-02 16:16:19 +02:00
if ( i - > type = = Scope : : eIf | | i - > type = = Scope : : eElse | | 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
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// For C++ code, warn if C-style casts are used on pointer types
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
2014-04-29 06:21:30 +02:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : size_t i = 0 ; i < symbolDatabase - > functionScopes . size ( ) ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
2014-08-04 11:20:48 +02:00
const Token * tok ;
if ( scope - > function & & scope - > function - > isConstructor ( ) )
tok = scope - > classDef ;
else
tok = scope - > classStart ;
for ( ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2014-04-29 06:21:30 +02:00
// Old style pointer casting..
2014-08-04 11:20:48 +02:00
if ( ! Token : : Match ( tok , " ( const| %type% * ) (| %var%|%num%|%bool%|%char%|%str% " ) )
2014-04-29 06:21:30 +02:00
continue ;
2009-01-26 20:14:46 +01:00
2014-04-29 06:21:30 +02:00
if ( tok - > strAt ( 1 ) = = " const " )
tok = tok - > next ( ) ;
2009-07-20 21:52:27 +02:00
2014-08-04 11:42:14 +02:00
if ( tok - > strAt ( 4 ) = = " 0 " ) // Casting nullpointers is safe
continue ;
2014-04-29 06:21:30 +02:00
// Is "type" a class?
if ( _tokenizer - > getSymbolDatabase ( ) - > isClassOrStruct ( tok - > strAt ( 1 ) ) )
cstyleCastError ( tok ) ;
}
2009-01-26 20:14:46 +01:00
}
}
2011-08-19 17:53:43 +02:00
void CheckOther : : cstyleCastError ( const Token * tok )
{
2014-05-22 08:57:45 +02:00
reportError ( tok , Severity : : style , " cstyleCast " ,
" C-style pointer casting \n "
" C-style pointer casting detected. C++ offers four different kinds of casts as replacements: "
" static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to "
2014-05-22 15:45:28 +02:00
" any of those automatically, thus it is considered safer if the programmer explicitly states "
2014-05-22 19:48:00 +02:00
" which kind of cast is expected. See also: https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts. " ) ;
2011-08-19 17:53:43 +02:00
}
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
2014-02-16 11:47:52 +01:00
const Variable * var = nullptr ;
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-07-02 13:28:44 +02:00
if ( var & & var - > isArray ( ) & & ! var - > isArgument ( ) & & ( ( dim = var - > dimension ( 0U ) ) < 2 ) ) {
2013-09-21 18:48:48 +02:00
const std : : string strDim = MathLib : : toString ( dim ) ;
2013-02-17 17:33:32 +01:00
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
}
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 ;
}
}
2013-11-04 23:46:07 +01:00
static void eraseMemberAssignments ( const unsigned int varId , std : : map < unsigned int , std : : set < unsigned int > > & membervars , std : : map < unsigned int , const Token * > & varAssignments )
{
const std : : map < unsigned int , std : : set < unsigned int > > : : const_iterator it = membervars . find ( varId ) ;
if ( it ! = membervars . end ( ) ) {
2013-11-14 16:10:00 +01:00
const std : : set < unsigned int > v = it - > second ;
for ( std : : set < unsigned int > : : const_iterator vit = v . begin ( ) ; vit ! = v . end ( ) ; + + vit ) {
2013-11-04 23:46:07 +01:00
varAssignments . erase ( * vit ) ;
2013-11-14 16:10:00 +01:00
if ( * vit ! = varId )
eraseMemberAssignments ( * vit , membervars , varAssignments ) ;
}
2013-11-04 23:46:07 +01:00
}
}
2012-09-02 13:09:32 +02:00
void CheckOther : : checkRedundantAssignment ( )
{
2013-08-15 16:13:58 +02:00
const bool performance = _settings - > isEnabled ( " performance " ) ;
const bool warning = _settings - > isEnabled ( " warning " ) ;
2013-03-03 11:41:59 +01:00
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-11-04 23:46:07 +01:00
std : : map < unsigned int , std : : set < unsigned int > > membervars ;
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 ( ) ;
2014-02-23 11:02:39 +01:00
} else if ( tok - > type ( ) = = Token : : eVariable & & ! Token : : Match ( tok , " %var% ( " ) ) {
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 ( ) ) ;
}
2013-08-15 16:13:58 +02:00
const Token * startToken = tok ;
2013-11-04 23:46:07 +01:00
while ( Token : : Match ( startToken , " %var%|::|. " ) ) {
2013-08-15 16:13:58 +02:00
startToken = startToken - > previous ( ) ;
2013-11-04 23:46:07 +01:00
if ( Token : : Match ( startToken , " %var% . %var% " ) )
membervars [ startToken - > varId ( ) ] . insert ( startToken - > tokAt ( 2 ) - > varId ( ) ) ;
}
2013-08-15 16:13:58 +02:00
2012-09-02 13:09:32 +02:00
std : : map < unsigned int , const Token * > : : iterator it = varAssignments . find ( tok - > varId ( ) ) ;
2013-08-15 16:13:58 +02:00
if ( tok - > next ( ) - > isAssignmentOp ( ) & & Token : : Match ( startToken , " [;{}] " ) ) { // Assignment
2012-09-02 13:09:32 +02:00
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-11-06 15:15:32 +01:00
const Function * const func = tok2 - > function ( ) ;
const Variable * const 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-10-23 06:42:52 +02:00
else if ( performance ) {
const bool nonlocal = nonLocal ( it - > second - > variable ( ) ) ;
if ( _settings - > inconclusive | | ! nonlocal ) // see #5089 - report inconclusive only when requested
redundantAssignmentError ( it - > second , tok , tok - > str ( ) , nonlocal ) ; // 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 ( ) ) ;
2013-11-04 23:46:07 +01:00
eraseMemberAssignments ( tok - > varId ( ) , membervars , varAssignments ) ;
2013-06-10 22:02:02 +02:00
} else if ( tok - > next ( ) - > type ( ) = = Token : : eIncDecOp | | ( tok - > previous ( ) - > type ( ) = = Token : : eIncDecOp & & tok - > strAt ( 1 ) = = " ; " )) { // Variable incremented/decremented; Prefix-Increment is only suspicious, if its return value is unused
2012-09-02 13:09:32 +02:00
varAssignments [ tok - > varId ( ) ] = tok ;
memAssignments . erase ( tok - > varId ( ) ) ;
2013-11-04 23:46:07 +01:00
eraseMemberAssignments ( tok - > varId ( ) , membervars , varAssignments ) ;
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
2013-11-06 15:15:32 +01:00
const bool memfunc = Token : : Match ( tok , " memcpy|memmove|memset|strcpy|strncpy|sprintf|snprintf|strcat|strncat|wcscpy|wcsncpy|swprintf|wcscat|wcsncat " ) ;
2014-04-10 21:56:30 +02:00
if ( tok - > varId ( ) ) // operator() or function pointer
varAssignments . erase ( tok - > varId ( ) ) ;
2014-04-27 11:00:47 +02:00
if ( memfunc & & tok - > strAt ( - 1 ) ! = " ( " & & tok - > strAt ( - 1 ) ! = " = " ) {
2012-09-02 13:09:32 +02:00
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 ( ) ) ;
2014-07-23 15:06:27 +02:00
if ( warning & & scope - > type = = Scope : : eSwitch & & Token : : findmatch ( it - > second , " default|case " , tok ) )
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-11-06 15:15:32 +01:00
const Function * const 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
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Detect fall through cases (experimental).
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 ;
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-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 ( ) ;
2014-04-11 10:47:02 +02:00
if ( tok2 - > link ( ) ) // skip over "do { } while ( ) ;" case
tok2 = 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 { " ) ) {
2014-04-11 10:47:02 +02:00
tok2 = tok2 - > next ( ) - > link ( ) ;
2011-02-19 20:02:28 +01:00
justbreak = false ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : simpleMatch ( tok2 , " for ( " ) ) {
2014-04-11 10:47:02 +02:00
tok2 = tok2 - > next ( ) - > link ( ) - > next ( ) - > link ( ) ;
2011-02-19 20:02:28 +01:00
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 " ) ) {
2014-04-11 10:47:02 +02: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 ( ) = = " } " ) {
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 ( ) ;
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 ( 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 " ) {
2014-04-11 11:56:25 +02:00
const Token * finding = nullptr ;
2012-12-07 21:27:32 +01:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
2014-04-11 11:56:25 +02:00
if ( tok2 - > str ( ) = = " : " )
2012-12-07 21:27:32 +01:00
break ;
2014-04-11 11:56:25 +02:00
if ( Token : : Match ( tok2 , " [;}{] " ) )
2012-12-07 21:44:30 +01:00
break ;
2014-04-11 11:56:25 +02:00
if ( tok2 - > str ( ) = = " ? " )
finding = nullptr ;
2012-12-07 21:44:30 +01:00
2014-04-11 11:56:25 +02:00
if ( Token : : Match ( tok2 , " &&|%oror% " ) )
finding = tok2 ;
2012-12-07 21:27:32 +01:00
}
2014-04-11 11:56:25 +02:00
if ( finding )
suspiciousCaseInSwitchError ( finding , 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)
2014-04-11 11:56:25 +02:00
if ( Token : : Match ( openParen - > next ( ) , " %var% == " ) )
suspiciousEqualityComparisonError ( openParen - > tokAt ( 2 ) ) ;
if ( Token : : Match ( closeParen - > tokAt ( - 2 ) , " == %any% " ) )
suspiciousEqualityComparisonError ( closeParen - > tokAt ( - 2 ) ) ;
2013-01-18 08:03:04 +01:00
// 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++)
2014-04-11 11:56:25 +02:00
if ( Token : : Match ( openParen - > next ( ) , " ! %var% " ) )
suspiciousEqualityComparisonError ( openParen - > next ( ) ) ;
if ( Token : : Match ( closeParen - > tokAt ( - 2 ) , " ! %var% " ) )
suspiciousEqualityComparisonError ( closeParen - > tokAt ( - 2 ) ) ;
2013-01-18 08:03:04 +01:00
// 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
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
{
2013-12-23 10:06: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 : : Match ( tok , " %var% ( !!) " ) )
continue ;
2014-03-27 15:50:30 +01:00
const std : : string & functionName = tok - > str ( ) ;
2013-12-23 10:06:45 +01:00
int argnr = 1 ;
const Token * argtok = tok - > tokAt ( 2 ) ;
while ( argtok & & argtok - > str ( ) ! = " ) " ) {
if ( Token : : Match ( argtok , " %num% [,)] " ) ) {
if ( MathLib : : isInt ( argtok - > str ( ) ) & &
! _settings - > library . isargvalid ( functionName , argnr , MathLib : : toLongNumber ( argtok - > str ( ) ) ) )
invalidFunctionArgError ( argtok , functionName , argnr , _settings - > library . validarg ( functionName , argnr ) ) ;
} else {
const Token * top = argtok ;
while ( top - > astParent ( ) & & top - > astParent ( ) - > str ( ) ! = " , " & & top - > astParent ( ) ! = tok - > next ( ) )
top = top - > astParent ( ) ;
if ( top - > isComparisonOp ( ) | | Token : : Match ( top , " %oror%|&& " ) ) {
if ( _settings - > library . isboolargbad ( functionName , argnr ) )
invalidFunctionArgBoolError ( top , functionName , argnr ) ;
// Are the values 0 and 1 valid?
else if ( ! _settings - > library . isargvalid ( functionName , argnr , 0 ) )
invalidFunctionArgError ( top , functionName , argnr , _settings - > library . validarg ( functionName , argnr ) ) ;
else if ( ! _settings - > library . isargvalid ( functionName , argnr , 1 ) )
invalidFunctionArgError ( top , functionName , argnr , _settings - > library . validarg ( functionName , argnr ) ) ;
}
}
argnr + + ;
argtok = argtok - > nextArgument ( ) ;
2009-01-26 20:14:46 +01:00
}
2013-12-23 10:06:45 +01:00
}
2009-01-26 20:14:46 +01:00
}
2011-08-19 17:53:43 +02:00
}
2013-12-23 10:06:45 +01:00
void CheckOther : : invalidFunctionArgError ( const Token * tok , const std : : string & functionName , int argnr , const std : : string & validstr )
{
std : : ostringstream errmsg ;
errmsg < < " Invalid " < < functionName < < " () argument nr " < < argnr ;
if ( ! tok )
;
else if ( tok - > isNumber ( ) )
errmsg < < " . The value is " < < tok - > str ( ) < < " but the valid values are ' " < < validstr < < " '. " ;
else if ( tok - > isComparisonOp ( ) )
errmsg < < " . The value is 0 or 1 (comparison result) but the valid values are ' " < < validstr < < " '. " ;
reportError ( tok , Severity : : error , " invalidFunctionArg " , errmsg . str ( ) ) ;
}
void CheckOther : : invalidFunctionArgBoolError ( const Token * tok , const std : : string & functionName , int argnr )
{
std : : ostringstream errmsg ;
errmsg < < " Invalid " < < functionName < < " () argument nr " < < argnr < < " . A non-boolean value is required. " ;
reportError ( tok , Severity : : error , " invalidFunctionArgBool " , errmsg . str ( ) ) ;
}
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 ;
2014-03-22 18:56:47 +01:00
if ( tok - > link ( ) & & Token : : Match ( tok , " (|[|< " ) )
2011-12-07 18:20:52 +01:00
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 ( ) ;
2014-03-22 22:35:20 +01:00
} else if ( Token : : Match ( tok , " %var% ( " ) & & _settings - > library . isnoreturn ( tok - > str ( ) ) & & tok - > strAt ( - 1 ) ! = " . " ) {
2014-03-22 19:55:13 +01:00
if ( ( ! tok - > function ( ) | | ( tok - > function ( ) - > token ! = tok & & tok - > function ( ) - > tokenDef ! = tok ) ) & & tok - > linkAt ( 1 ) - > strAt ( 1 ) ! = " { " )
2014-03-22 18:56:47 +01:00
secondBreak = tok - > linkAt ( 1 ) - > tokAt ( 2 ) ;
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 ;
2014-04-23 07:58:43 +02:00
tok = tok - > previous ( ) ; // Will be advanced again by for loop
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-08-07 16:27:37 +02: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
{
2014-08-09 11:44:55 +02:00
return ( var & & ! var - > typeStartToken ( ) - > isUnsigned ( ) & & var - > isIntegralType ( ) & & ! 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-11-07 14:15:36 +01:00
const std : : string verbose ( summary + " The second and third arguments might be inverted. "
" The function memset ( void * ptr, int value, size_t num ) sets the "
" first num bytes of the block of memory pointed by ptr to the specified value. " ) ;
2011-08-19 17:53:43 +02:00
reportError ( tok , Severity : : warning , " memsetZeroBytes " , summary + " \n " + verbose ) ;
}
2009-01-26 20:14:46 +01:00
2014-02-15 19:06:00 +01:00
void CheckOther : : checkMemsetInvalid2ndParam ( )
{
const bool portability = _settings - > isEnabled ( " portability " ) ;
const bool warning = _settings - > isEnabled ( " warning " ) ;
if ( ! warning & & ! 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 - > next ( ) ; tok & & ( tok ! = scope - > classEnd ) ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " memset ( " ) ) {
const Token * firstParamTok = tok - > tokAt ( 2 ) ;
if ( ! firstParamTok )
continue ;
const Token * secondParamTok = firstParamTok - > nextArgument ( ) ;
if ( ! secondParamTok )
continue ;
2014-03-22 18:42:29 +01:00
2014-04-11 12:00:33 +02:00
// Second parameter is zero literal, i.e. 0.0f
if ( Token : : Match ( secondParamTok , " %num% , " ) & & MathLib : : isNullValue ( secondParamTok - > str ( ) ) )
2014-03-22 18:42:29 +01:00
continue ;
const Token * top = secondParamTok ;
while ( top - > astParent ( ) & & top - > astParent ( ) - > str ( ) ! = " , " )
top = top - > astParent ( ) ;
2014-02-15 19:06:00 +01:00
// Check if second parameter is a float variable or a float literal != 0.0f
2014-03-22 18:42:29 +01:00
if ( portability & & astIsFloat ( top , false ) ) {
memsetFloatError ( secondParamTok , top - > expressionString ( ) ) ;
2014-02-15 19:06:00 +01:00
} else if ( warning & & secondParamTok - > isNumber ( ) ) { // Check if the second parameter is a literal and is out of range
const long long int value = MathLib : : toLongNumber ( secondParamTok - > str ( ) ) ;
if ( value < - 128 | | value > 255 )
memsetValueOutOfRangeError ( secondParamTok , secondParamTok - > str ( ) ) ;
}
}
}
}
}
void CheckOther : : memsetFloatError ( const Token * tok , const std : : string & var_value )
{
const std : : string message ( " The 2nd memset() argument ' " + var_value +
" ' is a float, its representation is implementation defined. " ) ;
const std : : string verbose ( message + " memset() is used to set each byte of a block of memory to a specific value and "
" the actual representation of a floating-point value is implementation defined. " ) ;
reportError ( tok , Severity : : portability , " memsetFloat " , message + " \n " + verbose ) ;
}
void CheckOther : : memsetValueOutOfRangeError ( const Token * tok , const std : : string & value )
{
const std : : string message ( " The 2nd memset() argument ' " + value + " ' doesn't fit into an 'unsigned char'. " ) ;
const std : : string verbose ( message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value. " ) ;
reportError ( tok , Severity : : warning , " memsetValueOutOfRange " , message + " \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 ;
2013-10-19 17:27:02 +02:00
if ( var - > isConst ( ) )
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 ( ) ;
2013-07-20 12:31:04 +02:00
if ( Token : : Match ( tok , " ; %varid% = %any% ; " , var - > declarationId ( ) ) ) {
2012-05-24 15:34:59 +02:00
tok = tok - > tokAt ( 3 ) ;
if ( ! tok - > isNumber ( ) & & tok - > type ( ) ! = Token : : eString & & tok - > type ( ) ! = Token : : eChar & & ! tok - > isBoolean ( ) )
continue ;
2013-10-19 17:27:02 +02: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 ( ) ) {
2014-08-30 11:18:31 +02:00
if ( tok - > str ( ) = = " { " & & tok - > scope ( ) ! = tok - > previous ( ) - > scope ( ) & & ! tok - > isExpandedMacro ( ) ) {
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 ( ) ;
2014-01-12 17:47:02 +01:00
// parse else if blocks..
} else if ( Token : : simpleMatch ( tok , " else { if ( " ) & & Token : : simpleMatch ( tok - > linkAt ( 3 ) , " ) { " )) {
const Token * endif = tok - > linkAt ( 3 ) - > linkAt ( 1 ) ;
bool elseif = false ;
if ( Token : : simpleMatch ( endif , " } } " ) )
elseif = true ;
else if ( Token : : simpleMatch ( endif , " } else { " ) & & Token : : simpleMatch ( endif - > linkAt ( 2 ) , " } } " ) )
elseif = true ;
2014-08-27 09:42:09 +02:00
if ( elseif & & Token : : findmatch ( tok - > next ( ) , " %varid% " , tok - > linkAt ( 1 ) , var - > declarationId ( ) ) ) {
2014-01-12 17:47:02 +01:00
reduce = false ;
2014-08-27 09:42:09 +02:00
break ;
}
2013-07-20 12:31:04 +02:00
} else if ( tok - > varId ( ) = = var - > declarationId ( ) | | tok - > str ( ) = = " goto " ) {
2013-03-03 19:35:33 +01:00
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
2014-04-17 21:32:56 +02:00
bool bFirstAssignment = 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-07-20 12:31:04 +02:00
if ( loopVariable & & noContinue & & tok - > scope ( ) = = scope & & ! forHeadEnd & & scope - > type ! = Scope : : eSwitch & & Token : : Match ( tok , " %varid% = " , var - > declarationId ( ) ) ) { // 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 ;
2013-07-20 12:31:04 +02:00
else if ( tok2 - > varId ( ) = = var - > declarationId ( ) ) {
2013-03-03 19:35:33 +01:00
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-07-20 12:31:04 +02:00
if ( loopVariable & & Token : : Match ( tok , " %varid% !!= " , var - > declarationId ( ) ) ) // Variable used in loop
2013-03-03 19:35:33 +01:00
return false ;
2009-09-29 23:56:43 +02:00
2013-07-20 12:31:04 +02:00
if ( Token : : Match ( tok , " & %varid% " , var - > declarationId ( ) ) ) // Taking address of variable
2013-03-03 19:35:33 +01:00
return false ;
2010-08-26 21:57:48 +02:00
2014-04-17 21:32:56 +02:00
if ( Token : : Match ( tok , " %varid% = %any% " , var - > declarationId ( ) ) )
bFirstAssignment = true ;
if ( ! bFirstAssignment & & Token : : Match ( tok , " * %varid% " , var - > declarationId ( ) ) ) // dereferencing means access to previous content
return false ;
2013-07-20 12:31:04 +02:00
if ( Token : : Match ( tok , " = %varid% " , var - > declarationId ( ) ) & & ( var - > isArray ( ) | | var - > isPointer ( ) ) ) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope
2013-03-03 19:35:33 +01:00
return false ;
2009-01-26 20:14:46 +01:00
2013-07-20 12:31:04 +02:00
if ( tok - > varId ( ) = = var - > declarationId ( ) ) {
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
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Comma in return statement: return a+1, b++;. (experimental)
//---------------------------------------------------------------------------
2013-06-15 17:49:10 +02:00
void CheckOther : : checkCommaSeparatedReturn ( )
{
2013-10-08 18:05:07 +02:00
// This is experimental for now. See #5076
if ( ! _settings - > experimental )
return ;
2013-06-15 17:49:10 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
return ;
2013-08-06 20:27:09 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " return " ) {
while ( tok & & tok - > str ( ) ! = " ; " ) {
if ( Token : : Match ( tok , " [([{<] " ) & & tok - > link ( ) )
tok = tok - > link ( ) ;
2013-06-18 00:13:45 +02:00
if ( ! tok - > isExpandedMacro ( ) & & tok - > str ( ) = = " , " & & tok - > linenr ( ) ! = tok - > next ( ) - > linenr ( ) )
2013-06-15 17:49:10 +02:00
commaSeparatedReturnError ( tok ) ;
2013-08-06 20:27:09 +02:00
tok = tok - > next ( ) ;
2013-06-15 17:49:10 +02:00
}
2013-07-28 10:39:58 +02:00
// bailout: missing semicolon (invalid code / bad tokenizer)
if ( ! tok )
break ;
2013-06-15 17:49:10 +02:00
}
}
}
void CheckOther : : commaSeparatedReturnError ( const Token * tok )
{
reportError ( tok ,
Severity : : style ,
" commaSeparatedReturn " ,
" Comma is used in return statement. The comma can easily be misread as a ';'. \n "
" Comma is used in return statement. When comma is used in a return statement it can "
" easily be misread as a semicolon. For example in the code below the value "
" of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is "
" returned: \n "
" if (x) \n "
" return a + 1, \n "
" b++; \n "
" However it can be useful to use comma in macros. Cppcheck does not warn when such a "
2013-06-15 19:31:17 +02:00
" macro is then used in a return statement, it is less likely such code is misunderstood. " ) ;
2013-06-15 17:49:10 +02:00
}
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 ;
2014-08-06 12:24:21 +02:00
if ( var - > scope ( ) & & var - > scope ( ) - > function - > arg - > link ( ) - > strAt ( - 1 ) = = " . " )
2014-08-06 12:06:36 +02:00
continue ; // references could not be used as va_start parameters (#5824)
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).
2014-07-18 15:51:59 +02:00
if ( Token : : Match ( tok , " std :: string|wstring !!:: " ) ) {
2012-05-24 17:40:43 +02:00
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 )
{
2013-08-07 16:27:37 +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 )
{
2013-08-07 16:27:37 +02:00
return ( isChar ( var ) & & ! var - > typeStartToken ( ) - > isUnsigned ( ) ) ;
2011-12-17 19:04:03 +01:00
}
2014-04-01 19:16:06 +02:00
static bool astIsSignedChar ( const Token * tok )
{
if ( ! tok )
return false ;
if ( tok - > str ( ) = = " * " & & tok - > astOperand1 ( ) & & ! tok - > astOperand2 ( ) ) {
const Variable * var = tok - > astOperand1 ( ) - > variable ( ) ;
if ( ! var | | ! var - > isPointer ( ) )
return false ;
2014-04-14 22:46:51 +02:00
const Token * type = var - > typeStartToken ( ) ;
2014-04-01 19:16:06 +02:00
while ( type & & type - > str ( ) = = " const " )
type = type - > next ( ) ;
return ( type & & type - > str ( ) = = " char " & & ! type - > isUnsigned ( ) ) ;
}
return isSignedChar ( tok - > variable ( ) ) ;
}
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 ( ) ) {
2014-08-04 11:20:48 +02:00
if ( Token : : Match ( tok , " %var% [ " ) & & astIsSignedChar ( tok - > next ( ) - > astOperand2 ( ) ) ) {
const Variable * arrayvar = tok - > variable ( ) ;
2012-11-15 07:11:45 +01:00
const MathLib : : bigint arraysize = ( arrayvar & & arrayvar - > isArray ( ) ) ? arrayvar - > dimension ( 0U ) : 0 ;
2014-04-03 06:27:29 +02:00
if ( arraysize > 0x80 )
2014-08-04 11:20:48 +02:00
charArrayIndexError ( tok ) ;
2012-11-15 07:11:45 +01:00
}
2014-04-01 19:16:06 +02:00
else if ( Token : : Match ( tok , " [&|^] " ) ) {
const Token * tok2 ;
if ( tok - > astOperand1 ( ) & & astIsSignedChar ( tok - > astOperand1 ( ) ) )
tok2 = tok - > astOperand2 ( ) ;
else if ( tok - > astOperand2 ( ) & & astIsSignedChar ( tok - > astOperand2 ( ) ) )
tok2 = tok - > astOperand1 ( ) ;
else
2012-11-15 07:11:45 +01:00
continue ;
2011-08-14 16:39:45 +02:00
2014-05-24 11:28:43 +02:00
// Don't care about address-of operator
if ( ! tok - > astOperand2 ( ) )
2014-05-13 15:53:31 +02:00
continue ;
2012-11-15 07:11:45 +01:00
// it's ok with a bitwise and where the other operand is 0xff or less..
2014-04-01 19:16:06 +02:00
if ( tok - > str ( ) = = " & " & & tok2 & & tok2 - > isNumber ( ) & & MathLib : : isGreater ( " 0x100 " , tok2 - > str ( ) ) )
2012-11-15 07:11:45 +01:00
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?
2014-04-01 19:16:06 +02:00
if ( tok - > astParent ( ) & & tok - > astParent ( ) - > str ( ) = = " = " ) {
const Token * eq = tok - > astParent ( ) ;
const Token * lhs = eq - > astOperand1 ( ) ;
if ( lhs & & lhs - > str ( ) = = " * " & & ! lhs - > astOperand2 ( ) )
lhs = lhs - > astOperand1 ( ) ;
2014-08-17 20:35:24 +02:00
while ( lhs & & ( lhs - > str ( ) = = " . " | | lhs - > str ( ) = = " :: " ) )
2014-04-01 19:16:06 +02:00
lhs = lhs - > astOperand2 ( ) ;
if ( ! lhs | | ! lhs - > isName ( ) )
continue ;
const Variable * var = lhs - > variable ( ) ;
2014-08-09 11:44:55 +02:00
if ( var & & var - > isIntegralType ( ) & & var - > typeStartToken ( ) - > str ( ) ! = " char " )
2014-04-01 19:16:06 +02:00
charBitOpError ( tok ) ; // This is an error..
}
2012-11-15 07:11:45 +01:00
}
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
2014-02-26 06:18:52 +01:00
// C++11 struct/array/etc initialization in initializer list
else if ( Token : : Match ( tok - > previous ( ) , " %var% { " ) && !Token::findsimplematch(tok, " ; " ,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 ) ;
2013-10-05 12:26:09 +02:00
// C++11 initialize set in initalizer list : [,:] std::set<int>{1} [{,]
2014-03-14 16:26:37 +01:00
else if ( Token : : simpleMatch ( tok , " > { " ) & & tok - > link ( ) )
2013-10-05 12:26:09 +02:00
tok = tok - > next ( ) - > link ( ) ;
2014-04-23 16:23:19 +02:00
else if ( Token : : Match ( tok , " [;{}] %str%|%num% " ) ) {
2011-11-05 20:28:52 +01:00
// 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
2014-04-23 16:23:19 +02:00
// no warning if this is the last statement in a ({})
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ( " )
2014-04-23 19:49:16 +02:00
tok2 = tok2 - > link ( ) ;
2014-04-23 16:23:19 +02:00
else if ( Token : : Match ( tok2 , " [;{}] " ) ) {
bailout = Token : : simpleMatch ( tok2 , " ; } ) " ) ;
break ;
}
}
if ( bailout )
continue ;
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
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Detect division by zero.
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 ( ) ) {
2014-01-20 06:31:13 +01:00
if ( Token : : Match ( tok , " div|ldiv|lldiv|imaxdiv ( %num% , %num% ) " ) & &
MathLib : : isInt ( tok - > strAt ( 4 ) ) & &
MathLib : : toLongNumber ( tok - > strAt ( 4 ) ) = = 0L ) {
2013-09-29 11:13:49 +02:00
if ( tok - > str ( ) = = " div " ) {
if ( tok - > strAt ( - 1 ) = = " . " )
continue ;
if ( tok - > variable ( ) | | tok - > function ( ) )
continue ;
}
2014-03-20 16:12:58 +01:00
zerodivError ( tok , false ) ;
2014-05-28 21:03:16 +02:00
} else if ( Token : : Match ( tok , " [/%] " ) & & tok - > astOperand2 ( ) ) {
2014-01-04 20:57:02 +01:00
// Value flow..
2014-01-20 06:49:45 +01:00
const ValueFlow : : Value * value = tok - > astOperand2 ( ) - > getValue ( 0LL ) ;
if ( value ) {
2014-03-20 16:12:58 +01:00
if ( ! _settings - > inconclusive & & value - > inconclusive )
continue ;
2014-02-15 08:46:28 +01:00
if ( value - > condition = = nullptr )
2014-03-20 16:12:58 +01:00
zerodivError ( tok , value - > inconclusive ) ;
2014-01-20 06:49:45 +01:00
else if ( _settings - > isEnabled ( " warning " ) )
2014-03-20 16:12:58 +01:00
zerodivcondError ( value - > condition , tok , value - > inconclusive ) ;
2014-01-04 20:57:02 +01:00
}
2009-08-23 05:34:19 +02:00
}
2009-03-28 07:49:47 +01:00
}
}
2014-03-20 16:12:58 +01:00
void CheckOther : : zerodivError ( const Token * tok , bool inconclusive )
2011-08-19 17:53:43 +02:00
{
2014-03-20 16:12:58 +01:00
reportError ( tok , Severity : : error , " zerodiv " , " Division by zero. " , inconclusive ) ;
2011-08-19 17:53:43 +02:00
}
2009-03-28 07:49:47 +01:00
2014-03-20 16:12:58 +01:00
void CheckOther : : zerodivcondError ( const Token * tokcond , const Token * tokdiv , bool inconclusive )
2013-09-07 07:40:10 +02:00
{
std : : list < const Token * > callstack ;
while ( Token : : Match ( tokcond , " (|%oror%|&& " ) )
tokcond = tokcond - > next ( ) ;
if ( tokcond & & tokdiv ) {
callstack . push_back ( tokcond ) ;
callstack . push_back ( tokdiv ) ;
}
std : : string condition ;
2014-01-06 10:51:17 +01:00
if ( ! tokcond ) {
// getErrorMessages
} else if ( Token : : Match ( tokcond , " %num% <|<= " ) ) {
2013-09-07 07:40:10 +02:00
condition = tokcond - > strAt ( 2 ) + ( ( tokcond - > strAt ( 1 ) = = " < " ) ? " > " : " >= " ) + tokcond - > str ( ) ;
2014-01-06 10:51:17 +01:00
} else if ( tokcond - > isComparisonOp ( ) ) {
2014-01-17 18:37:49 +01:00
condition = tokcond - > expressionString ( ) ;
2014-01-06 10:51:17 +01:00
} else {
2013-09-07 07:40:10 +02:00
if ( tokcond - > str ( ) = = " ! " )
condition = tokcond - > next ( ) - > str ( ) + " ==0 " ;
else
condition = tokcond - > str ( ) + " !=0 " ;
}
2013-09-21 18:48:48 +02:00
const std : : string linenr ( MathLib : : toString ( tokdiv ? tokdiv - > linenr ( ) : 0 ) ) ;
2014-03-20 16:12:58 +01:00
reportError ( callstack , Severity : : warning , " zerodivcond " , " Either the condition ' " + condition + " ' is useless or there is division by zero at line " + linenr + " . " , inconclusive ) ;
2013-09-07 07:40:10 +02:00
}
2014-08-27 09:42:09 +02:00
2013-06-10 08:13:08 +02:00
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Check for NaN (not-a-number) in an arithmetic expression, e.g.
// double d = 1.0 / 0.0 + 100.0;
2013-06-10 08:13:08 +02:00
//---------------------------------------------------------------------------
void CheckOther : : checkNanInArithmeticExpression ( )
{
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " inf.0 +|- " ) | |
Token : : Match ( tok , " +|- inf.0 " ) | |
Token : : Match ( tok , " +|- %num% / 0.0 " ) ) {
nanInArithmeticExpressionError ( tok ) ;
}
}
}
void CheckOther : : nanInArithmeticExpressionError ( const Token * tok )
{
reportError ( tok , Severity : : style , " nanInArithmeticExpression " ,
" Using NaN/Inf in a computation. \n "
2013-06-11 08:11:44 +02:00
" Using NaN/Inf in a computation. "
2013-06-10 08:13:08 +02:00
" Although nothing bad really happens, it is suspicious. " ) ;
}
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Detect passing wrong values to <cmath> functions like atan(0, x);
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 ;
2014-05-18 17:05:08 +02:00
if ( tok - > strAt ( - 1 ) ! = " . "
2014-04-23 01:23:38 +02:00
& & Token : : Match ( tok , " log|logf|logl|log10|log10f|log10l ( %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
2013-09-13 19:49:25 +02:00
else if ( Token : : Match ( tok , " acos|acosl|acosf|asin|asinf|asinl ( %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
2013-09-13 19:49:25 +02:00
else if ( Token : : Match ( tok , " atan2|atan2f|atan2l ( %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).
2013-09-13 19:49:25 +02:00
else if ( Token : : Match ( tok , " fmod|fmodf|fmodl ( %any% " ) ) {
2012-01-28 12:32:28 +01:00
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
2013-09-13 19:49:25 +02:00
else if ( Token : : Match ( tok , " pow|powf|powl ( %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
}
2014-08-27 09:42:09 +02:00
2011-08-19 17:53:43 +02:00
//---------------------------------------------------------------------------
2014-08-27 09:42:09 +02:00
// Creating instance of clases which are destroyed immediately
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% ( " )
2013-10-22 19:37:11 +02:00
& & Token : : Match ( tok - > linkAt ( 2 ) , " ) ; !!} " )
2011-01-16 19:57:29 +01:00
& & symbolDatabase - > isClassOrStruct ( tok - > next ( ) - > str ( ) )
2013-07-22 07:25:53 +02:00
& & ( ! tok - > next ( ) - > function ( ) | | // is not a function on this scope
( tok - > next ( ) - > function ( ) & & tok - > next ( ) - > function ( ) - > isConstructor ( ) ) ) ) { // or is function in this scope and it's a ctor
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-04-09 21:14:01 +02:00
//-----------------------------------------------------------------------------
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 ( )
{
2014-02-16 11:04:27 +01:00
// This is inconclusive since in practice most warnings are noise:
// * There can be unfixed low-priority todos. The code is fine as it
// is but it could be possible to enhance it. Writing a warning
// here is noise since the code is fine (see cppcheck, abiword, ..)
// * There can be overspecified code so some conditions can't be true
// and their conditional code is a duplicate of the condition that
// is always true just in case it would be false. See for instance
// abiword.
if ( ! _settings - > isEnabled ( " style " ) | | ! _settings - > inconclusive )
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 ) {
2014-07-02 16:16:19 +02:00
if ( scope - > type ! = Scope : : eIf )
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
2014-09-01 10:19:14 +02:00
if ( ! branch1 . empty ( ) & & 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 "
2014-02-16 11:04:27 +01:00
" carefully to determine if it is correct. " , true ) ;
2012-09-16 19:44:02 +02:00
}
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.
2014-04-12 23:28:13 +02:00
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% " ) ) {
2014-07-11 09:33:09 +02:00
const int varIndex = tok - > strAt ( 1 ) = = " ( " ? 2 :
2014-07-14 14:38:40 +02:00
tok - > strAt ( 3 ) = = " ( " ? 4 : 1 ;
2014-07-11 09:33:09 +02:00
const unsigned int var1 = tok - > tokAt ( varIndex ) - > varId ( ) ;
const unsigned int var2 = tok - > tokAt ( varIndex + 2 ) - > varId ( ) ;
2012-11-06 06:02:51 +01:00
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 ) ) ;
2014-02-15 08:46:28 +01:00
while ( tok2 ! = nullptr ) {
2012-09-05 08:31:23 +02:00
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% ) " ) ) {
2014-07-11 09:33:09 +02:00
const unsigned int varId = tok - > tokAt ( 2 ) - > varId ( ) ;
if ( varId ) {
2012-01-15 01:19:34 +01:00
if ( Token : : Match ( tok , " free|g_free " ) ) {
2014-07-11 09:33:09 +02:00
if ( freedVariables . find ( varId ) ! = freedVariables . end ( ) )
2012-09-07 12:36:40 +02:00
doubleFreeError ( tok , tok - > strAt ( 2 ) ) ;
2012-01-15 01:19:34 +01:00
else
2014-07-11 09:33:09 +02:00
freedVariables . insert ( varId ) ;
2012-01-15 01:19:34 +01:00
} else if ( tok - > str ( ) = = " closedir " ) {
2014-07-11 09:33:09 +02:00
if ( closeDirVariables . find ( varId ) ! = closeDirVariables . end ( ) )
2012-09-07 12:36:40 +02:00
doubleCloseDirError ( tok , tok - > strAt ( 2 ) ) ;
2012-01-15 01:19:34 +01:00
else
2014-07-11 09:33:09 +02:00
closeDirVariables . insert ( varId ) ;
2012-01-15 01:19:34 +01:00
}
}
}
// 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% ; " ) ) {
2014-07-11 09:33:09 +02:00
const int varIndex = ( tok - > strAt ( 1 ) = = " [ " ) ? 3 : 1 ;
const unsigned int varId = tok - > tokAt ( varIndex ) - > varId ( ) ;
if ( varId ) {
if ( freedVariables . find ( varId ) ! = freedVariables . end ( ) )
doubleFreeError ( tok , tok - > strAt ( varIndex ) ) ;
2012-01-15 01:19:34 +01:00
else
2014-07-11 09:33:09 +02:00
freedVariables . insert ( varId ) ;
2012-01-15 01:19:34 +01:00
}
}
// 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 " ) & &
2014-02-15 08:46:28 +01:00
Token : : findmatch ( tok - > link ( ) - > linkAt ( - 1 ) , " break|continue ; " , tok ) ! = nullptr ) {
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 ( ) ) {
2014-07-11 09:33:09 +02:00
const unsigned int varId = tok2 - > varId ( ) ;
if ( varId ) {
freedVariables . erase ( varId ) ;
closeDirVariables . erase ( varId ) ;
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% = " ) ) {
2014-07-11 09:33:09 +02:00
const unsigned int varId = tok - > varId ( ) ;
if ( varId ) {
freedVariables . erase ( varId ) ;
closeDirVariables . erase ( varId ) ;
2012-01-15 01:19:34 +01:00
}
}
// 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. " ) ;
}
2014-08-27 09:42:09 +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)
//---------------------------------------------------------------------------
2011-11-08 21:22:31 +01:00
namespace {
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
}
2014-05-21 18:19:42 +02:00
static bool isWithoutSideEffects ( const Tokenizer * tokenizer , const Token * tok )
{
if ( ! tokenizer - > isCPP ( ) )
return true ;
while ( tok & & tok - > astOperand2 ( ) & & tok - > astOperand2 ( ) - > str ( ) ! = " ( " )
tok = tok - > astOperand2 ( ) ;
if ( tok - > varId ( ) ) {
const Variable * var = tok - > variable ( ) ;
return var & & ( ! var - > isClass ( ) | | var - > isPointer ( ) | | var - > isStlType ( ) ) ;
}
return true ;
}
2011-04-10 16:25:02 +02:00
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 ;
2013-12-09 18:06:19 +01:00
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2014-05-21 18:19:42 +02:00
if ( tok - > isOp ( ) & & tok - > astOperand1 ( ) & & ! Token : : Match ( tok , " +|*|<<|>> " ) ) {
2014-03-22 18:42:29 +01:00
if ( Token : : Match ( tok , " ==|!=|- " ) & & astIsFloat ( tok - > astOperand1 ( ) , true ) )
2012-05-17 07:26:57 +02:00
continue ;
2014-05-21 18:19:42 +02:00
if ( isSameExpression ( tok - > astOperand1 ( ) , tok - > astOperand2 ( ) , _settings - > library . functionpure ) ) {
if ( isWithoutSideEffects ( _tokenizer , tok - > astOperand1 ( ) ) ) {
2014-07-10 09:23:14 +02:00
const bool assignment = tok - > str ( ) = = " = " ;
2014-06-28 15:26:22 +02:00
if ( assignment )
selfAssignmentError ( tok , tok - > astOperand1 ( ) - > expressionString ( ) ) ;
else
2014-05-21 18:19:42 +02:00
duplicateExpressionError ( tok , tok , tok - > str ( ) ) ;
}
} else if ( ! Token : : Match ( tok , " [-/%] " ) ) { // These operators are not associative
if ( tok - > astOperand2 ( ) & & tok - > str ( ) = = tok - > astOperand1 ( ) - > str ( ) & & isSameExpression ( tok - > astOperand2 ( ) , tok - > astOperand1 ( ) - > astOperand2 ( ) , _settings - > library . functionpure ) & & isWithoutSideEffects ( _tokenizer , tok - > astOperand2 ( ) ) )
2014-03-17 18:35:36 +01:00
duplicateExpressionError ( tok - > astOperand2 ( ) , tok - > astOperand2 ( ) , tok - > str ( ) ) ;
else if ( tok - > astOperand2 ( ) ) {
const Token * ast1 = tok - > astOperand1 ( ) ;
while ( ast1 & & tok - > str ( ) = = ast1 - > str ( ) ) {
2014-05-21 18:19:42 +02:00
if ( isSameExpression ( ast1 - > astOperand1 ( ) , tok - > astOperand2 ( ) , _settings - > library . functionpure ) & & isWithoutSideEffects ( _tokenizer , ast1 - > astOperand1 ( ) ) )
2014-04-16 16:04:46 +02:00
// TODO: warn if variables are unchanged. See #5683
// Probably the message should be changed to 'duplicate expressions X in condition or something like that'.
; //duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok->str());
2014-05-21 18:19:42 +02:00
else if ( isSameExpression ( ast1 - > astOperand2 ( ) , tok - > astOperand2 ( ) , _settings - > library . functionpure ) & & isWithoutSideEffects ( _tokenizer , ast1 - > astOperand2 ( ) ) )
2014-03-17 18:35:36 +01:00
duplicateExpressionError ( ast1 - > astOperand2 ( ) , tok - > astOperand2 ( ) , tok - > str ( ) ) ;
if ( ! isConstExpression ( ast1 - > astOperand2 ( ) , _settings - > library . functionpure ) )
break ;
ast1 = ast1 - > astOperand1 ( ) ;
}
2013-12-24 07:21:46 +01:00
}
}
2011-04-10 16:25:02 +02:00
}
}
}
}
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. " ) ;
}
2014-05-21 18:19:42 +02:00
void CheckOther : : selfAssignmentError ( const Token * tok , const std : : string & varname )
{
reportError ( tok , Severity : : warning ,
" selfAssignment " , " Redundant assignment of ' " + varname + " ' to itself. " ) ;
}
2013-09-26 07:07:48 +02:00
//-----------------------------------------------------------------------------
2013-11-09 16:07:28 +01:00
// Check is a comparison of two variables leads to condition, which is
// always true or false.
2013-09-26 07:07:48 +02:00
// For instance: int a = 1; if(isless(a,a)){...}
2013-11-09 16:07:28 +01:00
// In this case isless(a,a) evaluates always to false.
2013-09-26 07:07:48 +02:00
//
// Reference:
// - http://www.cplusplus.com/reference/cmath/
//-----------------------------------------------------------------------------
2014-04-12 23:28:13 +02:00
void CheckOther : : checkComparisonFunctionIsAlwaysTrueOrFalse ( )
2013-09-26 07:07:48 +02:00
{
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 ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > isName ( ) & & Token : : Match ( tok , " isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% ) " ) ) {
const unsigned int varidLeft = tok - > tokAt ( 2 ) - > varId ( ) ; // get the left varid
const unsigned int varidRight = tok - > tokAt ( 4 ) - > varId ( ) ; // get the right varid
// compare varids: if they are not zero but equal
// --> the comparison function is calles with the same variables
2013-09-29 13:33:46 +02:00
if ( varidLeft ! = 0 & & varidLeft = = varidRight ) {
2014-07-10 09:23:14 +02:00
const std : : string & functionName = tok - > str ( ) ; // store function name
const std : : string & varNameLeft = tok - > strAt ( 2 ) ; // get the left variable name
2013-09-26 07:07:48 +02:00
if ( functionName = = " isgreater " | | functionName = = " isless " | | functionName = = " islessgreater " ) {
// e.g.: isgreater(x,x) --> (x)>(x) --> false
2014-08-27 09:42:09 +02:00
checkComparisonFunctionIsAlwaysTrueOrFalseError ( tok , functionName , varNameLeft , false ) ;
2013-09-26 07:07:48 +02:00
} else { // functionName == "isgreaterequal" || functionName == "islessequal"
// e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true
2014-08-27 09:42:09 +02:00
checkComparisonFunctionIsAlwaysTrueOrFalseError ( tok , functionName , varNameLeft , true ) ;
2013-09-26 07:07:48 +02:00
}
}
}
}
}
}
void CheckOther : : checkComparisonFunctionIsAlwaysTrueOrFalseError ( const Token * tok , const std : : string & functionName , const std : : string & varName , const bool result )
{
const std : : string strResult = result ? " true " : " false " ;
reportError ( tok , Severity : : warning , " comparisonFunctionIsAlwaysTrueOrFalse " ,
2014-08-27 09:42:09 +02:00
" Comparison of two identical variables with " + functionName + " ( " + varName + " , " + varName + " ) evaluates always to " + strResult + " . \n "
" The function " + functionName + " is designed to compare two variables. Calling this function with one variable ( " + varName + " ) "
" for both parameters leads to a statement which is always " + strResult + " . " ) ;
2013-09-26 07:07:48 +02:00
}
2012-08-24 14:25:17 +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 ( ) ) {
2013-10-06 16:52:27 +02:00
if ( Token : : Match ( tok , " %var% <|<= 0 " ) & & tok - > varId ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " +|- " ) ) {
// TODO: handle a[10].b , a::b , (unsigned int)x , etc
const Token * prev = tok - > previous ( ) ;
while ( prev & & ( prev - > isName ( ) | | prev - > str ( ) = = " . " ) )
prev = prev - > previous ( ) ;
if ( ! Token : : Match ( prev , " (|&&|%oror% " ) )
continue ;
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 ) ;
2013-10-06 16:52:27 +02:00
else if ( var & & ( var - > isPointer ( ) | | var - > isArray ( ) ) )
2012-08-21 12:28:02 +02:00
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 ) ;
2013-07-23 17:19:20 +02:00
else if ( var & & var - > isPointer ( ) & & ! Token : : Match ( tok - > tokAt ( 3 ) , " [.[(] " ) )
2012-08-21 12:28:02 +02:00
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 ) ;
}
2013-12-14 08:46:18 +01:00
/* check if a constructor in given class scope takes a reference */
static bool constructorTakesReference ( const Scope * const classScope )
{
for ( std : : list < Function > : : const_iterator func = classScope - > functionList . begin ( ) ; func ! = classScope - > functionList . end ( ) ; + + func ) {
if ( func - > isConstructor ( ) ) {
const Function & constructor = * func ;
for ( std : : size_t argnr = 0U ; argnr < constructor . argCount ( ) ; argnr + + ) {
2014-05-23 18:11:44 +02:00
const Variable * const argVar = constructor . getArgumentVar ( argnr ) ;
if ( argVar & & argVar - > isReference ( ) ) {
2013-12-14 08:46:18 +01:00
return true ;
2014-05-23 18:11:44 +02:00
}
2013-12-14 08:46:18 +01:00
}
}
}
return false ;
}
2014-08-27 09:42:09 +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.
//---------------------------------------------------------------------------
2012-07-22 09:17:00 +02:00
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
2014-03-27 11:15:17 +01:00
if ( ! var | | var - > isReference ( ) | | ! var - > isConst ( ) | | var - > isPointer ( ) | | ( ! var - > type ( ) & & ! var - > isStlType ( ) ) ) // 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 ( ) ;
if ( startTok - > strAt ( 1 ) = = " = " ) // %type% %var% = ... ;
2013-12-14 08:46:18 +01:00
;
else if ( startTok - > strAt ( 1 ) = = " ( " & & var - > isClass ( ) & & var - > typeScope ( ) ) {
// Object is instantiated. Warn if constructor takes arguments by value.
if ( constructorTakesReference ( var - > typeScope ( ) ) )
continue ;
} else
2012-08-24 00:03:22 +02:00
continue ;
2013-03-05 15:53:09 +01:00
2014-05-18 12:52:15 +02:00
const Token * tok = startTok - > next ( ) - > astOperand2 ( ) ;
2014-05-29 02:58:18 +02:00
if ( ! tok )
continue ;
2014-05-18 12:52:15 +02:00
if ( ! Token : : Match ( tok - > previous ( ) , " %var% ( " ) )
2013-03-05 15:53:09 +01:00
continue ;
2014-05-18 12:52:15 +02:00
if ( ! Token : : Match ( tok - > link ( ) , " ) )| ; " ) ) // bailout for usage like "const A a = getA()+3"
2013-03-05 15:53:09 +01:00
continue ;
2014-05-18 12:52:15 +02:00
const Function * func = tok - > previous ( ) - > 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 "
2014-05-02 20:09:38 +02:00
" the unnecessary data copying by converting ' " + varname + " ' to const reference. " ,
true ) ; // since #5618 that check became inconlusive
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 ( ) ) {
2014-03-29 13:01:30 +01:00
if ( tok - > str ( ) ! = " << " & & tok - > str ( ) ! = " >> " )
continue ;
2012-11-16 06:50:49 +01:00
2014-03-29 13:01:30 +01:00
if ( ! tok - > astOperand1 ( ) | | ! tok - > astOperand2 ( ) )
continue ;
// don't warn if lhs is a class. this is an overloaded operator then
if ( _tokenizer - > isCPP ( ) ) {
const Token * rhs = tok - > astOperand1 ( ) ;
while ( Token : : Match ( rhs , " ::|. " ) )
rhs = rhs - > astOperand2 ( ) ;
if ( ! rhs )
continue ;
if ( ! rhs - > isNumber ( ) & & ! rhs - > variable ( ) )
continue ;
2014-03-29 21:01:21 +01:00
if ( rhs - > variable ( ) & &
( ! rhs - > variable ( ) - > typeStartToken ( ) | | ! rhs - > variable ( ) - > typeStartToken ( ) - > isStandardType ( ) ) )
2014-03-29 13:01:30 +01:00
continue ;
}
// Get negative rhs value. preferably a value which doesn't have 'condition'.
2014-04-02 06:49:28 +02:00
const ValueFlow : : Value * value = tok - > astOperand2 ( ) - > getValueLE ( - 1LL , _settings ) ;
if ( value )
negativeBitwiseShiftError ( tok ) ;
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
{
2014-03-29 13:01:30 +01:00
reportError ( tok , Severity : : error , " shiftNegative " , " Shifting by a negative value is undefined behaviour " ) ;
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
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Detect NULL being passed to variadic function.
//---------------------------------------------------------------------------
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 ( ) ;
}
2014-02-15 08:46:28 +01:00
ftok = ftok ? ftok - > previous ( ) : nullptr ;
2013-01-13 12:02:10 +01:00
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 ;
2014-02-15 08:46:28 +01:00
tok2 = tok2 ? tok2 - > link ( ) : nullptr ; // goto ')'
2013-01-13 12:02:10 +01:00
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 "
" } " ) ;
}