2010-01-16 08:47:46 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2016-01-01 14:34:45 +01:00
* Copyright ( C ) 2007 - 2016 Cppcheck team .
2010-01-16 08:47: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
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
# include "checkclass.h"
# include "tokenize.h"
# include "token.h"
# include "errorlogger.h"
2010-11-13 07:31:56 +01:00
# include "symboldatabase.h"
2015-11-29 10:49:10 +01:00
# include "utils.h"
2010-01-16 08:47:46 +01:00
# include <string>
# include <algorithm>
//---------------------------------------------------------------------------
// Register CheckClass..
2011-10-13 20:53:06 +02:00
namespace {
CheckClass instance ;
2016-01-25 20:01:48 +01:00
}
2013-03-30 15:09:22 +01:00
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
static const CWE CWE398 ( 398U ) ;
2016-01-25 20:01:48 +01:00
static const CWE CWE404 ( 404U ) ;
static const CWE CWE665 ( 665U ) ;
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
static const CWE CWE758 ( 758U ) ;
2016-01-25 20:01:48 +01:00
static const CWE CWE762 ( 762U ) ;
static const char * getFunctionTypeName (
Function : : Type type )
{
switch ( type ) {
case Function : : eConstructor :
return " constructor " ;
case Function : : eCopyConstructor :
return " copy constructor " ;
case Function : : eMoveConstructor :
return " move constructor " ;
case Function : : eDestructor :
return " destructor " ;
case Function : : eFunction :
return " function " ;
case Function : : eOperatorEqual :
return " operator= " ;
2013-03-30 15:09:22 +01:00
}
2016-01-25 20:01:48 +01:00
return " " ;
}
2013-03-30 15:09:22 +01:00
2016-01-25 20:01:48 +01:00
static bool isPureWithoutBody ( Function const & func )
{
return func . isPure ( ) & & ! func . hasBody ( ) ;
2010-01-16 08:47:46 +01:00
}
2010-07-26 16:46:37 +02:00
//---------------------------------------------------------------------------
CheckClass : : CheckClass ( const Tokenizer * tokenizer , const Settings * settings , ErrorLogger * errorLogger )
2011-02-02 10:29:10 +01:00
: Check ( myName ( ) , tokenizer , settings , errorLogger ) ,
2014-02-16 10:32:10 +01:00
symbolDatabase ( tokenizer ? tokenizer - > getSymbolDatabase ( ) : nullptr )
2010-07-26 16:46:37 +02:00
{
2010-08-07 12:41:11 +02:00
}
2010-07-26 16:46:37 +02:00
//---------------------------------------------------------------------------
// ClassCheck: Check that all class constructors are ok.
//---------------------------------------------------------------------------
2010-07-15 10:16:16 +02:00
2010-01-16 08:47:46 +01:00
void CheckClass : : constructors ( )
{
2015-04-10 14:18:52 +02:00
const bool printStyle = _settings - > isEnabled ( " style " ) ;
const bool printWarnings = _settings - > isEnabled ( " warning " ) ;
if ( ! printStyle & & ! printWarnings )
2010-04-21 08:38:25 +02:00
return ;
2015-04-10 14:18:52 +02:00
const bool printInconclusive = _settings - > inconclusive ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2010-08-09 17:50:26 +02:00
2010-07-26 16:46:37 +02:00
// There are no constructors.
2015-04-10 14:18:52 +02:00
if ( scope - > numConstructors = = 0 & & printStyle ) {
2010-07-26 16:46:37 +02:00
// If there is a private variable, there should be a constructor..
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var ) {
2014-03-24 06:15:51 +01:00
if ( var - > isPrivate ( ) & & ! var - > isStatic ( ) & & ! Token : : Match ( var - > nameToken ( ) , " %varid% ; %varid% = " , var - > declarationId ( ) ) & &
2013-03-05 13:33:38 +01:00
( ! var - > isClass ( ) | | ( var - > type ( ) & & var - > type ( ) - > needInitialization = = Type : : True ) ) ) {
2011-01-17 18:29:19 +01:00
noConstructorError ( scope - > classDef , scope - > className , scope - > classDef - > str ( ) = = " struct " ) ;
2010-07-26 16:46:37 +02:00
break ;
2010-01-16 08:47:46 +01:00
}
}
2010-07-26 16:46:37 +02:00
}
2010-01-16 08:47:46 +01:00
2015-04-10 14:18:52 +02:00
if ( ! printWarnings )
2013-03-03 11:41:59 +01:00
continue ;
2011-11-22 19:26:00 +01:00
// #3196 => bailout if there are nested unions
// TODO: handle union variables better
{
bool bailout = false ;
for ( std : : list < Scope * > : : const_iterator it = scope - > nestedList . begin ( ) ; it ! = scope - > nestedList . end ( ) ; + + it ) {
const Scope * const nestedScope = * it ;
if ( nestedScope - > type = = Scope : : eUnion ) {
bailout = true ;
break ;
}
}
if ( bailout )
continue ;
}
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
std : : vector < Usage > usage ( scope - > varlist . size ( ) ) ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( ! func - > hasBody ( ) | | ! ( func - > isConstructor ( ) | |
func - > type = = Function : : eOperatorEqual ) )
2010-07-26 16:46:37 +02:00
continue ;
// Mark all variables not used
2011-01-16 16:37:11 +01:00
clearAllVar ( usage ) ;
2010-07-14 19:00:52 +02:00
2013-01-01 09:53:40 +01:00
std : : list < const Function * > callstack ;
2015-01-17 16:28:39 +01:00
initializeVarList ( * func , callstack , scope , usage ) ;
2010-07-14 19:00:52 +02:00
2010-07-26 16:46:37 +02:00
// Check if any variables are uninitialized
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-01-16 16:37:11 +01:00
unsigned int count = 0 ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var , + + count ) {
2013-02-18 06:33:53 +01:00
// check for C++11 initializer
2014-07-08 10:28:57 +02:00
if ( var - > hasDefault ( ) ) {
2013-02-18 06:33:53 +01:00
usage [ count ] . init = true ;
2014-07-08 10:28:57 +02:00
continue ;
}
2013-02-18 06:33:53 +01:00
2011-01-18 07:32:06 +01:00
if ( usage [ count ] . assign | | usage [ count ] . init | | var - > isStatic ( ) )
2010-07-26 16:46:37 +02:00
continue ;
2010-07-15 10:16:16 +02:00
2015-01-08 05:45:31 +01:00
if ( var - > isConst ( ) & & func - > isOperator ( ) ) // We can't set const members in assignment operator
2010-11-16 07:30:55 +01:00
continue ;
2010-12-02 07:35:01 +01:00
// Check if this is a class constructor
2015-08-28 23:06:39 +02:00
if ( ! var - > isPointer ( ) & & ! var - > isPointerArray ( ) & & var - > isClass ( ) & & func - > type = = Function : : eConstructor ) {
2010-12-02 07:35:01 +01:00
// Unknown type so assume it is initialized
2011-01-18 07:32:06 +01:00
if ( ! var - > type ( ) )
2010-12-02 07:35:01 +01:00
continue ;
// Known type that doesn't need initialization or
// known type that has member variables of an unknown type
2013-03-05 13:33:38 +01:00
else if ( var - > type ( ) - > needInitialization ! = Type : : True )
2010-12-02 07:35:01 +01:00
continue ;
}
2011-01-18 07:34:11 +01:00
// Check if type can't be copied
2015-08-29 13:11:00 +02:00
if ( ! var - > isPointer ( ) & & ! var - > isPointerArray ( ) & & var - > typeScope ( ) ) {
2013-04-10 21:57:22 +02:00
if ( func - > type = = Function : : eMoveConstructor ) {
if ( canNotMove ( var - > typeScope ( ) ) )
continue ;
} else {
if ( canNotCopy ( var - > typeScope ( ) ) )
continue ;
}
}
2011-01-18 07:34:11 +01:00
2014-06-09 11:35:30 +02:00
bool inconclusive = false ;
2012-09-20 16:47:01 +02:00
// Don't warn about unknown types in copy constructors since we
// don't know if they can be copied or not..
2012-09-20 19:16:26 +02:00
if ( ! var - > isPointer ( ) & &
2013-03-05 13:33:38 +01:00
! ( var - > type ( ) & & var - > type ( ) - > needInitialization ! = Type : : True ) & &
2012-09-20 19:16:26 +02:00
( func - > type = = Function : : eCopyConstructor | | func - > type = = Function : : eOperatorEqual ) ) {
2014-08-19 11:55:00 +02:00
if ( ! var - > typeStartToken ( ) - > isStandardType ( ) ) {
2015-04-10 14:18:52 +02:00
if ( printInconclusive )
2012-09-20 19:16:26 +02:00
inconclusive = true ;
else
continue ;
}
2012-09-20 16:47:01 +02:00
}
2010-07-26 16:46:37 +02:00
// It's non-static and it's not initialized => error
2011-10-13 20:53:06 +02:00
if ( func - > type = = Function : : eOperatorEqual ) {
2012-05-22 21:58:46 +02:00
const Token * operStart = func - > arg ;
2010-07-26 16:46:37 +02:00
bool classNameUsed = false ;
2011-10-13 20:53:06 +02:00
for ( const Token * operTok = operStart ; operTok ! = operStart - > link ( ) ; operTok = operTok - > next ( ) ) {
if ( operTok - > str ( ) = = scope - > className ) {
2010-07-26 16:46:37 +02:00
classNameUsed = true ;
break ;
2010-07-14 19:00:52 +02:00
}
2010-07-15 10:16:16 +02:00
}
2010-01-16 08:47:46 +01:00
2010-07-26 16:46:37 +02:00
if ( classNameUsed )
2012-09-20 19:16:26 +02:00
operatorEqVarError ( func - > token , scope - > className , var - > name ( ) , inconclusive ) ;
2012-01-08 12:17:55 +01:00
} else if ( func - > access ! = Private ) {
2013-03-05 13:33:38 +01:00
const Scope * varType = var - > typeScope ( ) ;
2013-03-09 09:20:48 +01:00
if ( ! varType | | varType - > type ! = Scope : : eUnion ) {
if ( func - > type = = Function : : eConstructor & &
2013-04-10 21:57:22 +02:00
func - > nestedIn & & ( func - > nestedIn - > numConstructors - func - > nestedIn - > numCopyOrMoveConstructors ) > 1 & &
2013-03-09 09:20:48 +01:00
func - > argCount ( ) = = 0 & & func - > functionScope & &
func - > arg & & func - > arg - > link ( ) - > next ( ) = = func - > functionScope - > classStart & &
func - > functionScope - > classStart - > link ( ) = = func - > functionScope - > classStart - > next ( ) ) {
// don't warn about user defined default constructor when there are other constructors
2015-04-10 14:18:52 +02:00
if ( printInconclusive )
2013-03-09 09:20:48 +01:00
uninitVarError ( func - > token , scope - > className , var - > name ( ) , true ) ;
} else
uninitVarError ( func - > token , scope - > className , var - > name ( ) , inconclusive ) ;
}
2012-01-08 12:17:55 +01:00
}
2010-07-15 10:16:16 +02:00
}
2010-07-14 19:00:52 +02:00
}
2010-01-16 08:47:46 +01:00
}
}
2015-03-07 20:07:54 +01:00
void CheckClass : : checkExplicitConstructors ( )
{
2015-03-10 19:35:12 +01:00
if ( ! _settings - > isEnabled ( " style " ) )
2015-03-07 20:07:54 +01:00
return ;
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
// Do not perform check, if the class/struct has not any constructors
if ( scope - > numConstructors = = 0 )
continue ;
// Is class abstract? Maybe this test is over-simplification, but it will suffice for simple cases,
// and it will avoid false positives.
bool isAbstractClass = false ;
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( func - > isPure ( ) ) {
isAbstractClass = true ;
break ;
}
}
2015-11-13 12:48:26 +01:00
// Abstract classes can't be instantiated. But if there is C++11
// "misuse" by derived classes then these constructors must be explicit.
2015-11-09 08:36:47 +01:00
if ( isAbstractClass & & _settings - > standards . cpp ! = Standards : : CPP11 )
continue ;
2015-03-07 20:07:54 +01:00
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
// We are looking for constructors, which are meeting following criteria:
2015-03-11 20:26:53 +01:00
// 1) Constructor is declared with a single parameter
2015-03-07 20:07:54 +01:00
// 2) Constructor is not declared as explicit
// 3) It is not a copy/move constructor of non-abstract class
// 4) Constructor is not marked as delete (programmer can mark the default constructor as deleted, which is ok)
2015-07-21 13:46:14 +02:00
if ( ! func - > isConstructor ( ) | | func - > isDelete ( ) | | ( ! func - > hasBody ( ) & & func - > access = = Private ) )
2015-03-07 20:07:54 +01:00
continue ;
2015-11-13 12:48:26 +01:00
if ( ! func - > isExplicit ( ) & &
func - > argCount ( ) = = 1 & &
func - > type ! = Function : : eCopyConstructor & &
func - > type ! = Function : : eMoveConstructor ) {
2015-11-14 19:59:22 +01:00
noExplicitConstructorError ( func - > tokenDef , scope - > className , scope - > type = = Scope : : eStruct ) ;
2015-03-07 20:07:54 +01:00
}
}
2015-11-14 19:59:22 +01:00
}
2015-03-07 20:07:54 +01:00
}
2012-09-17 17:59:35 +02:00
void CheckClass : : copyconstructors ( )
{
if ( ! _settings - > isEnabled ( " style " ) )
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2012-09-17 17:59:35 +02:00
std : : map < unsigned int , const Token * > allocatedVars ;
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( func - > type = = Function : : eConstructor & & func - > functionScope ) {
2013-03-14 17:27:42 +01:00
const Token * tok = func - > functionScope - > classDef - > linkAt ( 1 ) ;
for ( const Token * const end = func - > functionScope - > classStart ; tok ! = end ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% ( new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc " ) ) {
const Variable * var = tok - > variable ( ) ;
2015-01-17 16:28:39 +01:00
if ( var & & var - > isPointer ( ) & & var - > scope ( ) = = scope )
2013-03-14 17:27:42 +01:00
allocatedVars [ tok - > varId ( ) ] = tok ;
}
}
for ( const Token * const end = func - > functionScope - > classEnd ; tok ! = end ; tok = tok - > next ( ) ) {
2012-09-17 17:59:35 +02:00
if ( Token : : Match ( tok , " %var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc " ) ) {
2013-02-02 16:30:17 +01:00
const Variable * var = tok - > variable ( ) ;
2015-12-04 18:26:49 +01:00
if ( var & & var - > isPointer ( ) & & var - > scope ( ) = = scope & & ! var - > isStatic ( ) )
2012-09-17 17:59:35 +02:00
allocatedVars [ tok - > varId ( ) ] = tok ;
}
}
}
}
std : : set < const Token * > copiedVars ;
const Token * copyCtor = 0 ;
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( func - > type = = Function : : eCopyConstructor ) {
copyCtor = func - > tokenDef ;
if ( func - > functionScope ) {
const Token * tok = func - > tokenDef - > linkAt ( 1 ) - > next ( ) ;
if ( tok - > str ( ) = = " : " ) {
tok = tok - > next ( ) ;
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( tok , " %name% ( " ) ) {
2012-09-17 17:59:35 +02:00
if ( allocatedVars . find ( tok - > varId ( ) ) ! = allocatedVars . end ( ) ) {
2015-01-31 10:50:39 +01:00
if ( tok - > varId ( ) & & Token : : Match ( tok - > tokAt ( 2 ) , " %name% . %name% ) " ) )
2012-09-17 17:59:35 +02:00
copiedVars . insert ( tok ) ;
else if ( ! Token : : Match ( tok - > tokAt ( 2 ) , " %any% ) " ) )
allocatedVars . erase ( tok - > varId ( ) ) ; // Assume memory is allocated
}
tok = tok - > linkAt ( 1 ) - > tokAt ( 2 ) ;
}
}
for ( tok = func - > functionScope - > classStart ; tok ! = func - > functionScope - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc " ) ) {
allocatedVars . erase ( tok - > varId ( ) ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " %var% = %name% . %name% ; " ) & & allocatedVars . find ( tok - > varId ( ) ) ! = allocatedVars . end ( ) ) {
2012-09-17 17:59:35 +02:00
copiedVars . insert ( tok ) ;
}
}
} else // non-copyable or implementation not seen
allocatedVars . clear ( ) ;
break ;
}
}
if ( ! copyCtor ) {
2013-03-05 15:28:40 +01:00
if ( ! allocatedVars . empty ( ) & & scope - > definedType - > derivedFrom . empty ( ) ) // TODO: Check if base class is non-copyable
2012-09-17 17:59:35 +02:00
noCopyConstructorError ( scope - > classDef , scope - > className , scope - > type = = Scope : : eStruct ) ;
} else {
if ( ! copiedVars . empty ( ) ) {
2012-10-10 20:42:07 +02:00
for ( std : : set < const Token * > : : const_iterator it = copiedVars . begin ( ) ; it ! = copiedVars . end ( ) ; + + it ) {
copyConstructorShallowCopyError ( * it , ( * it ) - > str ( ) ) ;
2012-09-17 17:59:35 +02:00
}
}
// throw error if count mismatch
2012-09-17 18:10:11 +02:00
/* FIXME: This doesn't work. See #4154
2012-09-17 17:59:35 +02:00
for ( std : : map < unsigned int , const Token * > : : const_iterator i = allocatedVars . begin ( ) ; i ! = allocatedVars . end ( ) ; + + i ) {
copyConstructorMallocError ( copyCtor , i - > second , i - > second - > str ( ) ) ;
}
2012-09-17 18:10:11 +02:00
*/
2012-09-17 17:59:35 +02:00
}
}
}
2012-09-17 18:10:11 +02:00
/* This doesn't work. See #4154
2012-09-17 17:59:35 +02:00
void CheckClass : : copyConstructorMallocError ( const Token * cctor , const Token * alloc , const std : : string & varname )
{
std : : list < const Token * > callstack ;
callstack . push_back ( cctor ) ;
callstack . push_back ( alloc ) ;
reportError ( callstack , Severity : : warning , " copyCtorNoAllocation " , " Copy constructor does not allocate memory for member ' " + varname + " ' although memory has been allocated in other constructors. " ) ;
}
2012-09-17 18:10:11 +02:00
*/
2012-09-17 17:59:35 +02:00
void CheckClass : : copyConstructorShallowCopyError ( const Token * tok , const std : : string & varname )
{
2016-02-27 16:03:50 +01:00
reportError ( tok , Severity : : style , " copyCtorPointerCopying " ,
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" Value of pointer ' " + varname + " ', which points to allocated memory, is copied in copy constructor instead of allocating new memory. " , CWE398 , false ) ;
2012-09-17 17:59:35 +02:00
}
void CheckClass : : noCopyConstructorError ( const Token * tok , const std : : string & classname , bool isStruct )
{
// The constructor might be intentionally missing. Therefore this is not a "warning"
reportError ( tok , Severity : : style , " noCopyConstructor " ,
" ' " + std : : string ( isStruct ? " struct " : " class " ) + " " + classname +
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" ' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory. " , CWE398 , false ) ;
2012-09-17 17:59:35 +02:00
}
2012-08-02 18:50:48 +02:00
bool CheckClass : : canNotCopy ( const Scope * scope )
2011-01-18 07:34:11 +01:00
{
std : : list < Function > : : const_iterator func ;
2012-09-22 08:16:16 +02:00
bool constructor = false ;
bool publicAssign = false ;
bool publicCopy = false ;
2011-01-18 07:34:11 +01:00
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2013-04-10 21:57:22 +02:00
if ( func - > isConstructor ( ) )
2012-09-22 08:16:16 +02:00
constructor = true ;
2013-04-10 21:57:22 +02:00
if ( ( func - > type = = Function : : eCopyConstructor ) & &
func - > access = = Public )
2012-09-22 08:16:16 +02:00
publicCopy = true ;
else if ( func - > type = = Function : : eOperatorEqual & & func - > access = = Public )
publicAssign = true ;
2011-01-18 07:34:11 +01:00
}
2012-09-29 12:19:30 +02:00
return constructor & & ! ( publicAssign | | publicCopy ) ;
2011-01-18 07:34:11 +01:00
}
2013-04-10 21:57:22 +02:00
bool CheckClass : : canNotMove ( const Scope * scope )
{
std : : list < Function > : : const_iterator func ;
bool constructor = false ;
bool publicAssign = false ;
bool publicCopy = false ;
bool publicMove = false ;
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( func - > isConstructor ( ) )
constructor = true ;
if ( ( func - > type = = Function : : eCopyConstructor ) & &
func - > access = = Public )
publicCopy = true ;
else if ( ( func - > type = = Function : : eMoveConstructor ) & &
func - > access = = Public )
publicMove = true ;
else if ( func - > type = = Function : : eOperatorEqual & & func - > access = = Public )
publicAssign = true ;
}
return constructor & & ! ( publicAssign | | publicCopy | | publicMove ) ;
}
2015-11-15 10:04:27 +01:00
void CheckClass : : assignVar ( unsigned int varid , const Scope * scope , std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-01-16 16:37:11 +01:00
unsigned int count = 0 ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var , + + count ) {
2015-11-15 10:04:27 +01:00
if ( var - > declarationId ( ) = = varid ) {
2011-01-16 16:37:11 +01:00
usage [ count ] . assign = true ;
return ;
}
}
}
2015-11-15 10:04:27 +01:00
void CheckClass : : initVar ( unsigned int varid , const Scope * scope , std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-01-16 16:37:11 +01:00
unsigned int count = 0 ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var , + + count ) {
2015-11-15 10:04:27 +01:00
if ( var - > declarationId ( ) = = varid ) {
2011-01-16 16:37:11 +01:00
usage [ count ] . init = true ;
return ;
}
}
}
2012-08-02 18:50:48 +02:00
void CheckClass : : assignAllVar ( std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
2012-07-08 23:39:46 +02:00
for ( std : : size_t i = 0 ; i < usage . size ( ) ; + + i )
2011-01-16 16:37:11 +01:00
usage [ i ] . assign = true ;
}
2012-08-02 18:50:48 +02:00
void CheckClass : : clearAllVar ( std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
2012-07-08 23:39:46 +02:00
for ( std : : size_t i = 0 ; i < usage . size ( ) ; + + i ) {
2011-01-16 16:37:11 +01:00
usage [ i ] . assign = false ;
usage [ i ] . init = false ;
}
}
2011-01-17 18:29:19 +01:00
bool CheckClass : : isBaseClassFunc ( const Token * tok , const Scope * scope )
2011-01-16 16:37:11 +01:00
{
// Iterate through each base class...
2013-03-05 15:28:40 +01:00
for ( std : : size_t i = 0 ; i < scope - > definedType - > derivedFrom . size ( ) ; + + i ) {
const Type * derivedFrom = scope - > definedType - > derivedFrom [ i ] . type ;
2011-01-16 16:37:11 +01:00
// Check if base class exists in database
2013-03-05 15:28:40 +01:00
if ( derivedFrom & & derivedFrom - > classScope ) {
2014-07-23 16:51:23 +02:00
const std : : list < Function > & functionList = derivedFrom - > classScope - > functionList ;
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
2011-01-16 16:37:11 +01:00
2014-07-23 16:51:23 +02:00
for ( func = functionList . begin ( ) ; func ! = functionList . end ( ) ; + + func ) {
2011-01-16 16:37:11 +01:00
if ( func - > tokenDef - > str ( ) = = tok - > str ( ) )
return true ;
}
}
// Base class not found so assume it is in it.
else
return true ;
}
return false ;
}
2013-01-01 09:53:40 +01:00
void CheckClass : : initializeVarList ( const Function & func , std : : list < const Function * > & callstack , const Scope * scope , std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
2014-04-21 21:44:17 +02:00
if ( ! func . functionScope )
throw InternalError ( 0 , " Internal Error: Invalid syntax " ) ; // #5702
2013-04-10 21:57:22 +02:00
bool initList = func . isConstructor ( ) ;
2012-07-10 14:47:51 +02:00
const Token * ftok = func . arg - > link ( ) - > next ( ) ;
int level = 0 ;
2014-04-04 22:13:51 +02:00
for ( ; ftok & & ftok ! = func . functionScope - > classEnd ; ftok = ftok - > next ( ) ) {
2011-01-16 16:37:11 +01:00
// Class constructor.. initializing variables like this
// clKalle::clKalle() : var(value) { }
2012-04-05 09:43:40 +02:00
if ( initList ) {
2015-04-06 19:47:21 +02:00
if ( level = = 0 & & Token : : Match ( ftok , " %name% {|( " ) & & Token : : Match ( ftok - > linkAt ( 1 ) , " }|) ,|{ " ) ) {
2013-03-02 07:24:51 +01:00
if ( ftok - > str ( ) ! = func . name ( ) ) {
2015-11-15 10:04:27 +01:00
initVar ( ftok - > varId ( ) , scope , usage ) ;
2013-03-02 07:24:51 +01:00
} else { // c++11 delegate constructor
2014-11-13 06:29:51 +01:00
const Function * member = ftok - > function ( ) ;
2013-01-01 09:53:40 +01:00
// member function found
if ( member ) {
// recursive call
// assume that all variables are initialized
if ( std : : find ( callstack . begin ( ) , callstack . end ( ) , member ) ! = callstack . end ( ) ) {
/** @todo false negative: just bail */
assignAllVar ( usage ) ;
return ;
}
// member function has implementation
2015-01-08 05:45:31 +01:00
if ( member - > hasBody ( ) ) {
2013-01-01 09:53:40 +01:00
// initialize variable use list using member function
callstack . push_back ( member ) ;
initializeVarList ( * member , callstack , scope , usage ) ;
callstack . pop_back ( ) ;
}
// there is a called member function, but it has no implementation, so we assume it initializes everything
else {
assignAllVar ( usage ) ;
}
}
}
2015-01-31 10:50:39 +01:00
} else if ( level ! = 0 & & Token : : Match ( ftok , " %name% = " ) ) // assignment in the initializer: var(value = x)
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > varId ( ) , scope , usage ) ;
2012-07-10 14:47:51 +02:00
2015-04-06 19:47:21 +02:00
// Level handling
if ( ftok - > link ( ) & & Token : : Match ( ftok , " (|< " ) )
2012-07-10 14:47:51 +02:00
level + + ;
else if ( ftok - > str ( ) = = " { " ) {
2015-04-06 19:47:21 +02:00
if ( level ! = 0 | |
( Token : : Match ( ftok - > previous ( ) , " %name%|> " ) & & Token : : Match ( ftok - > link ( ) , " } ,|{ " ) ) )
level + + ;
2012-07-10 14:47:51 +02:00
else
2015-04-06 19:47:21 +02:00
initList = false ;
} else if ( ftok - > link ( ) & & Token : : Match ( ftok , " )|>|} " ) )
level - - ;
2011-01-16 16:37:11 +01:00
}
2012-04-05 09:43:40 +02:00
if ( initList )
2011-01-16 16:37:11 +01:00
continue ;
// Variable getting value from stream?
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok , " >> %name% " ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > next ( ) - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
2016-01-30 20:15:56 +01:00
// Before a new statement there is "[{};()=[]" or ::
if ( ! Token : : Match ( ftok , " {|}|;|(|)|=|[|:: " ) )
2011-01-16 16:37:11 +01:00
continue ;
if ( Token : : simpleMatch ( ftok , " ( ! " ) )
ftok = ftok - > next ( ) ;
// Using the operator= function to initialize all variables..
2012-05-22 11:27:21 +02:00
if ( Token : : Match ( ftok - > next ( ) , " return| (| * this )| = " ) ) {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
break ;
}
2012-09-22 08:50:36 +02:00
// Using swap to assign all variables..
2015-01-31 10:50:39 +01:00
if ( func . type = = Function : : eOperatorEqual & & Token : : Match ( ftok , " [;{}] %name% ( " ) & & Token : : Match ( ftok - > linkAt ( 2 ) , " ) . %name% ( *| this ) ; " ) ) {
2012-09-22 08:50:36 +02:00
assignAllVar ( usage ) ;
break ;
}
2011-01-16 16:37:11 +01:00
// Calling member variable function?
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok - > next ( ) , " %var% . %name% ( " ) ) {
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var ) {
2013-07-20 12:31:04 +02:00
if ( var - > declarationId ( ) = = ftok - > next ( ) - > varId ( ) ) {
2011-01-16 16:37:11 +01:00
/** @todo false negative: we assume function changes variable state */
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > next ( ) - > varId ( ) , scope , usage ) ;
2012-03-11 14:29:00 +01:00
break ;
2011-01-16 16:37:11 +01:00
}
}
ftok = ftok - > tokAt ( 2 ) ;
}
2015-01-31 10:50:39 +01:00
if ( ! Token : : Match ( ftok - > next ( ) , " ::| %name% " ) & &
! Token : : Match ( ftok - > next ( ) , " *| this . %name% " ) & &
! Token : : Match ( ftok - > next ( ) , " * %name% = " ) & &
! Token : : Match ( ftok - > next ( ) , " ( * this ) . %name% " ) )
2011-01-16 16:37:11 +01:00
continue ;
// Goto the first token in this statement..
ftok = ftok - > next ( ) ;
2011-08-26 05:27:10 +02:00
// skip "return"
if ( ftok - > str ( ) = = " return " )
ftok = ftok - > next ( ) ;
2011-01-16 16:37:11 +01:00
// Skip "( * this )"
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( ftok , " ( * this ) . " ) ) {
2011-01-16 16:37:11 +01:00
ftok = ftok - > tokAt ( 5 ) ;
}
// Skip "this->"
if ( Token : : simpleMatch ( ftok , " this . " ) )
ftok = ftok - > tokAt ( 2 ) ;
// Skip "classname :: "
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok , " :: %name% " ) )
2014-01-01 18:34:39 +01:00
ftok = ftok - > next ( ) ;
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( ftok , " %name% :: " ) )
2011-01-16 16:37:11 +01:00
ftok = ftok - > tokAt ( 2 ) ;
// Clearing all variables..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok , " ::| memset ( this , " ) ) {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
return ;
}
// Clearing array..
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( ftok , " ::| memset ( %name% , " ) ) {
2012-03-11 14:29:00 +01:00
if ( ftok - > str ( ) = = " :: " )
ftok = ftok - > next ( ) ;
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > tokAt ( 2 ) - > varId ( ) , scope , usage ) ;
2012-03-11 14:29:00 +01:00
ftok = ftok - > linkAt ( 1 ) ;
2011-01-16 16:37:11 +01:00
continue ;
}
// Calling member function?
2011-01-27 18:44:20 +01:00
else if ( Token : : simpleMatch ( ftok , " operator= ( " ) & &
2011-10-13 20:53:06 +02:00
ftok - > previous ( ) - > str ( ) ! = " :: " ) {
2013-02-27 06:59:04 +01:00
if ( ftok - > function ( ) & & ftok - > function ( ) - > nestedIn = = scope ) {
const Function * member = ftok - > function ( ) ;
2013-01-01 09:53:40 +01:00
// recursive call
// assume that all variables are initialized
if ( std : : find ( callstack . begin ( ) , callstack . end ( ) , member ) ! = callstack . end ( ) ) {
/** @todo false negative: just bail */
assignAllVar ( usage ) ;
return ;
}
2011-01-16 16:37:11 +01:00
// member function has implementation
2015-01-08 05:45:31 +01:00
if ( member - > hasBody ( ) ) {
2011-09-15 01:58:11 +02:00
// initialize variable use list using member function
2013-01-01 09:53:40 +01:00
callstack . push_back ( member ) ;
initializeVarList ( * member , callstack , scope , usage ) ;
2011-09-15 01:58:11 +02:00
callstack . pop_back ( ) ;
2011-01-16 16:37:11 +01:00
}
// there is a called member function, but it has no implementation, so we assume it initializes everything
2011-10-13 20:53:06 +02:00
else {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
}
}
// using default operator =, assume everything initialized
2011-10-13 20:53:06 +02:00
else {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
}
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( ftok , " ::| %name% ( " ) & & ftok - > str ( ) ! = " if " ) {
2011-11-30 21:28:16 +01:00
if ( ftok - > str ( ) = = " :: " )
ftok = ftok - > next ( ) ;
2011-01-16 16:37:11 +01:00
// Passing "this" => assume that everything is initialized
2011-10-13 20:53:06 +02:00
for ( const Token * tok2 = ftok - > next ( ) - > link ( ) ; tok2 & & tok2 ! = ftok ; tok2 = tok2 - > previous ( ) ) {
if ( tok2 - > str ( ) = = " this " ) {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
return ;
}
}
// check if member function
2013-02-27 06:59:04 +01:00
if ( ftok - > function ( ) & & ftok - > function ( ) - > nestedIn = = scope & &
2013-04-10 21:57:22 +02:00
! ftok - > function ( ) - > isConstructor ( ) ) {
2013-02-27 06:59:04 +01:00
const Function * member = ftok - > function ( ) ;
2011-01-16 16:37:11 +01:00
2013-01-01 09:53:40 +01:00
// recursive call
// assume that all variables are initialized
if ( std : : find ( callstack . begin ( ) , callstack . end ( ) , member ) ! = callstack . end ( ) ) {
assignAllVar ( usage ) ;
return ;
}
2011-01-16 16:37:11 +01:00
// member function has implementation
2015-01-08 05:45:31 +01:00
if ( member - > hasBody ( ) ) {
2011-09-15 01:58:11 +02:00
// initialize variable use list using member function
2013-01-01 09:53:40 +01:00
callstack . push_back ( member ) ;
initializeVarList ( * member , callstack , scope , usage ) ;
2011-09-15 01:58:11 +02:00
callstack . pop_back ( ) ;
2012-09-23 18:29:05 +02:00
// Assume that variables that are passed to it are initialized..
for ( const Token * tok2 = ftok ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( Token : : Match ( tok2 , " [;{}] " ) )
break ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok2 , " [(,] &| %name% [,)] " ) ) {
2012-09-23 18:29:05 +02:00
tok2 = tok2 - > next ( ) ;
if ( tok2 - > str ( ) = = " & " )
tok2 = tok2 - > next ( ) ;
2015-11-15 10:04:27 +01:00
assignVar ( tok2 - > varId ( ) , scope , usage ) ;
2012-09-23 18:29:05 +02:00
}
}
2011-01-16 16:37:11 +01:00
}
// there is a called member function, but it has no implementation, so we assume it initializes everything
2011-10-13 20:53:06 +02:00
else {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
}
}
// not member function
2011-10-13 20:53:06 +02:00
else {
2011-01-16 16:37:11 +01:00
// could be a base class virtual function, so we assume it initializes everything
2013-04-10 21:57:22 +02:00
if ( ! func . isConstructor ( ) & & isBaseClassFunc ( ftok , scope ) ) {
2011-01-16 16:37:11 +01:00
/** @todo False Negative: we should look at the base class functions to see if they
* call any derived class virtual functions that change the derived class state
*/
assignAllVar ( usage ) ;
}
// has friends, so we assume it initializes everything
2013-03-05 15:28:40 +01:00
if ( ! scope - > definedType - > friendList . empty ( ) )
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
// the function is external and it's neither friend nor inherited virtual function.
// assume all variables that are passed to it are initialized..
2011-10-13 20:53:06 +02:00
else {
2012-01-15 12:31:49 +01:00
for ( const Token * tok = ftok - > tokAt ( 2 ) ; tok & & tok ! = ftok - > next ( ) - > link ( ) ; tok = tok - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( tok - > isName ( ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( tok - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
}
}
}
}
// Assignment of member variable?
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( ftok , " %name% = " ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > varId ( ) , scope , usage ) ;
2015-11-09 21:02:06 +01:00
bool bailout = ftok - > variable ( ) & & ftok - > variable ( ) - > isReference ( ) ;
const Token * tok2 = ftok - > tokAt ( 2 ) ;
if ( tok2 - > str ( ) = = " & " ) {
tok2 = tok2 - > next ( ) ;
bailout = true ;
}
if ( tok2 - > variable ( ) & & ( bailout | | tok2 - > variable ( ) - > isArray ( ) ) & & tok2 - > strAt ( 1 ) ! = " [ " )
2015-11-15 10:04:27 +01:00
assignVar ( tok2 - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// Assignment of array item of member variable?
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( ftok , " %name% [|. " ) ) {
2011-11-22 18:45:25 +01:00
const Token * tok2 = ftok ;
while ( tok2 ) {
2013-03-01 12:42:04 +01:00
if ( tok2 - > strAt ( 1 ) = = " [ " )
2011-11-22 18:45:25 +01:00
tok2 = tok2 - > next ( ) - > link ( ) ;
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok2 - > next ( ) , " . %name% " ) )
2011-11-22 18:45:25 +01:00
tok2 = tok2 - > tokAt ( 2 ) ;
else
break ;
}
2011-12-08 21:28:34 +01:00
if ( tok2 & & tok2 - > strAt ( 1 ) = = " = " )
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// Assignment of array item of member variable?
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( ftok , " * %name% = " ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > next ( ) - > varId ( ) , scope , usage ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( ftok , " * this . %name% = " ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > tokAt ( 3 ) - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// The functions 'clear' and 'Clear' are supposed to initialize variable.
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok , " %name% . clear|Clear ( " ) ) {
2015-11-15 10:04:27 +01:00
assignVar ( ftok - > varId ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
}
}
2010-11-14 06:50:33 +01:00
void CheckClass : : noConstructorError ( const Token * tok , const std : : string & classname , bool isStruct )
{
// For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning"
2010-11-27 09:17:03 +01:00
reportError ( tok , Severity : : style , " noConstructor " ,
" The " + std : : string ( isStruct ? " struct " : " class " ) + " ' " + classname +
" ' does not have a constructor. \n "
2010-12-01 16:55:22 +01:00
" The " + std : : string ( isStruct ? " struct " : " class " ) + " ' " + classname +
2012-07-09 11:11:05 +02:00
" ' does not have a constructor although it has private member variables. "
" Member variables of builtin types are left uninitialized when the class is "
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" instantiated. That may cause bugs or undefined behavior. " , CWE398 , false ) ;
2010-11-14 06:50:33 +01:00
}
2015-03-07 20:07:54 +01:00
void CheckClass : : noExplicitConstructorError ( const Token * tok , const std : : string & classname , bool isStruct )
{
2015-03-10 19:35:12 +01:00
const std : : string message ( std : : string ( isStruct ? " Struct " : " Class " ) + " ' " + classname + " ' has a constructor with 1 argument that is not explicit. " ) ;
const std : : string verbose ( message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided. " ) ;
reportError ( tok , Severity : : style , " noExplicitConstructor " , message + " \n " + verbose ) ;
2015-03-07 20:07:54 +01:00
}
2012-09-20 19:16:26 +02:00
void CheckClass : : uninitVarError ( const Token * tok , const std : : string & classname , const std : : string & varname , bool inconclusive )
2010-11-14 06:50:33 +01:00
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : warning , " uninitMemberVar " , " Member variable ' " + classname + " :: " + varname + " ' is not initialized in the constructor. " , CWE398 , inconclusive ) ;
2010-11-14 06:50:33 +01:00
}
2012-09-20 19:16:26 +02:00
void CheckClass : : operatorEqVarError ( const Token * tok , const std : : string & classname , const std : : string & varname , bool inconclusive )
2010-11-14 06:50:33 +01:00
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : warning , " operatorEqVarError " , " Member variable ' " + classname + " :: " + varname + " ' is not assigned a value in ' " + classname + " ::operator='. " , CWE398 , inconclusive ) ;
2010-11-14 06:50:33 +01:00
}
2012-05-18 16:54:58 +02:00
//---------------------------------------------------------------------------
// ClassCheck: Use initialization list instead of assignment
//---------------------------------------------------------------------------
void CheckClass : : initializationListUsage ( )
{
if ( ! _settings - > isEnabled ( " performance " ) )
return ;
2012-10-11 06:12:24 +02: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-05-18 16:54:58 +02:00
// Check every constructor
2013-04-10 21:57:22 +02:00
if ( ! scope - > function | | ( ! scope - > function - > isConstructor ( ) ) )
2012-05-18 16:54:58 +02:00
continue ;
2013-03-05 18:42:42 +01:00
const Scope * owner = scope - > functionOf ;
2012-05-18 16:54:58 +02:00
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% ( " ) ) // Assignments might depend on this function call or if/for/while/switch statement from now on.
2012-05-18 16:54:58 +02:00
break ;
2012-05-22 10:35:56 +02:00
if ( Token : : Match ( tok , " try|do { " ) )
break ;
2015-12-24 14:40:48 +01:00
if ( Token : : Match ( tok , " %var% = " ) & & tok - > strAt ( - 1 ) ! = " * " ) {
2013-02-02 16:30:17 +01:00
const Variable * var = tok - > variable ( ) ;
2012-11-04 11:59:09 +01:00
if ( var & & var - > scope ( ) = = owner & & ! var - > isStatic ( ) ) {
2015-08-14 12:50:45 +02:00
if ( var - > isPointer ( ) | | var - > isReference ( ) | | ( ! var - > type ( ) & & ! var - > isStlStringType ( ) & & ! ( Token : : Match ( var - > typeStartToken ( ) , " std :: %type% < " ) & & ! Token : : simpleMatch ( var - > typeStartToken ( ) - > linkAt ( 3 ) , " > :: " ) ) ) )
continue ;
2012-05-18 16:54:58 +02:00
bool allowed = true ;
2015-05-29 18:34:00 +02:00
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 & & tok2 - > str ( ) ! = " ; " ; tok2 = tok2 - > next ( ) ) {
2015-08-14 12:50:45 +02:00
const Variable * var2 = tok2 - > variable ( ) ;
if ( var2 ) {
if ( var2 - > scope ( ) = = owner & & tok2 - > strAt ( - 1 ) ! = " . " ) { // Is there a dependency between two member variables?
2012-05-18 16:54:58 +02:00
allowed = false ;
break ;
2015-08-14 12:50:45 +02:00
} else if ( var2 - > isArray ( ) & & var2 - > isLocal ( ) ) { // Can't initialize with a local array
2014-04-10 22:28:02 +02:00
allowed = false ;
break ;
2012-05-18 16:54:58 +02:00
}
2012-07-23 17:16:47 +02:00
} else if ( tok2 - > str ( ) = = " this " ) { // 'this' instance is not completely constructed in initialization list
allowed = false ;
break ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok2 , " %name% ( " ) & & tok2 - > strAt ( - 1 ) ! = " . " & & isMemberFunc ( owner , tok2 ) ) { // Member function called?
2012-07-23 17:16:47 +02:00
allowed = false ;
break ;
2012-05-18 16:54:58 +02:00
}
}
if ( ! allowed )
continue ;
2015-08-14 12:50:45 +02:00
suggestInitializationList ( tok , tok - > str ( ) ) ;
2012-05-18 16:54:58 +02:00
}
}
}
}
}
2012-05-19 21:51:39 +02:00
void CheckClass : : suggestInitializationList ( const Token * tok , const std : : string & varname )
2012-05-18 16:54:58 +02:00
{
2012-07-31 21:28:42 +02:00
reportError ( tok , Severity : : performance , " useInitializationList " , " Variable ' " + varname + " ' is assigned in constructor body. Consider performing initialization in initialization list. \n "
" When an object of a class is created, the constructors of all member variables are called consecutively "
2012-05-18 16:54:58 +02:00
" in the order the variables are declared, even if you don't explicitly write them to the initialization list. You "
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" could avoid assigning ' " + varname + " ' a value by passing the value to the constructor in the initialization list. " , CWE398 , false ) ;
2012-05-18 16:54:58 +02:00
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: Unused private functions
//---------------------------------------------------------------------------
2012-02-24 20:45:56 +01:00
static bool checkFunctionUsage ( const std : : string & name , const Scope * scope )
{
if ( ! scope )
2013-02-16 10:51:08 +01:00
return true ; // Assume it is used, if scope is not seen
2012-02-24 20:45:56 +01:00
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2012-05-22 21:58:46 +02:00
if ( func - > functionScope ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( func - > tokenDef , " %name% ( " ) ) {
2013-08-26 16:41:23 +02:00
for ( const Token * ftok = func - > tokenDef - > tokAt ( 2 ) ; ftok & & ftok - > str ( ) ! = " ) " ; ftok = ftok - > next ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok , " = %name% [(,)] " ) & & ftok - > strAt ( 1 ) = = name )
2013-08-26 16:41:23 +02:00
return true ;
if ( ftok - > str ( ) = = " ( " )
ftok = ftok - > link ( ) ;
}
}
2013-02-16 10:51:08 +01:00
for ( const Token * ftok = func - > functionScope - > classDef - > linkAt ( 1 ) ; ftok ! = func - > functionScope - > classEnd ; ftok = ftok - > next ( ) ) {
if ( ftok - > str ( ) = = name ) // Function used. TODO: Handle overloads
2012-02-24 20:45:56 +01:00
return true ;
}
2013-02-16 10:51:08 +01:00
} else if ( ( func - > type ! = Function : : eCopyConstructor & &
func - > type ! = Function : : eOperatorEqual ) | |
func - > access ! = Private ) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK
return true ;
}
2013-02-16 11:35:54 +01:00
for ( std : : list < Scope * > : : const_iterator i = scope - > nestedList . begin ( ) ; i ! = scope - > nestedList . end ( ) ; + + i ) {
2013-02-16 10:51:08 +01:00
if ( ( * i ) - > isClassOrStruct ( ) )
if ( checkFunctionUsage ( name , * i ) ) // Check nested classes, which can access private functions of their base
return true ;
2012-02-24 20:45:56 +01:00
}
2014-08-26 11:29:26 +02:00
for ( std : : list < Variable > : : const_iterator i = scope - > varlist . begin ( ) ; i ! = scope - > varlist . end ( ) ; + + i ) {
if ( i - > isStatic ( ) ) {
const Token * tok = Token : : findmatch ( scope - > classEnd , " %varid% =|(|{ " , i - > declarationId ( ) ) ;
if ( tok )
tok = tok - > tokAt ( 2 ) ;
while ( tok & & tok - > str ( ) ! = " ; " ) {
if ( tok - > str ( ) = = name & & ( tok - > strAt ( - 1 ) = = " . " | | tok - > strAt ( - 2 ) = = scope - > className ) )
return true ;
tok = tok - > next ( ) ;
}
}
}
2012-02-24 20:45:56 +01:00
return false ; // Unused in this scope
}
2010-01-16 08:47:46 +01:00
void CheckClass : : privateFunctions ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-04-21 08:38:25 +02:00
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2010-09-02 07:40:20 +02:00
2013-11-09 16:07:28 +01:00
// do not check borland classes with properties..
2011-12-14 21:11:40 +01:00
if ( Token : : findsimplematch ( scope - > classStart , " ; __property ; " , scope - > classEnd ) )
continue ;
2010-09-02 07:40:20 +02:00
2014-08-06 13:35:04 +02:00
std : : list < const Function * > privateFuncs ;
2013-02-16 10:51:08 +01:00
for ( std : : list < Function > : : const_iterator func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
// Get private functions..
2015-01-08 05:45:31 +01:00
if ( func - > type = = Function : : eFunction & & func - > access = = Private & & ! func - > isOperator ( ) ) // TODO: There are smarter ways to check private operator usage
2014-08-06 13:35:04 +02:00
privateFuncs . push_back ( & * func ) ;
2010-01-16 08:47:46 +01:00
}
2013-01-16 15:37:07 +01:00
// Bailout for overridden virtual functions of base classes
2013-03-05 15:28:40 +01:00
if ( ! scope - > definedType - > derivedFrom . empty ( ) ) {
2012-03-24 10:50:19 +01:00
// Check virtual functions
2014-08-06 13:35:04 +02:00
for ( std : : list < const Function * > : : iterator it = privateFuncs . begin ( ) ; it ! = privateFuncs . end ( ) ; ) {
2012-10-10 20:42:07 +02:00
if ( ( * it ) - > isImplicitlyVirtual ( true ) ) // Give true as default value to be returned if we don't see all base classes
2014-08-06 13:35:04 +02:00
privateFuncs . erase ( it + + ) ;
2012-03-24 10:50:19 +01:00
else
2012-10-10 20:42:07 +02:00
+ + it ;
2012-03-24 10:50:19 +01:00
}
}
2011-02-22 12:47:28 +01:00
2014-08-06 13:35:04 +02:00
while ( ! privateFuncs . empty ( ) ) {
const std : : string & funcName = privateFuncs . front ( ) - > tokenDef - > str ( ) ;
2012-02-24 20:45:56 +01:00
// Check that all private functions are used
2015-01-17 16:28:39 +01:00
bool used = checkFunctionUsage ( funcName , scope ) ; // Usage in this class
2012-02-24 20:45:56 +01:00
// Check in friend classes
2014-07-23 16:51:23 +02:00
const std : : list < Type : : FriendInfo > & friendList = scope - > definedType - > friendList ;
for ( std : : list < Type : : FriendInfo > : : const_iterator it = friendList . begin ( ) ; ! used & & it ! = friendList . end ( ) ; + + it ) {
2013-03-05 15:28:40 +01:00
if ( it - > type )
used = checkFunctionUsage ( funcName , it - > type - > classScope ) ;
else
used = true ; // Assume, it is used if we do not see friend class
2013-08-26 16:41:23 +02:00
}
2012-02-24 20:45:56 +01:00
2013-02-16 10:51:08 +01:00
if ( ! used )
2014-08-06 13:35:04 +02:00
unusedPrivateFunctionError ( privateFuncs . front ( ) - > tokenDef , scope - > className , funcName ) ;
2010-01-16 08:47:46 +01:00
2014-08-06 13:35:04 +02:00
privateFuncs . pop_front ( ) ;
2010-01-16 08:47:46 +01:00
}
}
}
2010-11-14 06:50:33 +01:00
void CheckClass : : unusedPrivateFunctionError ( const Token * tok , const std : : string & classname , const std : : string & funcname )
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : style , " unusedPrivateFunction " , " Unused private function: ' " + classname + " :: " + funcname + " ' " , CWE398 , false ) ;
2010-11-14 06:50:33 +01:00
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: Check that memset is not used on classes
//---------------------------------------------------------------------------
2013-02-27 06:59:04 +01:00
static const Scope * findFunctionOf ( const Scope * scope )
2013-02-16 20:02:43 +01:00
{
while ( scope ) {
if ( scope - > type = = Scope : : eFunction )
return scope - > functionOf ;
scope = scope - > nestedIn ;
}
return 0 ;
}
2013-11-18 16:56:00 +01:00
void CheckClass : : checkMemset ( )
2010-01-16 08:47:46 +01:00
{
2015-04-07 07:07:08 +02:00
const bool printWarnings = _settings - > isEnabled ( " warning " ) ;
2012-10-11 06:12:24 +02:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok & & tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2015-12-24 14:40:48 +01:00
if ( Token : : Match ( tok , " memset|memcpy|memmove ( " ) ) {
2013-03-01 15:07:20 +01:00
const Token * arg1 = tok - > tokAt ( 2 ) ;
2014-07-23 16:51:23 +02:00
const Token * arg3 = arg1 - > nextArgument ( ) ;
2014-06-09 11:35:30 +02:00
if ( arg3 )
arg3 = arg3 - > nextArgument ( ) ;
2013-05-17 14:10:53 +02:00
if ( ! arg3 )
// weird, shouldn't happen: memset etc should have
// 3 arguments.
continue ;
2013-03-01 15:07:20 +01:00
2013-08-30 13:46:33 +02:00
2014-02-16 11:47:52 +01:00
const Token * typeTok = nullptr ;
const Scope * type = nullptr ;
2013-03-01 15:07:20 +01:00
if ( Token : : Match ( arg3 , " sizeof ( %type% ) ) " ) )
typeTok = arg3 - > tokAt ( 2 ) ;
else if ( Token : : Match ( arg3 , " sizeof ( %type% :: %type% ) ) " ) )
typeTok = arg3 - > tokAt ( 4 ) ;
else if ( Token : : Match ( arg3 , " sizeof ( struct %type% ) ) " ) )
typeTok = arg3 - > tokAt ( 3 ) ;
else if ( Token : : simpleMatch ( arg3 , " sizeof ( * this ) ) " ) || Token::simpleMatch(arg1, " this , " )) {
type = findFunctionOf ( arg3 - > scope ( ) ) ;
2013-03-04 11:47:29 +01:00
} else if ( Token : : Match ( arg1 , " &|*|%var% " ) ) {
2015-12-05 18:22:01 +01:00
int numIndirToVariableType = 0 ; // Offset to the actual type in terms of dereference/addressof
2013-03-04 11:47:29 +01:00
for ( ; ; arg1 = arg1 - > next ( ) ) {
if ( arg1 - > str ( ) = = " & " )
2015-08-23 19:57:58 +02:00
+ + numIndirToVariableType ;
2013-03-04 11:47:29 +01:00
else if ( arg1 - > str ( ) = = " * " )
2015-08-23 19:57:58 +02:00
- - numIndirToVariableType ;
2013-03-04 11:47:29 +01:00
else
break ;
}
2015-12-05 18:22:01 +01:00
const Variable * const var = arg1 - > variable ( ) ;
2013-03-04 11:47:29 +01:00
if ( var & & arg1 - > strAt ( 1 ) = = " , " ) {
2015-08-23 19:57:58 +02:00
if ( var - > isArrayOrPointer ( ) ) {
const Token * endTok = var - > typeEndToken ( ) ;
while ( endTok & & Token : : simpleMatch ( endTok , " * " ) ) {
2015-08-27 23:35:22 +02:00
+ + numIndirToVariableType ;
2015-08-23 19:57:58 +02:00
endTok = endTok - > previous ( ) ;
}
2013-08-31 03:03:16 +02:00
}
2013-03-04 11:47:29 +01:00
if ( var - > isArray ( ) )
2015-12-05 18:22:01 +01:00
numIndirToVariableType + = int ( var - > dimensions ( ) . size ( ) ) ;
2013-03-04 11:47:29 +01:00
2015-08-23 19:57:58 +02:00
if ( numIndirToVariableType = = 1 )
2013-03-05 13:33:38 +01:00
type = var - > typeScope ( ) ;
2013-03-04 11:47:29 +01:00
}
2013-03-01 15:07:20 +01:00
}
2011-03-13 04:41:21 +01:00
2013-03-01 15:07:20 +01:00
// No type defined => The tokens didn't match
if ( ! typeTok & & ! type )
continue ;
2012-10-10 20:42:07 +02:00
2013-03-01 15:07:20 +01:00
if ( typeTok & & typeTok - > str ( ) = = " ( " )
typeTok = typeTok - > next ( ) ;
2010-01-16 08:47:46 +01:00
2015-08-14 20:46:13 +02:00
if ( ! type & & typeTok - > type ( ) )
type = typeTok - > type ( ) - > classScope ;
2012-04-05 09:43:40 +02:00
2014-03-30 17:38:07 +02:00
if ( type ) {
2015-12-25 14:19:27 +01:00
std : : set < const Scope * > parsedTypes ;
2015-01-17 16:28:39 +01:00
checkMemsetType ( scope , tok , type , false , parsedTypes ) ;
2014-03-30 17:38:07 +02:00
}
2013-03-05 13:33:38 +01:00
} else if ( tok - > variable ( ) & & tok - > variable ( ) - > typeScope ( ) & & Token : : Match ( tok , " %var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc ( " ) ) {
2015-12-25 14:19:27 +01:00
std : : set < const Scope * > parsedTypes ;
2015-01-17 16:28:39 +01:00
checkMemsetType ( scope , tok - > tokAt ( 2 ) , tok - > variable ( ) - > typeScope ( ) , true , parsedTypes ) ;
2011-03-23 02:24:28 +01:00
2015-04-07 07:07:08 +02:00
if ( tok - > variable ( ) - > typeScope ( ) - > numConstructors > 0 & & printWarnings )
2013-03-05 13:33:38 +01:00
mallocOnClassWarning ( tok , tok - > strAt ( 2 ) , tok - > variable ( ) - > typeScope ( ) - > classDef ) ;
2013-03-01 15:07:20 +01:00
}
2011-03-13 04:41:21 +01:00
}
2010-01-16 08:47:46 +01:00
}
}
2015-12-25 14:19:27 +01:00
void CheckClass : : checkMemsetType ( const Scope * start , const Token * tok , const Scope * type , bool allocation , std : : set < const Scope * > parsedTypes )
2011-03-26 03:21:40 +01:00
{
2014-03-30 17:38:07 +02:00
// If type has been checked there is no need to check it again
2015-12-25 14:19:27 +01:00
if ( parsedTypes . find ( type ) ! = parsedTypes . end ( ) )
2014-03-30 17:38:07 +02:00
return ;
2015-12-25 14:19:27 +01:00
parsedTypes . insert ( type ) ;
2014-03-30 17:38:07 +02:00
2015-12-05 18:22:01 +01:00
const bool printPortability = _settings - > isEnabled ( " portability " ) ;
2011-03-26 03:21:40 +01:00
// recursively check all parent classes
2013-03-05 15:28:40 +01:00
for ( std : : size_t i = 0 ; i < type - > definedType - > derivedFrom . size ( ) ; i + + ) {
2014-07-23 16:51:23 +02:00
const Type * derivedFrom = type - > definedType - > derivedFrom [ i ] . type ;
if ( derivedFrom & & derivedFrom - > classScope )
checkMemsetType ( start , tok , derivedFrom - > classScope , allocation , parsedTypes ) ;
2011-03-26 03:21:40 +01:00
}
// Warn if type is a class that contains any virtual functions
std : : list < Function > : : const_iterator func ;
2011-10-13 20:53:06 +02:00
for ( func = type - > functionList . begin ( ) ; func ! = type - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( func - > isVirtual ( ) ) {
2013-03-01 15:07:20 +01:00
if ( allocation )
mallocOnClassError ( tok , tok - > str ( ) , type - > classDef , " virtual method " ) ;
else
memsetError ( tok , tok - > str ( ) , " virtual method " , type - > classDef - > str ( ) ) ;
2013-03-02 07:30:55 +01:00
}
2011-03-26 03:21:40 +01:00
}
// Warn if type is a class or struct that contains any std::* variables
std : : list < Variable > : : const_iterator var ;
2011-10-13 20:53:06 +02:00
for ( var = type - > varlist . begin ( ) ; var ! = type - > varlist . end ( ) ; + + var ) {
2014-08-08 08:08:21 +02:00
if ( var - > isReference ( ) & & ! var - > isStatic ( ) ) {
2014-03-15 18:22:29 +01:00
memsetErrorReference ( tok , tok - > str ( ) , type - > classDef - > str ( ) ) ;
continue ;
}
2012-09-17 17:59:35 +02:00
// don't warn if variable static or const, pointer or reference
2014-03-15 18:22:29 +01:00
if ( ! var - > isStatic ( ) & & ! var - > isConst ( ) & & ! var - > isPointer ( ) ) {
2011-03-26 03:21:40 +01:00
const Token * tok1 = var - > typeStartToken ( ) ;
2014-03-02 15:56:42 +01:00
const Scope * typeScope = var - > typeScope ( ) ;
2011-03-26 03:21:40 +01:00
2012-08-20 17:27:43 +02:00
// check for std:: type
2014-07-09 15:00:06 +02:00
if ( var - > isStlType ( ) & & tok1 - > strAt ( 2 ) ! = " array " & & ! _settings - > library . podtype ( tok1 - > strAt ( 2 ) ) )
2013-03-01 15:07:20 +01:00
if ( allocation )
mallocOnClassError ( tok , tok - > str ( ) , type - > classDef , " 'std:: " + tok1 - > strAt ( 2 ) + " ' " ) ;
else
memsetError ( tok , tok - > str ( ) , " 'std:: " + tok1 - > strAt ( 2 ) + " ' " , type - > classDef - > str ( ) ) ;
2011-03-26 03:21:40 +01:00
2012-08-20 17:27:43 +02:00
// check for known type
2014-03-02 15:56:42 +01:00
else if ( typeScope & & typeScope ! = type )
2014-03-30 17:38:07 +02:00
checkMemsetType ( start , tok , typeScope , allocation , parsedTypes ) ;
2014-08-08 08:08:21 +02:00
// check for float
2015-04-07 07:07:08 +02:00
else if ( tok - > str ( ) = = " memset " & & var - > isFloatingType ( ) & & printPortability )
2014-08-20 15:12:53 +02:00
memsetErrorFloat ( tok , type - > classDef - > str ( ) ) ;
2011-03-26 03:21:40 +01:00
}
}
}
2013-03-01 15:07:20 +01:00
void CheckClass : : mallocOnClassWarning ( const Token * tok , const std : : string & memfunc , const Token * classTok )
{
std : : list < const Token * > toks ;
toks . push_back ( tok ) ;
toks . push_back ( classTok ) ;
reportError ( toks , Severity : : warning , " mallocOnClassWarning " ,
" Memory for class instance allocated with " + memfunc + " (), but class provides constructors. \n "
" Memory for class instance allocated with " + memfunc + " (), but class provides constructors. This is unsafe, "
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" since no constructor is called and class members remain uninitialized. Consider using 'new' instead. " , CWE762 , false ) ;
2013-03-01 15:07:20 +01:00
}
void CheckClass : : mallocOnClassError ( const Token * tok , const std : : string & memfunc , const Token * classTok , const std : : string & classname )
{
std : : list < const Token * > toks ;
toks . push_back ( tok ) ;
toks . push_back ( classTok ) ;
reportError ( toks , Severity : : error , " mallocOnClassError " ,
" Memory for class instance allocated with " + memfunc + " (), but class contains a " + classname + " . \n "
" Memory for class instance allocated with " + memfunc + " (), but class a " + classname + " . This is unsafe, "
2016-01-25 20:01:48 +01:00
" since no constructor is called and class members remain uninitialized. Consider using 'new' instead. " , CWE665 , false ) ;
2013-03-01 15:07:20 +01:00
}
2011-03-10 08:19:31 +01:00
void CheckClass : : memsetError ( const Token * tok , const std : : string & memfunc , const std : : string & classname , const std : : string & type )
2010-11-14 06:50:33 +01:00
{
2014-03-17 13:23:23 +01:00
reportError ( tok , Severity : : error , " memsetClass " ,
" Using ' " + memfunc + " ' on " + type + " that contains a " + classname + " . \n "
" Using ' " + memfunc + " ' on " + type + " that contains a " + classname + " is unsafe, because constructor, destructor "
" and copy operator calls are omitted. These are necessary for this non-POD type to ensure that a valid object "
2016-01-25 20:01:48 +01:00
" is created. " , CWE762 , false ) ;
2010-11-14 06:50:33 +01:00
}
2010-01-16 08:47:46 +01:00
2014-03-15 18:22:29 +01:00
void CheckClass : : memsetErrorReference ( const Token * tok , const std : : string & memfunc , const std : : string & type )
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " memsetClassReference " , " Using ' " + memfunc + " ' on " + type + " that contains a reference. " , CWE665 , false ) ;
2014-08-08 08:08:21 +02:00
}
2014-08-20 15:12:53 +02:00
void CheckClass : : memsetErrorFloat ( const Token * tok , const std : : string & type )
2014-08-08 08:08:21 +02:00
{
2014-08-20 15:12:53 +02:00
reportError ( tok , Severity : : portability , " memsetClassFloat " , " Using memset() on " + type + " which contains a floating point number. \n "
2015-02-22 15:41:02 +01:00
" Using memset() on " + type + " which contains a floating point number. "
" This is not portable because memset() sets each byte of a block of memory to a specific value and "
" the actual representation of a floating-point value is implementation defined. "
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0. " , CWE758 , false ) ;
2014-03-15 18:22:29 +01:00
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
2010-09-11 08:23:30 +02:00
// ClassCheck: "void operator=(" and "const type & operator=("
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
void CheckClass : : operatorEq ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-04-21 08:38:25 +02:00
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2011-03-11 01:43:29 +01:00
std : : list < Function > : : const_iterator func ;
2010-07-26 16:46:37 +02:00
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2014-12-26 15:38:22 +01:00
if ( func - > type = = Function : : eOperatorEqual & & func - > access = = Public ) {
2014-11-28 17:50:23 +01:00
// skip "deleted" functions - cannot be called anyway
2015-01-08 05:45:31 +01:00
if ( func - > isDelete ( ) )
2014-11-25 15:52:52 +01:00
continue ;
2011-04-01 01:40:28 +02:00
// use definition for check so we don't have to deal with qualification
2015-10-02 23:23:44 +02:00
bool returnSelfRef = false ;
if ( func - > retDef - > str ( ) = = scope - > className ) {
if ( Token : : Match ( func - > retDef , " %type% & " ) ) {
returnSelfRef = true ;
} else {
// We might have "Self<template_parameters>&""
Token * tok = func - > retDef - > next ( ) ;
if ( tok & & tok - > str ( ) = = " < " & & tok - > link ( ) & & tok - > link ( ) - > next ( ) & & tok - > link ( ) - > next ( ) - > str ( ) = = " & " )
returnSelfRef = true ;
}
}
if ( ! returnSelfRef ) {
2011-08-28 17:14:15 +02:00
// make sure we really have a copy assignment operator
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( func - > tokenDef - > tokAt ( 2 ) , " const| %name% & " ) ) {
2011-08-28 17:14:15 +02:00
if ( func - > tokenDef - > strAt ( 2 ) = = " const " & &
func - > tokenDef - > strAt ( 3 ) = = scope - > className )
2013-09-03 11:40:43 +02:00
operatorEqReturnError ( func - > retDef , scope - > className ) ;
2011-08-28 17:14:15 +02:00
else if ( func - > tokenDef - > strAt ( 2 ) = = scope - > className )
2013-09-03 11:40:43 +02:00
operatorEqReturnError ( func - > retDef , scope - > className ) ;
2011-08-28 17:14:15 +02:00
}
}
2010-01-16 08:47:46 +01:00
}
}
}
}
2011-04-01 01:40:28 +02:00
void CheckClass : : operatorEqReturnError ( const Token * tok , const std : : string & className )
2010-11-14 06:50:33 +01:00
{
2014-03-28 20:29:33 +01:00
reportError ( tok , Severity : : style , " operatorEq " , " ' " + className + " ::operator=' should return ' " + className + " &'. \n "
2014-03-29 12:21:35 +01:00
" The " + className + " ::operator= does not conform to standard C/C++ behaviour. To conform to standard C/C++ behaviour, return a reference to self (such as: ' " + className + " & " + className + " ::operator=(..) { .. return *this; }'. For safety reasons it might be better to not fix this message. If you think that safety is always more important than conformance then please ignore/suppress this message. For more details about this topic, see the book \" Effective C++ \" by Scott Meyers. "
2016-02-27 16:03:50 +01:00
, CWE398 , false ) ;
2010-11-14 06:50:33 +01:00
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: "C& operator=(const C&) { ... return *this; }"
// operator= should return a reference to *this
//---------------------------------------------------------------------------
2011-03-26 03:21:40 +01:00
void CheckClass : : operatorEqRetRefThis ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-03-26 03:21:40 +01:00
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
std : : list < Function > : : const_iterator func ;
2011-03-26 03:21:40 +01:00
2012-10-10 20:42:07 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( func - > type = = Function : : eOperatorEqual & & func - > hasBody ( ) ) {
2012-10-10 20:42:07 +02:00
// make sure return signature is correct
2013-09-03 11:40:43 +02:00
if ( Token : : Match ( func - > retDef , " %type% & " ) & & func - > retDef - > str ( ) = = scope - > className ) {
2015-01-17 16:28:39 +01:00
checkReturnPtrThis ( scope , & ( * func ) , func - > functionScope - > classStart , func - > functionScope - > classEnd ) ;
2011-03-26 03:21:40 +01:00
}
}
}
}
}
2014-07-24 14:54:20 +02:00
void CheckClass : : checkReturnPtrThis ( const Scope * scope , const Function * func , const Token * tok , const Token * last )
{
std : : set < const Function * > analyzedFunctions ;
checkReturnPtrThis ( scope , func , tok , last , analyzedFunctions ) ;
}
void CheckClass : : checkReturnPtrThis ( const Scope * scope , const Function * func , const Token * tok , const Token * last , std : : set < const Function * > & analyzedFunctions )
2010-09-11 08:23:30 +02:00
{
bool foundReturn = false ;
2015-01-24 11:18:33 +01:00
const Token * const startTok = tok ;
2011-10-13 20:53:06 +02:00
for ( ; tok & & tok ! = last ; tok = tok - > next ( ) ) {
2010-09-11 08:23:30 +02:00
// check for return of reference to this
2011-10-13 20:53:06 +02:00
if ( tok - > str ( ) = = " return " ) {
2010-09-11 08:23:30 +02:00
foundReturn = true ;
2011-01-17 18:29:19 +01:00
std : : string cast ( " ( " + scope - > className + " & ) " ) ;
2011-10-27 10:41:34 +02:00
if ( Token : : simpleMatch ( tok - > next ( ) , cast . c_str ( ) ) )
2010-09-11 08:23:30 +02:00
tok = tok - > tokAt ( 4 ) ;
// check if a function is called
2011-12-08 21:28:34 +01:00
if ( tok - > strAt ( 2 ) = = " ( " & &
2011-11-20 14:22:39 +01:00
tok - > linkAt ( 2 ) - > next ( ) - > str ( ) = = " ; " ) {
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator it ;
2010-09-11 08:23:30 +02:00
// check if it is a member function
2011-10-13 20:53:06 +02:00
for ( it = scope - > functionList . begin ( ) ; it ! = scope - > functionList . end ( ) ; + + it ) {
2011-01-21 19:54:41 +01:00
// check for a regular function with the same name and a body
2015-01-08 05:45:31 +01:00
if ( it - > type = = Function : : eFunction & & it - > hasBody ( ) & &
2011-10-13 20:53:06 +02:00
it - > token - > str ( ) = = tok - > next ( ) - > str ( ) ) {
2010-09-11 08:23:30 +02:00
// check for the proper return type
if ( it - > tokenDef - > previous ( ) - > str ( ) = = " & " & &
2011-10-13 20:53:06 +02:00
it - > tokenDef - > strAt ( - 2 ) = = scope - > className ) {
2010-09-11 08:23:30 +02:00
// make sure it's not a const function
2015-01-08 05:45:31 +01:00
if ( ! it - > isConst ( ) ) {
2011-01-21 19:54:41 +01:00
/** @todo make sure argument types match */
2014-05-11 12:26:24 +02:00
// avoid endless recursions
2014-07-24 14:54:20 +02:00
if ( analyzedFunctions . find ( & * it ) = = analyzedFunctions . end ( ) ) {
analyzedFunctions . insert ( & * it ) ;
2014-05-11 12:26:24 +02:00
checkReturnPtrThis ( scope , & * it , it - > arg - > link ( ) - > next ( ) , it - > arg - > link ( ) - > next ( ) - > link ( ) ,
analyzedFunctions ) ;
}
2011-01-21 19:54:41 +01:00
// just bail for now
else
return ;
}
2010-09-11 08:23:30 +02:00
}
}
}
}
2011-02-20 02:09:07 +01:00
// check if *this is returned
2012-11-30 07:08:16 +01:00
else if ( ! ( Token : : Match ( tok - > next ( ) , " (| * this ; | = " ) ||
2011-11-20 15:09:57 +01:00
Token : : simpleMatch ( tok - > next ( ) , " operator= ( " ) | |
Token : : simpleMatch ( tok - > next ( ) , " this . operator= ( " ) | |
( Token : : Match ( tok - > next ( ) , " %type% :: operator= ( " ) & &
2011-02-20 02:02:16 +01:00
tok - > next ( ) - > str ( ) = = scope - > className ) ) )
2010-09-11 08:23:30 +02:00
operatorEqRetRefThisError ( func - > token ) ;
}
}
2015-01-24 11:18:33 +01:00
if ( foundReturn ) {
return ;
}
if ( startTok - > next ( ) = = last ) {
2015-12-03 13:04:55 +01:00
if ( Token : : simpleMatch ( func - > argDef , std : : string ( " ( const " + scope - > className + " & " ) . c_str ( ) ) ) {
2015-01-24 11:18:33 +01:00
// Typical wrong way to suppress default assignment operator by declaring it and leaving empty
operatorEqMissingReturnStatementError ( func - > token , func - > access = = Public ) ;
} else {
operatorEqMissingReturnStatementError ( func - > token , true ) ;
}
return ;
}
if ( _settings - > library . isScopeNoReturn ( last , 0 ) ) {
// Typical wrong way to prohibit default assignment operator
// by always throwing an exception or calling a noreturn function
operatorEqShouldBeLeftUnimplementedError ( func - > token ) ;
return ;
}
operatorEqMissingReturnStatementError ( func - > token , func - > access = = Public ) ;
2010-09-11 08:23:30 +02:00
}
2010-11-14 06:50:33 +01:00
void CheckClass : : operatorEqRetRefThisError ( const Token * tok )
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : style , " operatorEqRetRefThis " , " 'operator=' should return reference to 'this' instance. " , CWE398 , false ) ;
2010-11-14 06:50:33 +01:00
}
2010-01-16 08:47:46 +01:00
2015-01-24 11:18:33 +01:00
void CheckClass : : operatorEqShouldBeLeftUnimplementedError ( const Token * tok )
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : style , " operatorEqShouldBeLeftUnimplemented " , " 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented. " , CWE398 , false ) ;
2015-01-24 11:18:33 +01:00
}
void CheckClass : : operatorEqMissingReturnStatementError ( const Token * tok , bool error )
{
if ( error ) {
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : error , " operatorEqMissingReturnStatement " , " No 'return' statement in non-void function causes undefined behavior. " , CWE398 , false ) ;
2015-01-24 11:18:33 +01:00
} else {
operatorEqRetRefThisError ( tok ) ;
}
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }"
// operator= should check for assignment to self
//
// For simple classes, an assignment to self check is only a potential optimization.
//
// For classes that allocate dynamic memory, assignment to self can be a real error
// if it is deallocated and allocated again without being checked for.
//
// This check is not valid for classes with multiple inheritance because a
// class can have multiple addresses so there is no trivial way to check for
// assignment to self.
//---------------------------------------------------------------------------
2010-11-14 06:50:33 +01:00
void CheckClass : : operatorEqToSelf ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2010-11-14 06:50:33 +01:00
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2010-11-14 06:50:33 +01:00
// skip classes with multiple inheritance
2013-03-05 15:28:40 +01:00
if ( scope - > definedType - > derivedFrom . size ( ) > 1 )
2010-11-14 06:50:33 +01:00
continue ;
2014-06-09 11:35:30 +02:00
std : : list < Function > : : const_iterator func ;
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( func - > type = = Function : : eOperatorEqual & & func - > hasBody ( ) ) {
2012-04-25 20:25:51 +02:00
// make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks
if ( func - > argumentList . empty ( ) )
continue ;
const Token * typeTok = func - > argumentList . front ( ) . typeEndToken ( ) ;
while ( typeTok - > str ( ) = = " const " | | typeTok - > str ( ) = = " & " | | typeTok - > str ( ) = = " * " )
typeTok = typeTok - > previous ( ) ;
if ( typeTok - > str ( ) ! = scope - > className )
continue ;
2010-11-14 06:50:33 +01:00
// make sure return signature is correct
2013-09-03 11:40:43 +02:00
if ( Token : : Match ( func - > retDef , " %type% & " ) & & func - > retDef - > str ( ) = = scope - > className ) {
2011-04-01 01:53:35 +02:00
// find the parameter name
const Token * rhs = func - > argumentList . begin ( ) - > nameToken ( ) ;
2010-11-14 06:50:33 +01:00
2011-10-13 20:53:06 +02:00
if ( ! hasAssignSelf ( & ( * func ) , rhs ) ) {
2015-01-17 16:28:39 +01:00
if ( hasAllocation ( & ( * func ) , scope ) )
2011-04-01 01:53:35 +02:00
operatorEqToSelfError ( func - > token ) ;
2010-11-14 06:50:33 +01:00
}
}
}
}
}
}
2014-03-15 18:22:29 +01:00
bool CheckClass : : hasAllocation ( const Function * func , const Scope * scope ) const
2010-01-16 08:47:46 +01:00
{
// This function is called when no simple check was found for assignment
2012-04-05 09:43:40 +02:00
// to self. We are currently looking for:
// - deallocate member ; ... member =
// - alloc member
// That is not ideal because it can cause false negatives but its currently
// necessary to prevent false positives.
2012-05-22 21:58:46 +02:00
const Token * last = func - > functionScope - > classEnd ;
for ( const Token * tok = func - > functionScope - > classStart ; tok & & ( tok ! = last ) ; tok = tok - > next ( ) ) {
2012-04-05 09:43:40 +02:00
if ( Token : : Match ( tok , " %var% = malloc|realloc|calloc|new " ) & & isMemberVar ( scope , tok ) )
return true ;
2010-01-16 08:47:46 +01:00
2012-04-05 09:43:40 +02:00
// check for deallocating memory
2014-02-16 11:47:52 +01:00
const Token * var = nullptr ;
2012-04-05 09:43:40 +02:00
if ( Token : : Match ( tok , " free ( %var% " ) )
var = tok - > tokAt ( 2 ) ;
else if ( Token : : Match ( tok , " delete [ ] %var% " ) )
var = tok - > tokAt ( 3 ) ;
else if ( Token : : Match ( tok , " delete %var% " ) )
var = tok - > next ( ) ;
2013-01-16 15:37:07 +01:00
// Check for assignment to the deleted pointer (only if its a member of the class)
2012-04-05 09:43:40 +02:00
if ( var & & isMemberVar ( scope , var ) ) {
for ( const Token * tok1 = var - > next ( ) ; tok1 & & ( tok1 ! = last ) ; tok1 = tok1 - > next ( ) ) {
2015-01-31 12:31:34 +01:00
if ( Token : : Match ( tok1 , " %varid% = " , var - > varId ( ) ) )
return true ;
2010-01-16 08:47:46 +01:00
}
}
}
return false ;
}
2011-04-01 01:53:35 +02:00
bool CheckClass : : hasAssignSelf ( const Function * func , const Token * rhs )
2010-01-16 08:47:46 +01:00
{
2015-07-20 19:04:34 +02:00
if ( ! rhs )
return false ;
2012-05-22 21:58:46 +02:00
const Token * last = func - > functionScope - > classEnd ;
for ( const Token * tok = func - > functionScope - > classStart ; tok & & tok ! = last ; tok = tok - > next ( ) ) {
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok , " if ( " ) ) {
2010-08-28 11:23:23 +02:00
const Token * tok1 = tok - > tokAt ( 2 ) ;
2011-11-20 15:09:57 +01:00
const Token * tok2 = tok - > next ( ) - > link ( ) ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
if ( tok1 & & tok2 ) {
for ( ; tok1 & & tok1 ! = tok2 ; tok1 = tok1 - > next ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok1 , " this ==|!= & %name% " ) ) {
2011-11-13 13:10:59 +01:00
if ( tok1 - > strAt ( 3 ) = = rhs - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok1 , " & %name% ==|!= this " ) ) {
2011-11-13 13:10:59 +01:00
if ( tok1 - > strAt ( 1 ) = = rhs - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
}
}
}
}
}
return false ;
}
2010-11-14 06:50:33 +01:00
void CheckClass : : operatorEqToSelfError ( const Token * tok )
2010-01-16 08:47:46 +01:00
{
2012-07-09 11:11:05 +02:00
reportError ( tok , Severity : : warning , " operatorEqToSelf " ,
" 'operator=' should check for assignment to self to avoid problems with dynamic memory. \n "
" 'operator=' should check for assignment to self to ensure that each block of dynamically "
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
" allocated memory is owned and managed by only one instance of the class. " , CWE398 , false ) ;
2010-01-16 08:47:46 +01:00
}
//---------------------------------------------------------------------------
// A destructor in a base class should be virtual
//---------------------------------------------------------------------------
void CheckClass : : virtualDestructor ( )
{
2010-05-29 11:19:28 +02:00
// This error should only be given if:
// * base class doesn't have virtual destructor
// * derived class has non-empty destructor
// * base class is deleted
2014-06-14 12:55:20 +02:00
// unless inconclusive in which case:
// * base class has virtual members but doesn't have virtual destructor
2015-04-10 14:18:52 +02:00
const bool printInconclusive = _settings - > inconclusive ;
2014-06-14 12:55:20 +02:00
2016-01-11 11:04:52 +01:00
std : : list < const Function * > inconclusiveErrors ;
2010-05-29 11:19:28 +02:00
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2010-01-16 08:47:46 +01:00
2014-06-14 12:55:20 +02:00
// Skip base classes (unless inconclusive)
if ( scope - > definedType - > derivedFrom . empty ( ) ) {
2015-04-10 14:18:52 +02:00
if ( printInconclusive ) {
2014-06-14 12:55:20 +02:00
const Function * destructor = scope - > getDestructor ( ) ;
2015-01-08 05:45:31 +01:00
if ( destructor & & ! destructor - > isVirtual ( ) ) {
2014-06-14 12:55:20 +02:00
std : : list < Function > : : const_iterator func ;
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( func - > isVirtual ( ) ) {
2016-01-11 11:04:52 +01:00
inconclusiveErrors . push_back ( destructor ) ;
2014-06-14 12:55:20 +02:00
break ;
}
}
}
}
2010-08-13 18:34:02 +02:00
continue ;
2014-06-14 12:55:20 +02:00
}
2010-01-16 08:47:46 +01:00
2010-08-13 18:34:02 +02:00
// Find the destructor
2011-01-17 18:29:19 +01:00
const Function * destructor = scope - > getDestructor ( ) ;
2010-08-13 18:34:02 +02:00
// Check for destructor with implementation
2015-01-08 05:45:31 +01:00
if ( ! destructor | | ! destructor - > hasBody ( ) )
2010-08-13 18:34:02 +02:00
continue ;
// Empty destructor
2011-11-20 14:22:39 +01:00
if ( destructor - > token - > linkAt ( 3 ) = = destructor - > token - > tokAt ( 4 ) )
2010-08-13 18:34:02 +02:00
continue ;
2010-01-16 08:47:46 +01:00
2011-01-17 18:29:19 +01:00
const Token * derived = scope - > classDef ;
2011-11-20 15:09:57 +01:00
const Token * derivedClass = derived - > next ( ) ;
2010-01-16 08:47:46 +01:00
// Iterate through each base class...
2014-07-07 21:25:30 +02:00
for ( std : : size_t j = 0 ; j < scope - > definedType - > derivedFrom . size ( ) ; + + j ) {
2010-08-13 18:34:02 +02:00
// Check if base class is public and exists in database
2013-03-05 15:28:40 +01:00
if ( scope - > definedType - > derivedFrom [ j ] . access ! = Private & & scope - > definedType - > derivedFrom [ j ] . type ) {
const Type * derivedFrom = scope - > definedType - > derivedFrom [ j ] . type ;
const Scope * derivedFromScope = derivedFrom - > classScope ;
if ( ! derivedFromScope )
continue ;
2010-01-16 08:47:46 +01:00
2011-04-20 18:03:16 +02:00
// Check for this pattern:
// 1. Base class pointer is given the address of derived class instance
// 2. Base class pointer is deleted
//
// If this pattern is not seen then bailout the checking of these base/derived classes
{
// pointer variables of type 'Base *'
2016-01-11 11:04:52 +01:00
std : : set < unsigned int > baseClassPointers ;
2011-04-20 18:03:16 +02:00
2014-09-28 09:47:11 +02:00
for ( std : : size_t k = 1 ; k < symbolDatabase - > getVariableListSize ( ) ; k + + ) {
2013-03-05 16:07:59 +01:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( k ) ;
2013-03-05 15:28:40 +01:00
if ( var & & var - > isPointer ( ) & & var - > type ( ) = = derivedFrom )
2016-01-11 11:04:52 +01:00
baseClassPointers . insert ( var - > declarationId ( ) ) ;
2013-03-05 15:28:40 +01:00
}
2011-04-20 18:03:16 +02:00
// pointer variables of type 'Base *' that should not be deleted
std : : set < unsigned int > dontDelete ;
// No deletion of derived class instance through base class pointer found => the code is ok
bool ok = true ;
2011-04-21 01:07:28 +02:00
2011-10-13 20:53:06 +02:00
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2013-03-05 15:28:40 +01:00
if ( Token : : Match ( tok , " [;{}] %var% = " ) & &
2016-01-11 11:04:52 +01:00
baseClassPointers . find ( tok - > next ( ) - > varId ( ) ) ! = baseClassPointers . end ( ) ) {
2011-04-20 18:03:16 +02:00
// new derived class..
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok - > tokAt ( 3 ) , ( " new " + derivedClass - > str ( ) ) . c_str ( ) ) ) {
2011-04-20 18:03:16 +02:00
dontDelete . insert ( tok - > next ( ) - > varId ( ) ) ;
}
}
// Delete base class pointer that might point at derived class
2011-12-17 11:39:20 +01:00
else if ( Token : : Match ( tok , " delete %var% ; " ) & &
dontDelete . find ( tok - > next ( ) - > varId ( ) ) ! = dontDelete . end ( ) ) {
2011-04-20 18:03:16 +02:00
ok = false ;
break ;
}
}
// No base class pointer that points at a derived class is deleted
if ( ok )
continue ;
}
2010-08-13 23:57:53 +02:00
// Find the destructor declaration for the base class.
2016-01-11 11:04:52 +01:00
const Function * baseDestructor = derivedFromScope - > getDestructor ( ) ;
2010-01-16 08:47:46 +01:00
2010-08-13 23:57:53 +02:00
// Check that there is a destructor..
2016-01-11 11:04:52 +01:00
if ( ! baseDestructor ) {
2014-06-14 12:55:20 +02:00
if ( derivedFrom - > derivedFrom . empty ( ) ) {
virtualDestructorError ( derivedFrom - > classDef , derivedFrom - > name ( ) , derivedClass - > str ( ) , false ) ;
}
2016-01-11 11:04:52 +01:00
} else if ( ! baseDestructor - > isVirtual ( ) ) {
2010-08-13 23:57:53 +02:00
// TODO: This is just a temporary fix, better solution is needed.
// Skip situations where base class has base classes of its own, because
// some of the base classes might have virtual destructor.
// Proper solution is to check all of the base classes. If base class is not
// found or if one of the base classes has virtual destructor, error should not
// be printed. See TODO test case "virtualDestructorInherited"
2011-10-13 20:53:06 +02:00
if ( derivedFrom - > derivedFrom . empty ( ) ) {
2010-08-13 23:57:53 +02:00
// Make sure that the destructor is public (protected or private
// would not compile if inheritance is used in a way that would
// cause the bug we are trying to find here.)
2016-01-11 11:04:52 +01:00
if ( baseDestructor - > access = = Public ) {
virtualDestructorError ( baseDestructor - > token , derivedFrom - > name ( ) , derivedClass - > str ( ) , false ) ;
// check for duplicate error and remove it if found
std : : list < const Function * > : : iterator found = find ( inconclusiveErrors . begin ( ) , inconclusiveErrors . end ( ) , baseDestructor ) ;
if ( found ! = inconclusiveErrors . end ( ) )
inconclusiveErrors . erase ( found ) ;
2014-06-14 12:55:20 +02:00
}
2010-08-13 23:57:53 +02:00
}
}
}
2010-01-16 08:47:46 +01:00
}
}
2014-06-14 12:55:20 +02:00
2016-01-11 11:04:52 +01:00
for ( std : : list < const Function * > : : const_iterator i = inconclusiveErrors . begin ( ) ; i ! = inconclusiveErrors . end ( ) ; + + i )
2014-06-14 12:55:20 +02:00
virtualDestructorError ( ( * i ) - > tokenDef , ( * i ) - > name ( ) , " " , true ) ;
2010-01-16 08:47:46 +01:00
}
2014-06-14 12:55:20 +02:00
void CheckClass : : virtualDestructorError ( const Token * tok , const std : : string & Base , const std : : string & Derived , bool inconclusive )
2010-01-16 08:47:46 +01:00
{
2014-06-14 12:55:20 +02:00
if ( inconclusive )
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : warning , " virtualDestructor " , " Class ' " + Base + " ' which has virtual members does not have a virtual destructor. " , CWE404 , true ) ;
2014-06-14 12:55:20 +02:00
else
reportError ( tok , Severity : : error , " virtualDestructor " , " Class ' " + Base + " ' which is inherited by class ' " + Derived + " ' does not have a virtual destructor. \n "
" Class ' " + Base + " ' which is inherited by class ' " + Derived + " ' does not have a virtual destructor. "
" If you destroy instances of the derived class by deleting a pointer that points to the base class, only "
" the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class "
" could leak. This can be avoided by adding a virtual destructor to the base class. " ) ;
2010-01-16 08:47:46 +01:00
}
2010-11-14 06:50:33 +01:00
//---------------------------------------------------------------------------
// warn for "this-x". The indented code may be "this->x"
//---------------------------------------------------------------------------
2010-01-16 08:47:46 +01:00
void CheckClass : : thisSubtraction ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2010-04-21 08:38:25 +02:00
return ;
2010-01-16 08:47:46 +01:00
const Token * tok = _tokenizer - > tokens ( ) ;
2011-10-13 20:53:06 +02:00
for ( ; ; ) {
2015-01-31 10:50:39 +01:00
tok = Token : : findmatch ( tok , " this - %name% " ) ;
2010-04-02 07:30:58 +02:00
if ( ! tok )
2010-01-16 08:47:46 +01:00
break ;
2012-03-11 14:29:00 +01:00
if ( tok - > strAt ( - 1 ) ! = " * " )
2010-01-16 08:47:46 +01:00
thisSubtractionError ( tok ) ;
tok = tok - > next ( ) ;
}
}
2010-11-14 06:50:33 +01:00
void CheckClass : : thisSubtractionError ( const Token * tok )
{
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( tok , Severity : : warning , " thisSubtraction " , " Suspicious pointer subtraction. Did you intend to write '->'? " , CWE398 , false ) ;
2010-11-14 06:50:33 +01:00
}
//---------------------------------------------------------------------------
// can member function be const?
2010-03-05 17:06:25 +01:00
//---------------------------------------------------------------------------
2010-01-16 08:47:46 +01:00
2010-01-23 09:19:22 +01:00
void CheckClass : : checkConst ( )
{
2012-09-17 17:59:35 +02:00
// This is an inconclusive check. False positives: #3322.
2011-11-20 16:54:06 +01:00
if ( ! _settings - > inconclusive )
return ;
2011-12-15 20:18:52 +01:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-01-23 09:38:35 +01:00
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
2010-03-05 17:06:25 +01:00
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2010-07-26 16:46:37 +02:00
// does the function have a body?
2015-01-08 05:45:31 +01:00
if ( func - > type = = Function : : eFunction & & func - > hasBody ( ) & & ! func - > isFriend ( ) & & ! func - > isStatic ( ) & & ! func - > isVirtual ( ) ) {
2010-07-26 16:46:37 +02:00
// get last token of return type
2012-08-01 19:24:38 +02:00
const Token * previous = func - > tokenDef - > previous ( ) ;
2010-03-12 18:30:20 +01:00
2010-07-26 16:46:37 +02:00
// does the function return a pointer or reference?
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( previous , " *|& " ) ) {
2013-09-03 11:40:43 +02:00
if ( func - > retDef - > str ( ) ! = " const " )
2010-03-19 17:40:23 +01:00
continue ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( previous - > previous ( ) , " *|& > " ) ) {
2015-08-16 14:22:46 +02:00
const Token * temp = previous - > previous ( ) ;
2010-07-26 16:46:37 +02:00
2014-07-18 11:31:31 +02:00
bool foundConst = false ;
2011-10-13 20:53:06 +02:00
while ( ! Token : : Match ( temp - > previous ( ) , " ;|}|{|public:|protected:|private: " ) ) {
2010-07-26 16:46:37 +02:00
temp = temp - > previous ( ) ;
2014-07-18 11:31:31 +02:00
if ( temp - > str ( ) = = " const " ) {
foundConst = true ;
2010-04-09 19:15:39 +02:00
break ;
2014-07-18 11:31:31 +02:00
}
2010-04-09 19:15:39 +02:00
}
2014-07-18 11:31:31 +02:00
if ( ! foundConst )
2010-04-09 19:15:39 +02:00
continue ;
2015-01-08 05:45:31 +01:00
} else if ( func - > isOperator ( ) & & Token : : Match ( previous , " ; | { | } | public : | private : | protected : " )) { // Operator without return type: conversion operator
2012-08-01 19:24:38 +02:00
const std : : string & opName = func - > tokenDef - > str ( ) ;
2015-07-25 17:19:53 +02:00
if ( opName . compare ( 8 , 5 , " const " ) ! = 0 & & opName . back ( ) = = ' & ' )
2012-03-24 13:48:33 +01:00
continue ;
2011-10-13 20:53:06 +02:00
} else {
2010-07-26 16:46:37 +02:00
// don't warn for unknown types..
// LPVOID, HDC, etc
2015-08-14 20:46:13 +02:00
if ( previous - > str ( ) . size ( ) > 2 & & ! previous - > type ( ) & & previous - > isUpperCaseName ( ) )
2012-08-01 19:24:38 +02:00
continue ;
2010-03-05 17:06:25 +01:00
}
2010-01-23 20:59:20 +01:00
2010-07-26 16:46:37 +02:00
// check if base class function is virtual
2013-03-05 15:28:40 +01:00
if ( ! scope - > definedType - > derivedFrom . empty ( ) ) {
2012-04-18 18:51:38 +02:00
if ( func - > isImplicitlyVirtual ( true ) )
2010-07-26 16:46:37 +02:00
continue ;
2010-01-23 09:19:22 +01:00
}
2010-03-05 17:06:25 +01:00
2012-08-01 19:24:38 +02:00
bool memberAccessed = false ;
2010-07-26 16:46:37 +02:00
// if nothing non-const was found. write error..
2015-01-17 16:28:39 +01:00
if ( checkConstFunc ( scope , & * func , memberAccessed ) ) {
2011-01-17 18:29:19 +01:00
std : : string classname = scope - > className ;
const Scope * nest = scope - > nestedIn ;
2011-10-13 20:53:06 +02:00
while ( nest & & nest - > type ! = Scope : : eGlobal ) {
2010-07-26 16:46:37 +02:00
classname = std : : string ( nest - > className + " :: " + classname ) ;
2010-08-28 11:23:23 +02:00
nest = nest - > nestedIn ;
2010-03-28 11:46:42 +02:00
}
2010-04-18 15:40:31 +02:00
2010-09-01 06:32:46 +02:00
// get function name
2011-12-30 18:47:42 +01:00
std : : string functionName = ( func - > tokenDef - > isName ( ) ? " " : " operator " ) + func - > tokenDef - > str ( ) ;
2010-09-01 06:32:46 +02:00
2010-11-02 18:28:55 +01:00
if ( func - > tokenDef - > str ( ) = = " ( " )
2010-09-01 06:32:46 +02:00
functionName + = " ) " ;
2010-11-02 18:28:55 +01:00
else if ( func - > tokenDef - > str ( ) = = " [ " )
2010-09-01 06:32:46 +02:00
functionName + = " ] " ;
2015-01-08 05:45:31 +01:00
if ( ! func - > isConst ( ) | | ( ! memberAccessed & & ! func - > isOperator ( ) ) ) {
if ( func - > isInline ( ) )
checkConstError ( func - > token , classname , functionName , ! memberAccessed & & ! func - > isOperator ( ) ) ;
2012-08-01 19:24:38 +02:00
else // not inline
2015-01-08 05:45:31 +01:00
checkConstError2 ( func - > token , func - > tokenDef , classname , functionName , ! memberAccessed & & ! func - > isOperator ( ) ) ;
2012-08-01 19:24:38 +02:00
}
2010-04-18 15:40:31 +02:00
}
2010-03-28 11:46:42 +02:00
}
}
}
}
2014-03-15 18:22:29 +01:00
bool CheckClass : : isMemberVar ( const Scope * scope , const Token * tok ) const
2011-01-16 11:18:12 +01:00
{
2011-03-27 19:59:12 +02:00
bool again = false ;
2011-01-16 11:18:12 +01:00
2011-03-27 19:59:12 +02:00
// try to find the member variable
2011-10-13 20:53:06 +02:00
do {
2011-03-27 19:59:12 +02:00
again = false ;
2011-01-16 11:18:12 +01:00
2011-10-13 20:53:06 +02:00
if ( tok - > str ( ) = = " this " ) {
2011-03-27 19:59:12 +02:00
return true ;
2011-10-27 10:41:34 +02:00
} else if ( Token : : simpleMatch ( tok - > tokAt ( - 3 ) , " ( * this ) " ) ) {
2011-08-28 15:21:00 +02:00
return true ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok - > tokAt ( - 2 ) , " %name% . %name% " ) ) {
2011-03-27 19:59:12 +02:00
tok = tok - > tokAt ( - 2 ) ;
again = true ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok - > tokAt ( - 2 ) , " ] . %name% " ) ) {
2011-11-20 14:22:39 +01:00
tok = tok - > linkAt ( - 2 ) - > previous ( ) ;
2011-03-27 19:59:12 +02:00
again = true ;
2011-10-13 20:53:06 +02:00
} else if ( tok - > str ( ) = = " ] " ) {
2011-03-27 19:59:12 +02:00
tok = tok - > link ( ) - > previous ( ) ;
again = true ;
}
2011-10-13 20:53:06 +02:00
} while ( again ) ;
2011-01-16 11:18:12 +01:00
2011-01-17 18:29:19 +01:00
std : : list < Variable > : : const_iterator var ;
2011-10-13 20:53:06 +02:00
for ( var = scope - > varlist . begin ( ) ; var ! = scope - > varlist . end ( ) ; + + var ) {
if ( var - > name ( ) = = tok - > str ( ) ) {
2011-03-27 20:27:14 +02:00
if ( tok - > varId ( ) = = 0 )
symbolDatabase - > debugMessage ( tok , " CheckClass::isMemberVar found used member variable \' " + tok - > str ( ) + " \' with varid 0 " ) ;
2012-08-01 19:24:38 +02:00
return ! var - > isStatic ( ) ;
2011-01-16 11:18:12 +01:00
}
}
// not found in this class
2015-07-01 07:50:13 +02:00
if ( ! scope - > definedType - > derivedFrom . empty ( ) ) {
2011-01-16 11:18:12 +01:00
// check each base class
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < scope - > definedType - > derivedFrom . size ( ) ; + + i ) {
2011-01-16 11:18:12 +01:00
// find the base class
2013-03-05 15:28:40 +01:00
const Type * derivedFrom = scope - > definedType - > derivedFrom [ i ] . type ;
2011-01-16 11:18:12 +01:00
// find the function in the base class
2013-03-05 15:28:40 +01:00
if ( derivedFrom & & derivedFrom - > classScope ) {
if ( isMemberVar ( derivedFrom - > classScope , tok ) )
2011-01-16 11:18:12 +01:00
return true ;
}
}
}
return false ;
}
2014-03-15 18:22:29 +01:00
bool CheckClass : : isMemberFunc ( const Scope * scope , const Token * tok ) const
2012-05-16 20:57:12 +02:00
{
2014-11-20 06:18:29 +01:00
if ( tok - > function ( ) & & tok - > function ( ) - > nestedIn = = scope )
2015-01-08 05:45:31 +01:00
return ! tok - > function ( ) - > isStatic ( ) ;
2012-05-16 20:57:12 +02:00
// not found in this class
2013-03-05 15:28:40 +01:00
if ( ! scope - > definedType - > derivedFrom . empty ( ) ) {
2012-05-16 20:57:12 +02:00
// check each base class
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < scope - > definedType - > derivedFrom . size ( ) ; + + i ) {
2012-05-16 20:57:12 +02:00
// find the base class
2013-03-05 15:28:40 +01:00
const Type * derivedFrom = scope - > definedType - > derivedFrom [ i ] . type ;
2012-05-16 20:57:12 +02:00
// find the function in the base class
2015-07-01 07:50:13 +02:00
if ( derivedFrom & & derivedFrom - > classScope ) {
2013-03-05 15:28:40 +01:00
if ( isMemberFunc ( derivedFrom - > classScope , tok ) )
2012-05-16 20:57:12 +02:00
return true ;
}
}
}
return false ;
}
2014-03-15 18:22:29 +01:00
bool CheckClass : : isConstMemberFunc ( const Scope * scope , const Token * tok ) const
2011-01-16 11:18:12 +01:00
{
2014-11-20 06:18:29 +01:00
if ( tok - > function ( ) & & tok - > function ( ) - > nestedIn = = scope )
2015-01-08 05:45:31 +01:00
return tok - > function ( ) - > isConst ( ) ;
2011-08-23 02:34:00 +02:00
2011-01-16 11:18:12 +01:00
// not found in this class
2013-03-05 15:28:40 +01:00
if ( ! scope - > definedType - > derivedFrom . empty ( ) ) {
2011-01-16 11:18:12 +01:00
// check each base class
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < scope - > definedType - > derivedFrom . size ( ) ; + + i ) {
2011-01-16 11:18:12 +01:00
// find the base class
2013-03-05 15:28:40 +01:00
const Type * derivedFrom = scope - > definedType - > derivedFrom [ i ] . type ;
2011-01-16 11:18:12 +01:00
// find the function in the base class
2015-07-01 07:50:13 +02:00
if ( derivedFrom & & derivedFrom - > classScope ) {
2013-03-05 15:28:40 +01:00
if ( isConstMemberFunc ( derivedFrom - > classScope , tok ) )
2011-01-16 11:18:12 +01:00
return true ;
}
}
}
return false ;
}
2015-06-13 16:22:43 +02:00
namespace {
// The container contains the STL types whose operator[] is not a const.
2015-11-30 22:13:49 +01:00
const std : : set < std : : string > stl_containers_not_const = make_container < std : : set < std : : string > > ( ) < < " map " < < " unordered_map " ;
2015-06-13 16:22:43 +02:00
}
2014-05-10 11:49:14 +02:00
bool CheckClass : : checkConstFunc ( const Scope * scope , const Function * func , bool & memberAccessed ) const
2011-01-16 11:18:12 +01:00
{
// if the function doesn't have any assignment nor function call,
// it can be a const function..
2012-05-22 21:58:46 +02:00
for ( const Token * tok1 = func - > functionScope - > classStart ; tok1 & & tok1 ! = func - > functionScope - > classEnd ; tok1 = tok1 - > next ( ) ) {
2012-08-01 19:24:38 +02:00
if ( tok1 - > isName ( ) & & isMemberVar ( scope , tok1 ) ) {
memberAccessed = true ;
2013-02-02 16:30:17 +01:00
const Variable * v = tok1 - > variable ( ) ;
2012-08-01 19:24:38 +02:00
if ( v & & v - > isMutable ( ) )
continue ;
2011-01-16 11:18:12 +01:00
2012-08-01 19:24:38 +02:00
if ( tok1 - > str ( ) = = " this " & & tok1 - > previous ( ) - > isAssignmentOp ( ) )
2015-12-05 18:22:01 +01:00
return false ;
2011-09-17 00:07:25 +02:00
2014-11-02 13:38:03 +01:00
2015-08-28 14:19:24 +02:00
const Token * lhs = tok1 - > previous ( ) ;
2014-11-02 13:38:03 +01:00
if ( lhs - > str ( ) = = " & " ) {
lhs = lhs - > previous ( ) ;
2015-08-14 20:46:13 +02:00
if ( lhs - > tokType ( ) = = Token : : eAssignmentOp & & lhs - > previous ( ) - > variable ( ) ) {
2014-11-02 13:38:03 +01:00
if ( lhs - > previous ( ) - > variable ( ) - > typeStartToken ( ) - > strAt ( - 1 ) ! = " const " & & lhs - > previous ( ) - > variable ( ) - > isPointer ( ) )
return false ;
}
2015-10-26 18:47:44 +01:00
} else if ( lhs - > str ( ) = = " : " & & lhs - > astParent ( ) & & lhs - > astParent ( ) - > str ( ) = = " ( " & & tok1 - > strAt ( 1 ) = = " ) " ) { // range-based for-loop (C++11)
// TODO: We could additionally check what is done with the elements to avoid false negatives. Here we just rely on "const" keyword being used.
if ( lhs - > astParent ( ) - > strAt ( 1 ) ! = " const " )
return false ;
2014-11-02 13:38:03 +01:00
} else {
2015-12-24 15:08:49 +01:00
if ( lhs - > tokType ( ) = = Token : : eAssignmentOp ) {
const Variable * lhsVar = lhs - > previous ( ) - > variable ( ) ;
if ( lhsVar & & ! lhsVar - > isConst ( ) & & lhsVar - > isReference ( ) & & lhs = = lhsVar - > nameToken ( ) - > next ( ) )
2014-11-02 13:38:03 +01:00
return false ;
2015-12-24 15:08:49 +01:00
}
2014-11-02 13:38:03 +01:00
}
2015-06-13 16:22:43 +02:00
const Token * jumpBackToken = nullptr ;
2013-02-02 16:30:17 +01:00
const Token * lastVarTok = tok1 ;
const Token * end = tok1 ;
2012-08-01 19:24:38 +02:00
for ( ; ; ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( end - > next ( ) , " . %name% " ) ) {
2012-08-01 19:24:38 +02:00
end = end - > tokAt ( 2 ) ;
if ( end - > varId ( ) )
2013-02-02 16:30:17 +01:00
lastVarTok = end ;
2012-08-01 19:24:38 +02:00
} else if ( end - > strAt ( 1 ) = = " [ " ) {
if ( end - > varId ( ) ) {
2013-02-02 16:30:17 +01:00
const Variable * var = end - > variable ( ) ;
2015-06-13 16:22:43 +02:00
if ( var & & var - > isStlType ( stl_containers_not_const ) )
2014-01-30 05:26:48 +01:00
return false ;
2012-08-01 19:24:38 +02:00
}
2013-02-16 11:20:18 +01:00
if ( ! jumpBackToken )
jumpBackToken = end - > next ( ) ; // Check inside the [] brackets
2012-08-01 19:24:38 +02:00
end = end - > linkAt ( 1 ) ;
} else if ( end - > strAt ( 1 ) = = " ) " )
end = end - > next ( ) ;
else
break ;
2011-03-20 18:29:52 +01:00
}
2011-03-26 03:37:32 +01:00
2012-08-01 19:24:38 +02:00
if ( end - > strAt ( 1 ) = = " ( " ) {
2013-02-02 16:30:17 +01:00
const Variable * var = lastVarTok - > variable ( ) ;
2012-08-01 19:24:38 +02:00
if ( ! var )
2014-01-30 05:26:48 +01:00
return false ;
if ( var - > isStlType ( ) // assume all std::*::size() and std::*::empty() are const
2012-08-01 19:24:38 +02:00
& & ( Token : : Match ( end , " size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( ) " ) | | Token : : Match ( end , " rfind|copy " ) ) )
;
2013-03-05 13:33:38 +01:00
else if ( ! var - > typeScope ( ) | | ! isConstMemberFunc ( var - > typeScope ( ) , end ) )
2015-12-05 18:22:01 +01:00
return false ;
2011-03-20 18:29:52 +01:00
}
2011-01-16 11:18:12 +01:00
2012-08-01 19:24:38 +02:00
// Assignment
2015-08-14 20:46:13 +02:00
else if ( end - > next ( ) - > tokType ( ) = = Token : : eAssignmentOp )
2015-12-05 18:22:01 +01:00
return false ;
2011-06-04 04:00:27 +02:00
2012-08-01 19:24:38 +02:00
// Streaming
else if ( end - > strAt ( 1 ) = = " << " & & tok1 - > strAt ( - 1 ) ! = " << " )
2015-12-05 18:22:01 +01:00
return false ;
2012-08-01 19:24:38 +02:00
else if ( tok1 - > strAt ( - 1 ) = = " >> " )
2015-12-05 18:22:01 +01:00
return false ;
2012-08-01 19:24:38 +02:00
// ++/--
2015-08-14 20:46:13 +02:00
else if ( end - > next ( ) - > tokType ( ) = = Token : : eIncDecOp | | tok1 - > previous ( ) - > tokType ( ) = = Token : : eIncDecOp )
2015-12-05 18:22:01 +01:00
return false ;
2012-08-01 19:24:38 +02:00
const Token * start = tok1 ;
while ( tok1 - > strAt ( - 1 ) = = " ) " )
tok1 = tok1 - > linkAt ( - 1 ) ;
if ( start - > strAt ( - 1 ) = = " delete " )
2015-12-05 18:22:01 +01:00
return false ;
2012-08-01 19:24:38 +02:00
2013-02-16 11:20:18 +01:00
tok1 = jumpBackToken ? jumpBackToken : end ; // Jump back to first [ to check inside, or jump to end of expression
2012-08-01 19:24:38 +02:00
}
// streaming: <<
else if ( Token : : simpleMatch ( tok1 - > previous ( ) , " ) << " ) & &
isMemberVar ( scope , tok1 - > tokAt ( - 2 ) ) ) {
2013-02-02 16:30:17 +01:00
const Variable * var = tok1 - > tokAt ( - 2 ) - > variable ( ) ;
2012-08-01 19:24:38 +02:00
if ( ! var | | ! var - > isMutable ( ) )
2015-12-05 18:22:01 +01:00
return false ;
2011-06-04 04:00:27 +02:00
}
2012-08-01 19:24:38 +02:00
2011-01-16 11:18:12 +01:00
// function call..
2015-01-31 10:50:39 +01:00
else if ( Token : : Match ( tok1 , " %name% ( " ) & & ! tok1 - > isStandardType ( ) & &
2012-04-18 18:51:38 +02:00
! Token : : Match ( tok1 , " return|if|string|switch|while|catch|for " ) ) {
2012-08-01 19:24:38 +02:00
if ( isMemberFunc ( scope , tok1 ) & & tok1 - > strAt ( - 1 ) ! = " . " ) {
if ( ! isConstMemberFunc ( scope , tok1 ) )
2015-12-05 18:22:01 +01:00
return false ;
2012-08-01 19:24:38 +02:00
memberAccessed = true ;
2011-12-15 20:18:52 +01:00
}
// Member variable given as parameter
for ( const Token * tok2 = tok1 - > tokAt ( 2 ) ; tok2 & & tok2 ! = tok1 - > next ( ) - > link ( ) ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ( " )
tok2 = tok2 - > link ( ) ;
2012-08-01 19:24:38 +02:00
else if ( tok2 - > isName ( ) & & isMemberVar ( scope , tok2 ) ) {
2013-02-02 16:30:17 +01:00
const Variable * var = tok2 - > variable ( ) ;
2012-08-01 19:24:38 +02:00
if ( ! var | | ! var - > isMutable ( ) )
2015-12-05 18:22:01 +01:00
return false ; // TODO: Only bailout if function takes argument as non-const reference
2012-08-01 19:24:38 +02:00
}
2011-01-16 11:18:12 +01:00
}
2012-07-11 17:45:16 +02:00
} else if ( Token : : simpleMatch ( tok1 , " > ( " ) & & ( ! tok1 - > link ( ) | | ! Token : : Match ( tok1 - > link ( ) - > previous ( ) , " static_cast|const_cast|dynamic_cast|reinterpret_cast " ) ) ) {
2015-12-05 18:22:01 +01:00
return false ;
2011-01-16 11:18:12 +01:00
}
}
2015-12-05 18:22:01 +01:00
return true ;
2011-01-16 11:18:12 +01:00
}
2012-08-01 19:24:38 +02:00
void CheckClass : : checkConstError ( const Token * tok , const std : : string & classname , const std : : string & funcname , bool suggestStatic )
2010-01-23 09:19:22 +01:00
{
2012-08-01 19:24:38 +02:00
checkConstError2 ( tok , 0 , classname , funcname , suggestStatic ) ;
2010-01-23 09:19:22 +01:00
}
2012-08-01 19:24:38 +02:00
void CheckClass : : checkConstError2 ( const Token * tok1 , const Token * tok2 , const std : : string & classname , const std : : string & funcname , bool suggestStatic )
2010-03-10 07:47:01 +01:00
{
std : : list < const Token * > toks ;
toks . push_back ( tok1 ) ;
2012-07-09 11:11:05 +02:00
if ( tok2 )
toks . push_back ( tok2 ) ;
2012-08-01 19:24:38 +02:00
if ( ! suggestStatic )
reportError ( toks , Severity : : style , " functionConst " ,
" Technically the member function ' " + classname + " :: " + funcname + " ' can be const. \n "
" The member function ' " + classname + " :: " + funcname + " ' can be made a const "
" function. Making this function 'const' should not cause compiler errors. "
" Even though the function can be made const function technically it may not make "
" sense conceptually. Think about your design and the task of the function first - is "
2016-01-25 20:01:48 +01:00
" it a function that must not change object internal state? " , CWE ( 0U ) , true ) ;
2012-08-01 19:24:38 +02:00
else
reportError ( toks , Severity : : performance , " functionStatic " ,
" Technically the member function ' " + classname + " :: " + funcname + " ' can be static. \n "
" The member function ' " + classname + " :: " + funcname + " ' can be made a static "
" function. Making a function static can bring a performance benefit since no 'this' instance is "
" passed to the function. This change should not cause compiler errors but it does not "
" necessarily make sense conceptually. Think about your design and the task of the function first - "
2016-01-25 20:01:48 +01:00
" is it a function that must not access members of class instances? " , CWE ( 0U ) , true ) ;
2010-03-10 07:47:01 +01:00
}
2011-09-28 03:07:37 +02:00
//---------------------------------------------------------------------------
// ClassCheck: Check that initializer list is in declared order.
//---------------------------------------------------------------------------
2015-09-08 15:11:17 +02:00
namespace { // avoid one-definition-rule violation
struct VarInfo {
VarInfo ( const Variable * _var , const Token * _tok )
: var ( _var ) , tok ( _tok ) { }
const Variable * var ;
const Token * tok ;
} ;
}
2011-09-28 03:07:37 +02:00
2012-05-18 16:54:58 +02:00
void CheckClass : : initializerListOrder ( )
2011-09-28 03:07:37 +02:00
{
if ( ! _settings - > isEnabled ( " style " ) )
return ;
// This check is not inconclusive. However it only determines if the initialization
// order is incorrect. It does not determine if being out of order causes
// a real error. Out of order is not necessarily an error but you can never
// have an error if the list is in order so this enforces defensive programming.
if ( ! _settings - > inconclusive )
return ;
2012-10-11 06:12:24 +02:00
const std : : size_t classes = symbolDatabase - > classAndStructScopes . size ( ) ;
2012-10-10 20:42:07 +02:00
for ( std : : size_t i = 0 ; i < classes ; + + i ) {
2015-03-23 09:58:56 +01:00
const Scope * scope = symbolDatabase - > classAndStructScopes [ i ] ;
2011-09-28 03:07:37 +02:00
std : : list < Function > : : const_iterator func ;
// iterate through all member functions looking for constructors
2015-03-23 09:58:56 +01:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2015-01-08 05:45:31 +01:00
if ( ( func - > isConstructor ( ) ) & & func - > hasBody ( ) ) {
2011-09-28 03:07:37 +02:00
// check for initializer list
const Token * tok = func - > arg - > link ( ) - > next ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok - > str ( ) = = " : " ) {
2011-09-28 03:07:37 +02:00
std : : vector < VarInfo > vars ;
tok = tok - > next ( ) ;
// find all variable initializations in list
2014-08-05 16:11:42 +02:00
while ( tok & & tok ! = func - > functionScope - > classStart ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% (|{ " ) ) {
2015-03-23 09:58:56 +01:00
const Variable * var = scope - > getVariable ( tok - > str ( ) ) ;
2011-09-28 03:07:37 +02:00
if ( var )
vars . push_back ( VarInfo ( var , tok ) ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok - > tokAt ( 2 ) , " %name% = " ) ) {
2015-03-23 09:58:56 +01:00
var = scope - > getVariable ( tok - > strAt ( 2 ) ) ;
2011-09-28 03:07:37 +02:00
if ( var )
vars . push_back ( VarInfo ( var , tok - > tokAt ( 2 ) ) ) ;
}
2012-03-24 10:50:19 +01:00
tok = tok - > next ( ) - > link ( ) - > next ( ) ;
} else
tok = tok - > next ( ) ;
2011-09-28 03:07:37 +02:00
}
// need at least 2 members to have out of order initialization
2012-10-10 20:42:07 +02:00
for ( std : : size_t j = 1 ; j < vars . size ( ) ; j + + ) {
2012-03-24 10:50:19 +01:00
// check for out of order initialization
2012-10-10 20:42:07 +02:00
if ( vars [ j ] . var - > index ( ) < vars [ j - 1 ] . var - > index ( ) )
2015-03-23 09:58:56 +01:00
initializerListError ( vars [ j ] . tok , vars [ j ] . var - > nameToken ( ) , scope - > className , vars [ j ] . var - > name ( ) ) ;
2011-09-28 03:07:37 +02:00
}
}
}
}
}
}
void CheckClass : : initializerListError ( const Token * tok1 , const Token * tok2 , const std : : string & classname , const std : : string & varname )
{
std : : list < const Token * > toks ;
toks . push_back ( tok1 ) ;
toks . push_back ( tok2 ) ;
2012-05-06 19:37:41 +02:00
reportError ( toks , Severity : : style , " initializerList " ,
" Member variable ' " + classname + " :: " +
2012-07-09 11:11:05 +02:00
varname + " ' is in the wrong place in the initializer list. \n "
" Member variable ' " + classname + " :: " +
varname + " ' is in the wrong place in the initializer list. "
" Members are initialized in the order they are declared, not in the "
2012-05-06 19:37:41 +02:00
" order they are in the initializer list. Keeping the initializer list "
" in the same order that the members were declared prevents order dependent "
2016-01-25 20:01:48 +01:00
" initialization errors. " , CWE ( 0U ) , true ) ;
2011-09-28 03:07:37 +02:00
}
2013-03-30 15:09:22 +01:00
2014-08-05 11:48:53 +02:00
//---------------------------------------------------------------------------
// Check for self initialization in initialization list
//---------------------------------------------------------------------------
void CheckClass : : checkSelfInitialization ( )
{
for ( std : : size_t i = 0 ; i < symbolDatabase - > functionScopes . size ( ) ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
const Function * function = scope - > function ;
if ( ! function | | ! function - > isConstructor ( ) )
continue ;
const Token * tok = function - > arg - > link ( ) - > next ( ) ;
if ( tok - > str ( ) ! = " : " )
continue ;
for ( ; tok ! = scope - > classStart ; tok = tok - > next ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " [:,] %var% (|{ %var% )|} " ) & & tok - > next ( ) - > varId ( ) = = tok - > tokAt ( 3 ) - > varId ( ) ) {
2014-08-05 11:48:53 +02:00
selfInitializationError ( tok , tok - > strAt ( 1 ) ) ;
}
}
}
}
2014-08-06 13:35:39 +02:00
void CheckClass : : selfInitializationError ( const Token * tok , const std : : string & varname )
2014-08-05 11:48:53 +02:00
{
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " selfInitialization " , " Member variable ' " + varname + " ' is initialized by itself. " , CWE665 , false ) ;
2014-08-05 11:48:53 +02:00
}
//---------------------------------------------------------------------------
// Check for pure virtual function calls
//---------------------------------------------------------------------------
2013-03-30 15:09:22 +01:00
void CheckClass : : checkPureVirtualFunctionCall ( )
{
2015-04-06 13:34:44 +02:00
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
2013-03-30 15:09:22 +01:00
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
std : : map < const Function * , std : : list < const Token * > > callsPureVirtualFunctionMap ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
2015-06-13 16:22:43 +02:00
if ( scope - > function = = nullptr | | ! scope - > function - > hasBody ( ) | |
2013-04-10 21:57:22 +02:00
! ( scope - > function - > isConstructor ( ) | |
scope - > function - > isDestructor ( ) ) )
2013-03-30 15:09:22 +01:00
continue ;
const std : : list < const Token * > & pureVirtualFunctionCalls = callsPureVirtualFunction ( * scope - > function , callsPureVirtualFunctionMap ) ;
for ( std : : list < const Token * > : : const_iterator pureCallIter = pureVirtualFunctionCalls . begin ( ) ;
pureCallIter ! = pureVirtualFunctionCalls . end ( ) ;
+ + pureCallIter ) {
const Token & pureCall = * * pureCallIter ;
std : : list < const Token * > pureFuncStack ;
pureFuncStack . push_back ( & pureCall ) ;
getFirstPureVirtualFunctionCallStack ( callsPureVirtualFunctionMap , pureCall , pureFuncStack ) ;
if ( ! pureFuncStack . empty ( ) )
callsPureVirtualFunctionError ( * scope - > function , pureFuncStack , pureFuncStack . back ( ) - > str ( ) ) ;
}
}
}
const std : : list < const Token * > & CheckClass : : callsPureVirtualFunction ( const Function & function ,
std : : map < const Function * , std : : list < const Token * > > & callsPureVirtualFunctionMap )
{
2014-08-05 11:48:53 +02:00
std : : pair < std : : map < const Function * , std : : list < const Token * > > : : iterator , bool > found =
callsPureVirtualFunctionMap . insert ( std : : pair < const Function * , std : : list < const Token * > > ( & function , std : : list < const Token * > ( ) ) ) ;
std : : list < const Token * > & pureFunctionCalls = found . first - > second ;
2013-03-30 15:09:22 +01:00
if ( found . second ) {
2015-01-08 05:45:31 +01:00
if ( function . hasBody ( ) ) {
2013-03-30 15:09:22 +01:00
for ( const Token * tok = function . arg - > link ( ) ;
2013-09-26 17:25:16 +02:00
tok & & tok ! = function . functionScope - > classEnd ;
2013-03-30 15:09:22 +01:00
tok = tok - > next ( ) ) {
2013-09-27 09:25:38 +02:00
if ( function . type ! = Function : : eConstructor & &
function . type ! = Function : : eCopyConstructor & &
function . type ! = Function : : eMoveConstructor & &
function . type ! = Function : : eDestructor ) {
2014-08-05 11:48:53 +02:00
if ( ( Token : : simpleMatch ( tok , " ) { " ) & &
2013-09-27 09:25:38 +02:00
tok - > link ( ) & &
2014-08-05 11:48:53 +02:00
Token : : Match ( tok - > link ( ) - > previous ( ) , " if|switch " ) ) | |
Token : : simpleMatch ( tok , " else { " )
2013-09-27 09:25:38 +02:00
) {
// Assume pure virtual function call is prevented by "if|else|switch" condition
tok = tok - > linkAt ( 1 ) ;
continue ;
}
2013-09-26 17:25:16 +02:00
}
2015-11-09 20:15:26 +01:00
if ( tok - > scope ( ) - > type = = Scope : : eLambda )
tok = tok - > scope ( ) - > classEnd - > next ( ) ;
2014-08-05 11:48:53 +02:00
const Function * callFunction = tok - > function ( ) ;
2013-03-30 15:09:22 +01:00
if ( ! callFunction | |
function . nestedIn ! = callFunction - > nestedIn | |
2014-08-05 11:48:53 +02:00
( tok - > previous ( ) & & tok - > previous ( ) - > str ( ) = = " . " ) )
2013-03-30 15:09:22 +01:00
continue ;
2013-10-20 14:09:10 +02:00
if ( tok - > previous ( ) & &
2014-08-05 11:48:53 +02:00
tok - > previous ( ) - > str ( ) = = " ( " ) {
2013-10-20 14:09:10 +02:00
const Token * prev = tok - > previous ( ) ;
if ( prev - > previous ( ) & &
( _settings - > library . ignorefunction ( tok - > str ( ) )
| | _settings - > library . ignorefunction ( prev - > previous ( ) - > str ( ) ) ) )
continue ;
}
2013-03-30 15:09:22 +01:00
if ( isPureWithoutBody ( * callFunction ) ) {
pureFunctionCalls . push_back ( tok ) ;
continue ;
}
2014-08-05 11:48:53 +02:00
const std : : list < const Token * > & pureFunctionCallsOfTok = callsPureVirtualFunction ( * callFunction ,
2013-03-30 15:09:22 +01:00
callsPureVirtualFunctionMap ) ;
if ( ! pureFunctionCallsOfTok . empty ( ) ) {
pureFunctionCalls . push_back ( tok ) ;
continue ;
}
}
}
}
return pureFunctionCalls ;
}
void CheckClass : : getFirstPureVirtualFunctionCallStack (
std : : map < const Function * , std : : list < const Token * > > & callsPureVirtualFunctionMap ,
const Token & pureCall ,
std : : list < const Token * > & pureFuncStack )
{
if ( isPureWithoutBody ( * pureCall . function ( ) ) ) {
pureFuncStack . push_back ( pureCall . function ( ) - > token ) ;
return ;
}
2014-08-05 11:48:53 +02:00
std : : map < const Function * , std : : list < const Token * > > : : const_iterator found = callsPureVirtualFunctionMap . find ( pureCall . function ( ) ) ;
if ( found = = callsPureVirtualFunctionMap . end ( ) | |
2013-03-30 15:09:22 +01:00
found - > second . empty ( ) ) {
pureFuncStack . clear ( ) ;
return ;
}
2014-08-05 11:48:53 +02:00
const Token & firstPureCall = * * found - > second . begin ( ) ;
2013-03-30 15:09:22 +01:00
pureFuncStack . push_back ( & firstPureCall ) ;
getFirstPureVirtualFunctionCallStack ( callsPureVirtualFunctionMap , firstPureCall , pureFuncStack ) ;
}
void CheckClass : : callsPureVirtualFunctionError (
const Function & scopeFunction ,
const std : : list < const Token * > & tokStack ,
const std : : string & purefuncname )
{
2014-08-05 11:48:53 +02:00
const char * scopeFunctionTypeName = getFunctionTypeName ( scopeFunction . type ) ;
2013-03-30 15:09:22 +01:00
reportError ( tokStack , Severity : : warning , " pureVirtualCall " , " Call of pure virtual function ' " + purefuncname + " ' in " + scopeFunctionTypeName + " . \n "
2016-01-25 20:01:48 +01:00
" Call of pure virtual function ' " + purefuncname + " ' in " + scopeFunctionTypeName + " . The call will fail during runtime. " , CWE ( 0U ) , false ) ;
2013-03-30 15:09:22 +01:00
}
2014-08-05 11:48:53 +02:00
//---------------------------------------------------------------------------
// Check for members hiding inherited members with the same name
//---------------------------------------------------------------------------
void CheckClass : : checkDuplInheritedMembers ( )
{
if ( ! _settings - > isEnabled ( " warning " ) )
return ;
// Iterate over all classes
for ( std : : list < Type > : : const_iterator classIt = symbolDatabase - > typeList . begin ( ) ;
classIt ! = symbolDatabase - > typeList . end ( ) ;
+ + classIt ) {
// Iterate over the parent classes
for ( std : : vector < Type : : BaseInfo > : : const_iterator parentClassIt = classIt - > derivedFrom . begin ( ) ;
parentClassIt ! = classIt - > derivedFrom . end ( ) ;
+ + parentClassIt ) {
// Check if there is info about the 'Base' class
if ( ! parentClassIt - > type | | ! parentClassIt - > type - > classScope )
continue ;
// Check if they have a member variable in common
for ( std : : list < Variable > : : const_iterator classVarIt = classIt - > classScope - > varlist . begin ( ) ;
classVarIt ! = classIt - > classScope - > varlist . end ( ) ;
+ + classVarIt ) {
for ( std : : list < Variable > : : const_iterator parentClassVarIt = parentClassIt - > type - > classScope - > varlist . begin ( ) ;
parentClassVarIt ! = parentClassIt - > type - > classScope - > varlist . end ( ) ;
+ + parentClassVarIt ) {
if ( classVarIt - > name ( ) = = parentClassVarIt - > name ( ) & & ! parentClassVarIt - > isPrivate ( ) ) { // Check if the class and its parent have a common variable
duplInheritedMembersError ( classVarIt - > nameToken ( ) , parentClassVarIt - > nameToken ( ) ,
classIt - > name ( ) , parentClassIt - > type - > name ( ) , classVarIt - > name ( ) ,
classIt - > classScope - > type = = Scope : : eStruct ,
parentClassIt - > type - > classScope - > type = = Scope : : eStruct ) ;
}
}
}
}
}
}
void CheckClass : : duplInheritedMembersError ( const Token * tok1 , const Token * tok2 ,
const std : : string & derivedname , const std : : string & basename ,
const std : : string & variablename , bool derivedIsStruct , bool baseIsStruct )
{
std : : list < const Token * > toks ;
toks . push_back ( tok1 ) ;
toks . push_back ( tok2 ) ;
const std : : string message = " The " + std : : string ( derivedIsStruct ? " struct " : " class " ) + " ' " + derivedname +
" ' defines member variable with name ' " + variablename + " ' also defined in its parent " +
std : : string ( baseIsStruct ? " struct " : " class " ) + " ' " + basename + " '. " ;
Mapped toomanyconfigs ,AssignmentAddressToInteger
,AssignmentIntegerToAddress ,CastIntegerToAddressAtReturn
,CastAddressToIntegerAtReturn ,assertWithSideEffect ,assignmentInAssert
,uselessAssignmentArg ,uselessAssignmentPtrArg
,comparisonOfFuncReturningBoolError
,comparisonOfTwoFuncsReturningBoolError ,comparisonOfBoolWithBoolError
,incrementboolean ,comparisonOfBoolWithInt ,compareBoolExpressionWithInt
,negativeIndex ,pointerOutOfBounds ,arrayIndexThenCheck
,possibleBufferAccessOutOfBounds ,argumentSize
,arrayIndexOutOfBoundsCond ,noConstructor ,copyCtorPointerCopying
,noCopyConstructor ,uninitMemberVar ,operatorEqVarError
,unusedPrivateFunction ,memsetClassFloat ,mallocOnClassWarning
,operatorEq ,thisSubtraction ,operatorEqRetRefThis ,operatorEqToSelf
,useInitializationList ,duplInheritedMember ,assignIfError
,comparisonError ,multiCondition ,mismatchingBitAnd
,oppositeInnerCondition ,incorrectLogicOperator ,redundantCondition
,moduloAlwaysTrueFalse to their CWEs ids.
2016-02-20 23:56:36 +01:00
reportError ( toks , Severity : : warning , " duplInheritedMember " , message , CWE398 , false ) ;
2014-08-05 11:48:53 +02:00
}