2010-01-16 08:47:46 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2015-01-03 12:14:58 +01:00
* Copyright ( C ) 2007 - 2015 Daniel Marjamäki and 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"
2010-01-16 08:47:46 +01:00
# include <string>
# include <algorithm>
2012-03-24 13:48:33 +01:00
# include <cctype>
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// Register CheckClass..
2011-10-13 20:53:06 +02:00
namespace {
CheckClass instance ;
2013-03-30 15:09:22 +01:00
const char * getFunctionTypeName (
Function : : Type type )
{
switch ( type ) {
case Function : : eConstructor :
return " constructor " ;
case Function : : eCopyConstructor :
return " copy constructor " ;
2013-04-08 19:37:08 +02:00
case Function : : eMoveConstructor :
return " move constructor " ;
2013-03-30 15:09:22 +01:00
case Function : : eDestructor :
return " destructor " ;
case Function : : eFunction :
return " function " ;
case Function : : eOperatorEqual :
return " operator= " ;
}
return " " ;
}
inline bool isPureWithoutBody ( Function const & func )
{
2015-01-08 05:45:31 +01:00
return func . isPure ( ) & & ! func . hasBody ( ) ;
2013-03-30 15:09:22 +01:00
}
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
2012-05-14 21:50:23 +02:00
if ( ! var - > isPointer ( ) & & 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
2013-04-10 21:57:22 +02:00
if ( ! var - > isPointer ( ) & & var - > typeScope ( ) ) {
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 ;
}
}
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 ;
if ( ! func - > isExplicit ( ) & & func - > argCount ( ) = = 1 ) {
// We must decide, if it is not a copy/move constructor, or it is a copy/move constructor of abstract class.
if ( func - > type ! = Function : : eCopyConstructor & & func - > type ! = Function : : eMoveConstructor ) {
noExplicitConstructorError ( func - > tokenDef , scope - > className , scope - > type = = Scope : : eStruct ) ;
2015-03-11 20:26:53 +01:00
} else if ( isAbstractClass ) {
2015-03-07 20:07:54 +01:00
noExplicitCopyMoveConstructorError ( func - > tokenDef , scope - > className , scope - > type = = Scope : : eStruct ) ;
}
}
}
}
}
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-01-17 16:28:39 +01:00
if ( var & & var - > isPointer ( ) & & var - > scope ( ) = = scope )
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 )
{
reportError ( tok , Severity : : style , " copyCtorPointerCopying " , " Value of pointer ' " + varname + " ', which points to allocated memory, is copied in copy constructor instead of allocating new memory. " ) ;
}
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 +
2012-10-28 13:03:40 +01:00
" ' does not have a copy constructor which is recommended since the class contains a pointer to allocated memory. " ) ;
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 ) ;
}
2011-01-17 18:29:19 +01:00
void CheckClass : : assignVar ( const std : : string & varname , 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 ) {
if ( var - > name ( ) = = varname ) {
2011-01-16 16:37:11 +01:00
usage [ count ] . assign = true ;
return ;
}
}
}
2011-01-17 18:29:19 +01:00
void CheckClass : : initVar ( const std : : string & varname , 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 ) {
if ( var - > name ( ) = = varname ) {
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 ( ) ) {
initVar ( ftok - > str ( ) , scope , usage ) ;
} 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)
2012-07-10 14:47:51 +02:00
assignVar ( ftok - > str ( ) , scope , usage ) ;
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% " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > strAt ( 1 ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
2013-06-14 06:32:26 +02:00
// Before a new statement there is "[{};()=[]"
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 */
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > next ( ) - > str ( ) , 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 ( ) ;
assignVar ( ftok - > strAt ( 2 ) , scope , usage ) ;
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 ( ) ;
assignVar ( tok2 - > str ( ) , scope , usage ) ;
}
}
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 ( ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( tok - > str ( ) , 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% = " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > str ( ) , 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 ) = = " = " )
2011-11-22 18:45:25 +01:00
assignVar ( ftok - > str ( ) , 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-01-17 18:29:19 +01:00
assignVar ( ftok - > next ( ) - > str ( ) , scope , usage ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( ftok , " * this . %name% = " ) ) {
2014-09-29 16:49:20 +02:00
assignVar ( ftok - > strAt ( 3 ) , 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 ( " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > str ( ) , 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 "
2013-08-28 05:48:31 +02:00
" instantiated. That may cause bugs or undefined behavior. " ) ;
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
}
void CheckClass : : noExplicitCopyMoveConstructorError ( const Token * tok , const std : : string & classname , bool isStruct )
{
2015-03-10 19:35:12 +01:00
const std : : string message ( std : : string ( isStruct ? " Abstract struct " : " Abstract class " ) + " ' " + classname + " ' has a copy/move constructor that is not explicit. " ) ;
const std : : string verbose ( message + " For abstract classes, even copy/move constructors may be declared explicit, as, by definition, abstract classes cannot be instantiated, and so objects of such type should never be passed by value. " ) ;
reportError ( tok , Severity : : style , " noExplicitCopyMoveConstructor " , 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
{
2015-04-25 17:48:11 +02:00
reportError ( tok , Severity : : warning , " uninitMemberVar " , " Member variable ' " + classname + " :: " + varname + " ' is not initialized in the constructor. " , 0U , 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
{
2015-04-25 17:48:11 +02:00
reportError ( tok , Severity : : warning , " operatorEqVarError " , " Member variable ' " + classname + " :: " + varname + " ' is not assigned a value in ' " + classname + " ::operator='. " , 0U , 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-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %var% = %any% " ) ) {
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 ( ) ) {
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 ( ) ) {
2012-05-18 16:54:58 +02:00
if ( tok2 - > varId ( ) ) {
2013-02-02 16:30:17 +01:00
const Variable * var2 = tok2 - > variable ( ) ;
2013-04-10 21:57:22 +02:00
if ( var2 & & 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 ;
2014-04-14 22:46:51 +02:00
} else if ( var2 & & ( 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 ;
2014-09-05 12:03:08 +02:00
if ( ! var - > isPointer ( ) & & ! var - > isReference ( ) & & ( var - > type ( ) | | var - > isStlStringType ( ) | | ( Token : : Match ( var - > typeStartToken ( ) , " std :: %type% < " ) & & ! Token : : simpleMatch ( var - > typeStartToken ( ) - > linkAt ( 3 ) , " > :: " ) ) ) )
2012-06-06 12:03:51 +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 "
2012-05-19 21:51:39 +02:00
" could avoid assigning ' " + varname + " ' a value by passing the value to the constructor in the initialization list. " ) ;
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 )
{
2012-07-09 11:11:05 +02:00
reportError ( tok , Severity : : style , " unusedPrivateFunction " , " Unused private function: ' " + classname + " :: " + funcname + " ' " ) ;
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 ( ) ) {
2013-03-01 15:07:20 +01:00
if ( Token : : Match ( tok , " memset|memcpy|memmove ( %any% " ) ) {
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% " ) ) {
int derefs = 1 ;
for ( ; ; arg1 = arg1 - > next ( ) ) {
if ( arg1 - > str ( ) = = " & " )
derefs - - ;
else if ( arg1 - > str ( ) = = " * " )
derefs + + ;
else
break ;
}
const Variable * var = arg1 - > variable ( ) ;
if ( var & & arg1 - > strAt ( 1 ) = = " , " ) {
2013-08-31 03:03:16 +02:00
if ( var - > isPointer ( ) ) {
2013-03-04 11:47:29 +01:00
derefs - - ;
2014-03-14 16:26:37 +01:00
if ( var - > typeEndToken ( ) & & Token : : simpleMatch ( var - > typeEndToken ( ) - > previous ( ) , " * * " ) ) // Check if it's a pointer to pointer
2013-08-31 03:03:16 +02:00
derefs - - ;
}
2013-03-04 11:47:29 +01:00
if ( var - > isArray ( ) )
2013-03-04 19:28:27 +01:00
derefs - = ( int ) var - > dimensions ( ) . size ( ) ;
2013-03-04 11:47:29 +01:00
if ( derefs = = 0 )
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
2013-03-05 13:33:38 +01:00
if ( ! type ) {
2015-01-17 16:28:39 +01:00
const Type * t = symbolDatabase - > findVariableType ( scope , typeTok ) ;
2013-03-05 13:33:38 +01:00
if ( t )
type = t - > classScope ;
}
2012-04-05 09:43:40 +02:00
2014-03-30 17:38:07 +02:00
if ( type ) {
std : : list < 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 ( " ) ) {
2014-03-30 17:38:07 +02:00
std : : list < 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
}
}
2014-03-30 17:38:07 +02:00
void CheckClass : : checkMemsetType ( const Scope * start , const Token * tok , const Scope * type , bool allocation , std : : list < const Scope * > parsedTypes )
2011-03-26 03:21:40 +01:00
{
2015-04-07 07:07:08 +02:00
const bool printPortability = _settings - > isEnabled ( " portability " ) ;
2014-03-30 17:38:07 +02:00
// If type has been checked there is no need to check it again
if ( std : : find ( parsedTypes . begin ( ) , parsedTypes . end ( ) , type ) ! = parsedTypes . end ( ) )
return ;
parsedTypes . push_back ( type ) ;
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, "
2015-04-25 17:48:11 +02:00
" since no constructor is called and class members remain uninitialized. Consider using 'new' instead. " , 0U , 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, "
2015-04-25 17:48:11 +02:00
" since no constructor is called and class members remain uninitialized. Consider using 'new' instead. " , 0U , 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 "
" is created. " ) ;
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 )
{
2014-08-08 08:08:21 +02:00
reportError ( tok , Severity : : error , " memsetClassReference " , " Using ' " + memfunc + " ' on " + type + " that contains a reference. " ) ;
}
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. "
" Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0. " ) ;
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
2013-09-03 11:40:43 +02:00
if ( ! ( Token : : Match ( func - > retDef , " %type% & " ) & & func - > retDef - > str ( ) = = scope - > className ) ) {
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. "
) ;
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 ) {
if ( Token : : Match ( func - > argDef , std : : string ( " ( const " + scope - > className + " & " ) . c_str ( ) ) ) {
// 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 )
{
2012-07-09 11:11:05 +02:00
reportError ( tok , Severity : : style , " operatorEqRetRefThis " , " 'operator=' should return reference to 'this' instance. " ) ;
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 )
{
reportError ( tok , Severity : : style , " operatorEqShouldBeLeftUnimplemented " , " 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented. " ) ;
}
void CheckClass : : operatorEqMissingReturnStatementError ( const Token * tok , bool error )
{
if ( error ) {
reportError ( tok , Severity : : error , " operatorEqMissingReturnStatement " , " No 'return' statement in non-void function causes undefined behavior. " ) ;
} 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 "
" allocated memory is owned and managed by only one instance of the class. " ) ;
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
std : : list < const Function * > inconclusive_errors ;
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 ( ) ) {
2014-06-14 12:55:20 +02:00
inconclusive_errors . push_back ( destructor ) ;
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 *'
std : : set < unsigned int > basepointer ;
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 )
2013-07-20 12:31:04 +02:00
basepointer . 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% = " ) & &
basepointer . find ( tok - > next ( ) - > varId ( ) ) ! = basepointer . 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.
2013-03-05 15:28:40 +01:00
const Function * base_destructor = derivedFromScope - > getDestructor ( ) ;
2014-02-16 11:47:52 +01:00
const Token * base = nullptr ;
2010-08-13 23:57:53 +02:00
if ( base_destructor )
base = base_destructor - > token ;
2010-01-16 08:47:46 +01:00
2010-08-13 23:57:53 +02:00
// Check that there is a destructor..
2011-10-13 20:53:06 +02:00
if ( ! base_destructor ) {
2014-06-14 12:55:20 +02:00
if ( derivedFrom - > derivedFrom . empty ( ) ) {
virtualDestructorError ( derivedFrom - > classDef , derivedFrom - > name ( ) , derivedClass - > str ( ) , false ) ;
// check for duplicate error and remove if if found
std : : list < const Function * > : : iterator found = find ( inconclusive_errors . begin ( ) , inconclusive_errors . end ( ) , base_destructor ) ;
if ( found ! = inconclusive_errors . end ( ) )
inconclusive_errors . erase ( found ) ;
}
2015-01-08 05:45:31 +01:00
} else if ( ! base_destructor - > 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.)
2014-06-14 12:55:20 +02:00
if ( base_destructor - > access = = Public ) {
virtualDestructorError ( base , derivedFrom - > name ( ) , derivedClass - > str ( ) , false ) ;
// check for duplicate error and remove if if found
std : : list < const Function * > : : iterator found = find ( inconclusive_errors . begin ( ) , inconclusive_errors . end ( ) , base_destructor ) ;
if ( found ! = inconclusive_errors . end ( ) )
inconclusive_errors . erase ( found ) ;
}
2010-08-13 23:57:53 +02:00
}
}
}
2010-01-16 08:47:46 +01:00
}
}
2014-06-14 12:55:20 +02:00
for ( std : : list < const Function * > : : const_iterator i = inconclusive_errors . begin ( ) ; i ! = inconclusive_errors . end ( ) ; + + i )
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 )
2015-04-25 17:48:11 +02:00
reportError ( tok , Severity : : warning , " virtualDestructor " , " Class ' " + Base + " ' which has virtual members does not have a virtual destructor. " , 0U , 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 )
{
2012-07-09 11:11:05 +02:00
reportError ( tok , Severity : : warning , " thisSubtraction " , " Suspicious pointer subtraction. Did you intend to write '->'? " ) ;
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 ( ) , " *|& > " ) ) {
2012-08-01 19:24:38 +02:00
const Token * temp = 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
2012-08-01 19:24:38 +02:00
if ( previous - > isUpperCaseName ( ) & & previous - > str ( ) . size ( ) > 2 & & ! symbolDatabase - > isClassOrStruct ( previous - > str ( ) ) )
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.
static const std : : set < std : : string > stl_containers_not_const = make_container < std : : set < std : : string > > ( ) < < " map " < < " unordered_map " ;
}
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 ( ) )
2013-08-07 16:27:37 +02:00
return ( false ) ;
2011-09-17 00:07:25 +02:00
2014-11-02 13:38:03 +01:00
const Token * lhs = tok1 - > tokAt ( - 1 ) ;
if ( lhs - > str ( ) = = " & " ) {
lhs = lhs - > previous ( ) ;
if ( lhs - > type ( ) = = Token : : eAssignmentOp & & lhs - > previous ( ) - > variable ( ) ) {
if ( lhs - > previous ( ) - > variable ( ) - > typeStartToken ( ) - > strAt ( - 1 ) ! = " const " & & lhs - > previous ( ) - > variable ( ) - > isPointer ( ) )
return false ;
}
} else {
const Variable * v2 = lhs - > previous ( ) - > variable ( ) ;
if ( lhs - > type ( ) = = Token : : eAssignmentOp & & v2 )
if ( ! v2 - > isConst ( ) & & v2 - > isReference ( ) & & lhs = = v2 - > nameToken ( ) - > next ( ) )
return false ;
}
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 ) )
2013-08-07 16:27:37 +02: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
else if ( end - > next ( ) - > type ( ) = = Token : : eAssignmentOp )
2013-08-07 16:27:37 +02: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 ) ! = " << " )
2013-08-07 16:27:37 +02:00
return ( false ) ;
2012-08-01 19:24:38 +02:00
else if ( tok1 - > strAt ( - 1 ) = = " >> " )
2013-08-07 16:27:37 +02:00
return ( false ) ;
2012-08-01 19:24:38 +02:00
// ++/--
else if ( end - > next ( ) - > type ( ) = = Token : : eIncDecOp | | tok1 - > previous ( ) - > type ( ) = = Token : : eIncDecOp )
2013-08-07 16:27:37 +02: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 " )
2013-08-07 16:27:37 +02: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 ( ) )
2013-08-07 16:27:37 +02: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 ) )
2013-08-07 16:27:37 +02: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 ( ) )
2013-08-07 16:27:37 +02: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 " ) ) ) {
2013-08-07 16:27:37 +02:00
return ( false ) ;
2011-01-16 11:18:12 +01:00
}
}
2013-08-07 16:27:37 +02: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 "
2015-04-25 17:48:11 +02:00
" it a function that must not change object internal state? " , 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 - "
2015-04-25 17:48:11 +02:00
" is it a function that must not access members of class instances? " , 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.
//---------------------------------------------------------------------------
2011-10-13 20:53:06 +02:00
struct VarInfo {
2011-09-28 03:07:37 +02:00
VarInfo ( const Variable * _var , const Token * _tok )
: var ( _var ) , tok ( _tok ) { }
const Variable * var ;
const Token * tok ;
} ;
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 "
2015-04-25 17:48:11 +02:00
" initialization errors. " , 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
{
2014-08-06 13:35:39 +02:00
reportError ( tok , Severity : : error , " selfInitialization " , " Member variable ' " + varname + " ' is initialized by itself. " ) ;
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
}
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 "
2015-04-25 17:48:11 +02:00
" Call of pure virtual function ' " + purefuncname + " ' in " + scopeFunctionTypeName + " . The call will fail during runtime. " , 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 + " '. " ;
2015-04-25 17:48:11 +02:00
reportError ( toks , Severity : : warning , " duplInheritedMember " , message , 0U , false ) ;
2014-08-05 11:48:53 +02:00
}