2010-01-16 08:47:46 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2011-01-09 20:33:36 +01:00
* Copyright ( C ) 2007 - 2011 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 <locale>
# include <cstring>
# include <string>
# include <sstream>
# include <algorithm>
//---------------------------------------------------------------------------
// Register CheckClass..
2011-10-13 20:53:06 +02:00
namespace {
CheckClass instance ;
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 ) ,
2010-12-07 07:08:49 +01:00
symbolDatabase ( NULL )
2010-07-26 16:46:37 +02:00
{
2010-08-07 12:41:11 +02:00
}
2010-11-13 07:31:56 +01:00
void CheckClass : : createSymbolDatabase ( )
2010-07-14 19:00:52 +02:00
{
2010-11-13 07:31:56 +01:00
// Multiple calls => bail out
if ( symbolDatabase )
return ;
2010-07-14 19:00:52 +02:00
2010-12-07 07:08:49 +01:00
symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-07-14 19:00:52 +02:00
}
2010-01-16 08:47:46 +01: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 ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-04-21 08:38:25 +02:00
return ;
2010-08-07 12:41:11 +02:00
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-07-26 16:46:37 +02:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2010-11-20 07:26:50 +01:00
// only check classes and structures
2011-01-17 18:29:19 +01:00
if ( ! scope - > isClassOrStruct ( ) )
2010-08-09 17:50:26 +02:00
continue ;
2010-07-26 16:46:37 +02:00
// There are no constructors.
2011-10-13 20:53:06 +02:00
if ( scope - > numConstructors = = 0 ) {
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 ) {
if ( var - > isPrivate ( ) & & ! var - > isClass ( ) & & ! var - > isStatic ( ) ) {
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
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 ) {
2011-01-17 18:29:19 +01:00
if ( ! func - > hasBody | | ! ( func - > type = = Function : : eConstructor | |
func - > type = = Function : : eCopyConstructor | |
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
2010-07-26 16:46:37 +02:00
std : : list < std : : string > callstack ;
2011-03-11 01:43:29 +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 ) {
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
2011-01-18 07:32:06 +01:00
if ( var - > isConst ( ) & & var - > nameToken ( ) - > previous ( ) - > str ( ) ! = " * " )
2010-11-16 07:30:55 +01:00
continue ;
2010-12-02 07:35:01 +01:00
// Check if this is a class constructor
2011-10-13 20:53:06 +02:00
if ( 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
2011-01-18 07:32:06 +01:00
else if ( var - > type ( ) - > needInitialization ! = Scope : : 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
2011-01-18 11:07:33 +01:00
if ( var - > type ( ) & & canNotCopy ( var - > type ( ) ) )
2011-01-18 07:34:11 +01:00
continue ;
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 ) {
2011-01-27 18:44:20 +01:00
const Token * operStart = func - > token - > tokAt ( 1 ) ;
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 )
2011-01-18 07:32:06 +01:00
operatorEqVarError ( func - > token , scope - > className , var - > name ( ) ) ;
2011-10-13 20:53:06 +02:00
} else if ( func - > access ! = Private )
2011-01-18 07:32:06 +01:00
uninitVarError ( func - > token , scope - > className , var - > name ( ) ) ;
2010-07-15 10:16:16 +02:00
}
2010-07-14 19:00:52 +02:00
}
2010-01-16 08:47:46 +01:00
}
}
2011-01-18 07:34:11 +01:00
bool CheckClass : : canNotCopy ( const Scope * scope ) const
{
std : : list < Function > : : const_iterator func ;
bool privateAssign = false ;
bool privateCopy = false ;
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2011-01-18 07:34:11 +01:00
if ( func - > type = = Function : : eCopyConstructor & & func - > access = = Private )
privateCopy = true ;
else if ( func - > type = = Function : : eOperatorEqual & & func - > access = = Private )
privateAssign = true ;
}
return privateAssign & & privateCopy ;
}
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 ;
}
}
}
void CheckClass : : assignAllVar ( std : : vector < Usage > & usage )
{
for ( size_t i = 0 ; i < usage . size ( ) ; + + i )
usage [ i ] . assign = true ;
}
void CheckClass : : clearAllVar ( std : : vector < Usage > & usage )
{
2011-10-13 20:53:06 +02:00
for ( 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...
2011-10-13 20:53:06 +02:00
for ( size_t i = 0 ; i < scope - > derivedFrom . size ( ) ; + + i ) {
2011-01-21 07:42:41 +01:00
const Scope * derivedFrom = scope - > derivedFrom [ i ] . scope ;
2011-01-16 16:37:11 +01:00
// Check if base class exists in database
2011-10-13 20:53:06 +02:00
if ( derivedFrom ) {
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
2011-01-16 16:37:11 +01:00
2011-10-13 20:53:06 +02:00
for ( func = derivedFrom - > functionList . begin ( ) ; func ! = derivedFrom - > 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 ;
}
2011-01-17 18:29:19 +01:00
void CheckClass : : initializeVarList ( const Function & func , std : : list < std : : string > & callstack , const Scope * scope , std : : vector < Usage > & usage )
2011-01-16 16:37:11 +01:00
{
bool Assign = false ;
unsigned int indentlevel = 0 ;
const Token * ftok = func . token ;
2011-10-13 20:53:06 +02:00
for ( ; ftok ; ftok = ftok - > next ( ) ) {
2011-01-16 16:37:11 +01:00
if ( ! ftok - > next ( ) )
break ;
// Class constructor.. initializing variables like this
// clKalle::clKalle() : var(value) { }
2011-10-13 20:53:06 +02:00
if ( indentlevel = = 0 ) {
if ( Assign & & Token : : Match ( ftok , " %var% ( " ) ) {
2011-01-17 18:29:19 +01:00
initVar ( ftok - > str ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
// assignment in the initializer..
// : var(value = x)
if ( Token : : Match ( ftok - > tokAt ( 2 ) , " %var% = " ) )
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > strAt ( 2 ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
Assign | = ( ftok - > str ( ) = = " : " ) ;
}
2011-10-13 20:53:06 +02:00
if ( ftok - > str ( ) = = " { " ) {
2011-01-16 16:37:11 +01:00
+ + indentlevel ;
Assign = false ;
}
2011-10-13 20:53:06 +02:00
else if ( ftok - > str ( ) = = " } " ) {
2011-01-16 16:37:11 +01:00
if ( indentlevel < = 1 )
break ;
- - indentlevel ;
}
if ( indentlevel < 1 )
continue ;
// Variable getting value from stream?
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok , " >> %var% " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > strAt ( 1 ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// Before a new statement there is "[{};)=]"
if ( ! Token : : Match ( ftok , " [{};()=] " ) )
continue ;
if ( Token : : simpleMatch ( ftok , " ( ! " ) )
ftok = ftok - > next ( ) ;
// Using the operator= function to initialize all variables..
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok - > next ( ) , " return| * this = " ) ) {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
break ;
}
// Calling member variable function?
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok - > next ( ) , " %var% . %var% ( " ) ) {
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 - > varId ( ) = = 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 ) ;
2011-01-16 16:37:11 +01:00
continue ;
}
}
ftok = ftok - > tokAt ( 2 ) ;
}
2011-08-05 02:37:27 +02:00
if ( ! Token : : Match ( ftok - > next ( ) , " ::| %var% " ) & &
2011-01-16 16:37:11 +01:00
! Token : : Match ( ftok - > next ( ) , " this . %var% " ) & &
! Token : : Match ( ftok - > next ( ) , " * %var% = " ) & &
! Token : : Match ( ftok - > next ( ) , " ( * this ) . %var% " ) )
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 :: "
if ( Token : : Match ( ftok , " %var% :: " ) )
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..
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( ftok , " ::| memset ( %var% , " ) ) {
2011-08-05 02:37:27 +02:00
const int offset = ftok - > str ( ) = = " :: " ? 1 : 0 ;
assignVar ( ftok - > strAt ( 2 + offset ) , scope , usage ) ;
ftok = ftok - > tokAt ( 1 + offset ) - > link ( ) ;
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 ( ) ! = " :: " ) {
2011-09-15 01:58:11 +02:00
// recursive call / calling overloaded function
// assume that all variables are initialized
2011-10-13 20:53:06 +02:00
if ( std : : find ( callstack . begin ( ) , callstack . end ( ) , ftok - > str ( ) ) ! = callstack . end ( ) ) {
2011-09-15 01:58:11 +02:00
/** @todo false negative: just bail */
assignAllVar ( usage ) ;
return ;
}
2011-09-15 01:04:06 +02:00
/** @todo check function parameters for overloaded function so we check the right one */
2011-01-16 16:37:11 +01:00
// check if member function exists
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator it ;
2011-10-13 20:53:06 +02:00
for ( it = scope - > functionList . begin ( ) ; it ! = scope - > functionList . end ( ) ; + + it ) {
2011-01-27 18:44:20 +01:00
if ( ftok - > str ( ) = = it - > tokenDef - > str ( ) & & it - > type ! = Function : : eConstructor )
2011-01-16 16:37:11 +01:00
break ;
}
// member function found
2011-10-13 20:53:06 +02:00
if ( it ! = scope - > functionList . end ( ) ) {
2011-01-16 16:37:11 +01:00
// member function has implementation
2011-10-13 20:53:06 +02:00
if ( it - > hasBody ) {
2011-09-15 01:58:11 +02:00
// initialize variable use list using member function
callstack . push_back ( ftok - > str ( ) ) ;
initializeVarList ( * it , callstack , scope , usage ) ;
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 ) ;
}
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( ftok , " %var% ( " ) & & ftok - > str ( ) ! = " if " ) {
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 ;
}
}
// recursive call / calling overloaded function
// assume that all variables are initialized
2011-10-13 20:53:06 +02:00
if ( std : : find ( callstack . begin ( ) , callstack . end ( ) , ftok - > str ( ) ) ! = callstack . end ( ) ) {
2011-01-16 16:37:11 +01:00
assignAllVar ( usage ) ;
return ;
}
// check if member function
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator it ;
2011-10-13 20:53:06 +02:00
for ( it = scope - > functionList . begin ( ) ; it ! = scope - > functionList . end ( ) ; + + it ) {
2011-01-17 18:29:19 +01:00
if ( ftok - > str ( ) = = it - > tokenDef - > str ( ) & & it - > type ! = Function : : eConstructor )
2011-01-16 16:37:11 +01:00
break ;
}
// member function found
2011-10-13 20:53:06 +02:00
if ( it ! = scope - > functionList . end ( ) ) {
2011-01-16 16:37:11 +01:00
// member function has implementation
2011-10-13 20:53:06 +02:00
if ( it - > hasBody ) {
2011-09-15 01:58:11 +02:00
// initialize variable use list using member function
callstack . push_back ( ftok - > str ( ) ) ;
initializeVarList ( * it , callstack , scope , usage ) ;
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 ) ;
}
}
// 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
2011-10-13 20:53:06 +02:00
if ( func . type ! = Function : : eConstructor & & 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
2011-01-17 18:29:19 +01:00
if ( ! scope - > 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 {
2011-01-16 16:37:11 +01:00
unsigned int indentlevel2 = 0 ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = ftok - > tokAt ( 2 ) ; tok ; tok = tok - > next ( ) ) {
2011-01-16 16:37:11 +01:00
if ( tok - > str ( ) = = " ( " )
+ + indentlevel2 ;
2011-10-13 20:53:06 +02:00
else if ( tok - > str ( ) = = " ) " ) {
2011-01-16 16:37:11 +01:00
if ( indentlevel2 = = 0 )
break ;
- - indentlevel2 ;
}
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?
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( ftok , " %var% = " ) ) {
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?
2011-07-27 18:03:44 +02:00
else if ( Token : : Match ( ftok , " %var% [ " ) & &
2011-10-13 20:53:06 +02:00
Token : : simpleMatch ( ftok - > next ( ) - > link ( ) , " ] = " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > str ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// Assignment of member of array item of member variable?
2011-07-27 18:03:44 +02:00
else if ( Token : : Match ( ftok , " %var% [ " ) & &
( Token : : Match ( ftok - > next ( ) - > link ( ) , " ] . %var% = " ) | |
2011-10-13 20:53:06 +02:00
Token : : Match ( ftok - > next ( ) - > link ( ) , " ] . %var% . %var% = " ) ) ) {
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?
2011-07-27 18:03:44 +02:00
else if ( Token : : Match ( ftok , " %var% [ " ) & &
Token : : simpleMatch ( ftok - > next ( ) - > link ( ) , " ] [ " ) & &
2011-10-13 20:53:06 +02:00
Token : : simpleMatch ( ftok - > next ( ) - > link ( ) - > next ( ) - > link ( ) , " ] = " ) ) {
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?
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( ftok , " * %var% = " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > next ( ) - > str ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// Assignment of struct member of member variable?
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( ftok , " %var% . %any% = " ) ) {
2011-01-17 18:29:19 +01:00
assignVar ( ftok - > str ( ) , scope , usage ) ;
2011-01-16 16:37:11 +01:00
}
// The functions 'clear' and 'Clear' are supposed to initialize variable.
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( ftok , " %var% . 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 +
" 'does not have a constructor but it has attributes. "
2010-11-27 09:17:03 +01:00
" The attributes are not initialized which may cause bugs or undefined behavior. " ) ;
2010-11-14 06:50:33 +01:00
}
void CheckClass : : uninitVarError ( const Token * tok , const std : : string & classname , const std : : string & varname )
{
2011-04-27 02:37:16 +02:00
reportError ( tok , Severity : : warning , " uninitVar " , " Member variable ' " + classname + " :: " + varname + " ' is not initialized in the constructor. " ) ;
2010-11-14 06:50:33 +01:00
}
void CheckClass : : operatorEqVarError ( const Token * tok , const std : : string & classname , const std : : string & varname )
{
reportError ( tok , Severity : : warning , " operatorEqVarError " , " Member variable ' " + classname + " :: " + varname + " ' is not assigned a value in ' " + classname + " ::operator= " + " ' " ) ;
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: Unused private functions
//---------------------------------------------------------------------------
void CheckClass : : privateFunctions ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-04-21 08:38:25 +02:00
return ;
2010-09-30 21:22:49 +02:00
// don't check code that contains templates. Templates that are
// "unused" are removed from the code. #2067
if ( _tokenizer - > codeWithTemplates ( ) )
return ;
2010-09-07 18:37:43 +02:00
// dont check borland classes with properties..
if ( Token : : findmatch ( _tokenizer - > tokens ( ) , " ; __property ; " ) )
return ;
2010-09-02 07:40:20 +02:00
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-06-13 07:17:50 +02:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2010-11-20 07:26:50 +01:00
// only check classes and structures
2011-01-17 18:29:19 +01:00
if ( ! scope - > isClassOrStruct ( ) )
2010-09-02 07:40:20 +02:00
continue ;
2010-12-15 18:45:53 +01:00
// don’ t check derived classes
2011-01-17 18:29:19 +01:00
if ( ! scope - > derivedFrom . empty ( ) )
2010-09-02 07:40:20 +02:00
continue ;
2011-01-22 17:35:54 +01:00
// skip checking if there are friends
if ( ! scope - > friendList . empty ( ) )
continue ;
2010-09-02 07:40:20 +02:00
// Locate some class
2011-01-17 18:29:19 +01:00
const Token * tok1 = scope - > classDef ;
2010-09-02 07:40:20 +02:00
2010-09-02 19:22:54 +02:00
// check that the whole class implementation is seen
bool whole = true ;
2011-01-17 18:29:19 +01: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 ) {
if ( ! func - > hasBody ) {
2010-09-02 19:22:54 +02:00
// empty private copy constructors and assignment operators are OK
2011-02-11 14:00:41 +01:00
if ( ( func - > type = = Function : : eCopyConstructor | |
func - > type = = Function : : eOperatorEqual ) & &
func - > access = = Private )
2010-09-02 19:22:54 +02:00
continue ;
whole = false ;
break ;
}
}
if ( ! whole )
2010-01-16 08:47:46 +01:00
continue ;
const std : : string & classname = tok1 - > next ( ) - > str ( ) ;
std : : list < const Token * > FuncList ;
2010-09-02 07:40:20 +02:00
/** @todo embedded class have access to private functions */
2011-10-13 20:53:06 +02:00
if ( ! scope - > getNestedNonFunctions ( ) ) {
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2010-09-02 07:40:20 +02:00
// Get private functions..
2011-02-11 14:00:41 +01:00
if ( func - > type = = Function : : eFunction & & func - > access = = Private )
2010-09-02 19:22:54 +02:00
FuncList . push_back ( func - > tokenDef ) ;
2010-01-16 08:47:46 +01:00
}
}
// Check that all private functions are used..
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2011-02-20 14:36:06 +01:00
const Token * ftok = func - > start ;
2011-10-13 20:53:06 +02:00
if ( ftok ) {
2011-02-22 12:47:28 +01:00
const Token * etok = ftok - > link ( ) ;
2011-10-13 20:53:06 +02:00
for ( ; ftok ! = etok ; ftok = ftok - > next ( ) ) {
if ( Token : : Match ( ftok , " %var% ( " ) ) {
2011-02-22 12:47:28 +01:00
// Remove function from FuncList
std : : list < const Token * > : : iterator it = FuncList . begin ( ) ;
2011-10-13 20:53:06 +02:00
while ( it ! = FuncList . end ( ) ) {
2011-02-22 12:47:28 +01:00
if ( ftok - > str ( ) = = ( * it ) - > str ( ) )
FuncList . erase ( it + + ) ;
else
+ + it ;
}
2010-01-16 08:47:46 +01:00
}
}
}
}
2011-10-13 20:53:06 +02:00
while ( ! FuncList . empty ( ) ) {
2010-01-16 08:47:46 +01:00
// Final check; check if the function pointer is used somewhere..
2011-06-29 13:19:34 +02:00
const std : : string _pattern ( " return|(|)|,|= &| " + FuncList . front ( ) - > str ( ) ) ;
2010-08-15 07:44:08 +02:00
// or if the function address is used somewhere...
// eg. sigc::mem_fun(this, &className::classFunction)
const std : : string _pattern2 ( " & " + classname + " :: " + FuncList . front ( ) - > str ( ) ) ;
2011-01-19 01:58:44 +01:00
const std : : string methodAsArgument ( " (|, " + classname + " :: " + FuncList . front ( ) - > str ( ) + " ,|) " ) ;
2010-08-15 07:44:08 +02:00
if ( ! Token : : findmatch ( _tokenizer - > tokens ( ) , _pattern . c_str ( ) ) & &
2011-01-19 01:58:44 +01:00
! Token : : findmatch ( _tokenizer - > tokens ( ) , _pattern2 . c_str ( ) ) & &
! Token : : findmatch ( _tokenizer - > tokens ( ) , methodAsArgument . c_str ( ) )
2011-10-13 20:53:06 +02:00
) {
2010-01-16 08:47:46 +01:00
unusedPrivateFunctionError ( FuncList . front ( ) , classname , FuncList . front ( ) - > str ( ) ) ;
}
FuncList . pop_front ( ) ;
}
}
}
2010-11-14 06:50:33 +01:00
void CheckClass : : unusedPrivateFunctionError ( const Token * tok , const std : : string & classname , const std : : string & funcname )
{
reportError ( tok , Severity : : style , " unusedPrivateFunction " , " Unused private function ' " + classname + " :: " + funcname + " ' " ) ;
}
2010-01-16 08:47:46 +01:00
//---------------------------------------------------------------------------
// ClassCheck: Check that memset is not used on classes
//---------------------------------------------------------------------------
void CheckClass : : noMemset ( )
{
2011-02-26 22:00:05 +01:00
createSymbolDatabase ( ) ;
2011-03-13 04:41:21 +01:00
std : : list < Scope > : : const_iterator scope ;
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2011-03-13 04:41:21 +01:00
std : : list < Function > : : const_iterator func ;
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 ) {
2011-03-13 04:41:21 +01:00
// only check functions with bodies
if ( ! func - > hasBody )
continue ;
2010-01-16 08:47:46 +01:00
2011-03-13 04:41:21 +01:00
// Locate all 'memset' tokens..
const Token * end = func - > start - > link ( ) ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = func - > start ; tok & & tok ! = end ; tok = tok - > next ( ) ) {
2011-03-13 04:41:21 +01:00
if ( ! Token : : Match ( tok , " memset|memcpy|memmove " ) )
continue ;
const Token * typeTok = 0 ;
if ( Token : : Match ( tok , " memset ( %var% , %num% , sizeof ( %type% ) ) " ) )
typeTok = tok - > tokAt ( 8 ) ;
else if ( Token : : Match ( tok , " memset ( & %var% , %num% , sizeof ( %type% ) ) " ) )
typeTok = tok - > tokAt ( 9 ) ;
else if ( Token : : Match ( tok , " memset ( & %var% , %num% , sizeof ( %type% :: %type% ) ) " ) )
typeTok = tok - > tokAt ( 11 ) ;
else if ( Token : : Match ( tok , " memset ( %var% , %num% , sizeof ( struct %type% ) ) " ) )
typeTok = tok - > tokAt ( 9 ) ;
else if ( Token : : Match ( tok , " memset ( & %var% , %num% , sizeof ( struct %type% ) ) " ) )
typeTok = tok - > tokAt ( 10 ) ;
else if ( Token : : Match ( tok , " %type% ( %var% , %var% , sizeof ( %type% ) ) " ) )
typeTok = tok - > tokAt ( 8 ) ;
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok , " memset ( & %var% , %num% , sizeof ( %var% ) ) " )) {
2011-03-13 04:41:21 +01:00
unsigned int varid = tok - > tokAt ( 3 ) - > varId ( ) ;
const Variable * var = symbolDatabase - > getVariableFromVarId ( varid ) ;
if ( var & & ( var - > typeStartToken ( ) = = var - > typeEndToken ( ) | |
Token : : Match ( var - > typeStartToken ( ) , " %type% :: %type% " ) ) )
typeTok = var - > typeEndToken ( ) ;
}
// No type defined => The tokens didn't match
if ( ! typeTok )
continue ;
2010-01-16 08:47:46 +01:00
2011-03-23 02:24:28 +01:00
const Scope * type = symbolDatabase - > findVariableType ( & ( * scope ) , typeTok ) ;
if ( type )
checkMemsetType ( & ( * scope ) , tok , type ) ;
2011-03-13 04:41:21 +01:00
}
}
2010-01-16 08:47:46 +01:00
}
}
2011-03-26 03:21:40 +01:00
void CheckClass : : checkMemsetType ( const Scope * start , const Token * tok , const Scope * type )
{
// recursively check all parent classes
2011-10-13 20:53:06 +02:00
for ( size_t i = 0 ; i < type - > derivedFrom . size ( ) ; i + + ) {
2011-03-26 03:21:40 +01:00
if ( type - > derivedFrom [ i ] . scope )
checkMemsetType ( start , tok , type - > derivedFrom [ i ] . scope ) ;
}
// 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 ) {
2011-03-26 03:21:40 +01:00
if ( func - > isVirtual )
memsetError ( tok , tok - > str ( ) , " virtual method " , type - > classDef - > str ( ) ) ;
}
// 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 ) {
2011-03-26 03:21:40 +01:00
// don't warn if variable static or const
2011-10-13 20:53:06 +02:00
if ( ! var - > isStatic ( ) & & ! var - > isConst ( ) ) {
2011-03-26 03:21:40 +01:00
const Token * tok1 = var - > typeStartToken ( ) ;
// check for std:: type that is not a pointer or reference
if ( Token : : simpleMatch ( tok1 , " std :: " ) & & ! Token : : Match ( var - > nameToken ( ) - > previous ( ) , " *|& " ) )
memsetError ( tok , tok - > str ( ) , " 'std:: " + tok1 - > strAt ( 2 ) + " ' " , type - > classDef - > str ( ) ) ;
// check for known type that is not a pointer or reference
else if ( var - > type ( ) & & ! Token : : Match ( var - > nameToken ( ) - > previous ( ) , " *|& " ) )
checkMemsetType ( start , tok , var - > type ( ) ) ;
}
}
}
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
{
2011-03-10 08:19:31 +01:00
reportError ( tok , Severity : : error , " memsetClass " , " Using ' " + memfunc + " ' on " + type + " that contains a " + classname ) ;
2010-11-14 06:50:33 +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 ;
2010-08-07 12:41:11 +02:00
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2011-04-01 01:40:28 +02:00
if ( ! scope - > isClassOrStruct ( ) )
continue ;
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 ) {
if ( func - > type = = Function : : eOperatorEqual & & func - > access ! = Private ) {
2011-04-01 01:40:28 +02:00
// use definition for check so we don't have to deal with qualification
if ( ! ( Token : : Match ( func - > tokenDef - > tokAt ( - 3 ) , " ;|}|{|public:|protected:|private: %type% & " ) & &
2011-10-13 20:53:06 +02:00
func - > tokenDef - > strAt ( - 2 ) = = scope - > className ) ) {
2011-08-28 17:14:15 +02:00
// make sure we really have a copy assignment operator
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( func - > tokenDef - > tokAt ( 2 ) , " const| %var% & " ) ) {
2011-08-28 17:14:15 +02:00
if ( func - > tokenDef - > strAt ( 2 ) = = " const " & &
func - > tokenDef - > strAt ( 3 ) = = scope - > className )
operatorEqReturnError ( func - > tokenDef - > tokAt ( - 1 ) , scope - > className ) ;
else if ( func - > tokenDef - > strAt ( 2 ) = = scope - > className )
operatorEqReturnError ( func - > tokenDef - > tokAt ( - 1 ) , scope - > className ) ;
}
}
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
{
2011-04-01 01:40:28 +02:00
reportError ( tok , Severity : : style , " operatorEq " , " \' " + className + " ::operator=' should return \' " + className + " & \' " ) ;
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 ;
createSymbolDatabase ( ) ;
std : : list < Scope > : : const_iterator scope ;
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2011-03-26 03:21:40 +01:00
// only check classes and structures
2011-10-13 20:53:06 +02:00
if ( scope - > isClassOrStruct ( ) ) {
2011-03-26 03:21:40 +01: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 ) {
if ( func - > type = = Function : : eOperatorEqual & & func - > hasBody ) {
2011-03-26 03:21:40 +01:00
// make sure return signature is correct
if ( Token : : Match ( func - > tokenDef - > tokAt ( - 3 ) , " ;|}|{|public:|protected:|private: %type% & " ) & &
2011-10-13 20:53:06 +02:00
func - > tokenDef - > strAt ( - 2 ) = = scope - > className ) {
2011-03-26 03:21:40 +01:00
// find the ')'
const Token * tok = func - > token - > next ( ) - > link ( ) ;
checkReturnPtrThis ( & ( * scope ) , & ( * func ) , tok - > tokAt ( 2 ) , tok - > next ( ) - > link ( ) ) ;
}
}
}
}
}
}
2011-01-17 18:29:19 +01:00
void CheckClass : : checkReturnPtrThis ( const Scope * scope , const Function * func , const Token * tok , const Token * last )
2010-09-11 08:23:30 +02:00
{
bool foundReturn = false ;
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
if ( Token : : Match ( tok - > tokAt ( 1 ) , " %any% ( " ) & &
2011-10-13 20:53:06 +02:00
tok - > tokAt ( 2 ) - > link ( ) - > 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
2011-01-17 18:29:19 +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
2011-10-13 20:53:06 +02:00
if ( it - > arg - > link ( ) - > next ( ) - > str ( ) ! = " const " ) {
2011-01-21 19:54:41 +01:00
/** @todo make sure argument types match */
// make sure it's not the same function
if ( & * it ! = func )
checkReturnPtrThis ( scope , & * it , it - > arg - > link ( ) - > next ( ) , it - > arg - > link ( ) - > next ( ) - > link ( ) ) ;
// 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
2010-09-11 08:23:30 +02:00
else if ( ! ( Token : : Match ( tok - > tokAt ( 1 ) , " (| * this ; | = " ) ||
Token : : Match ( tok - > tokAt ( 1 ) , " (| * this += " ) | |
2011-02-20 02:02:16 +01:00
Token : : simpleMatch ( tok - > tokAt ( 1 ) , " operator= ( " ) | |
2011-03-15 08:19:30 +01:00
Token : : simpleMatch ( tok - > tokAt ( 1 ) , " this . operator= ( " ) | |
2011-02-20 02:02:16 +01:00
( Token : : Match ( tok - > tokAt ( 1 ) , " %type% :: operator= ( " ) & &
tok - > next ( ) - > str ( ) = = scope - > className ) ) )
2010-09-11 08:23:30 +02:00
operatorEqRetRefThisError ( func - > token ) ;
}
}
if ( ! foundReturn )
operatorEqRetRefThisError ( func - > token ) ;
}
2010-11-14 06:50:33 +01:00
void CheckClass : : operatorEqRetRefThisError ( const Token * tok )
{
reportError ( tok , Severity : : style , " operatorEqRetRefThis " , " 'operator=' should return reference to self " ) ;
}
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 ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2010-11-14 06:50:33 +01:00
return ;
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-11-14 06:50:33 +01:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2011-04-01 01:53:35 +02:00
if ( ! scope - > isClassOrStruct ( ) )
continue ;
2011-03-11 01:43:29 +01:00
std : : list < Function > : : const_iterator func ;
2010-11-14 06:50:33 +01:00
// skip classes with multiple inheritance
2011-01-17 18:29:19 +01:00
if ( scope - > derivedFrom . size ( ) > 1 )
2010-11-14 06:50:33 +01:00
continue ;
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
if ( func - > type = = Function : : eOperatorEqual & & func - > hasBody ) {
2010-11-14 06:50:33 +01:00
// make sure return signature is correct
2011-03-11 01:43:29 +01:00
if ( Token : : Match ( func - > tokenDef - > tokAt ( - 3 ) , " ;|}|{|public:|protected:|private: %type% & " ) & &
2011-10-13 20:53:06 +02:00
func - > tokenDef - > strAt ( - 2 ) = = 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 ) ) {
2011-04-01 01:53:35 +02:00
if ( hasDeallocation ( & ( * func ) ) )
operatorEqToSelfError ( func - > token ) ;
2010-11-14 06:50:33 +01:00
}
}
}
}
}
}
2011-04-01 01:53:35 +02:00
bool CheckClass : : hasDeallocation ( const Function * func )
2010-01-16 08:47:46 +01:00
{
// This function is called when no simple check was found for assignment
// to self. We are currently looking for a specific sequence of:
// deallocate member ; ... member = allocate
// This check is far from ideal because it can cause false negatives.
// Unfortunately, this is necessary to prevent false positives.
// This check needs to do careful analysis someday to get this
// correct with a high degree of certainty.
2011-04-01 01:53:35 +02:00
const Token * last = func - > start - > link ( ) ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = func - > start ; tok & & ( tok ! = last ) ; tok = tok - > next ( ) ) {
2010-01-16 08:47:46 +01:00
// check for deallocating memory
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " {|;|, free ( %var% " ) ) {
2010-08-28 11:23:23 +02:00
const Token * var = tok - > tokAt ( 3 ) ;
2010-01-16 08:47:46 +01:00
// we should probably check that var is a pointer in this class
2010-08-28 11:23:23 +02:00
const Token * tok1 = tok - > tokAt ( 4 ) ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
while ( tok1 & & ( tok1 ! = last ) ) {
if ( Token : : Match ( tok1 , " %var% = " ) ) {
2010-04-02 07:30:58 +02:00
if ( tok1 - > str ( ) = = var - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
}
tok1 = tok1 - > next ( ) ;
}
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " {|;|, delete [ ] %var% " ) ) {
2010-08-28 11:23:23 +02:00
const Token * var = tok - > tokAt ( 4 ) ;
2010-01-16 08:47:46 +01:00
// we should probably check that var is a pointer in this class
2010-08-28 11:23:23 +02:00
const Token * tok1 = tok - > tokAt ( 5 ) ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
while ( tok1 & & ( tok1 ! = last ) ) {
if ( Token : : Match ( tok1 , " %var% = new %type% [ " ) ) {
2010-04-02 07:30:58 +02:00
if ( tok1 - > str ( ) = = var - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
}
tok1 = tok1 - > next ( ) ;
}
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " {|;|, delete %var% " ) ) {
2010-08-28 11:23:23 +02:00
const Token * var = tok - > tokAt ( 2 ) ;
2010-01-16 08:47:46 +01:00
// we should probably check that var is a pointer in this class
2010-08-28 11:23:23 +02:00
const Token * tok1 = tok - > tokAt ( 3 ) ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
while ( tok1 & & ( tok1 ! = last ) ) {
if ( Token : : Match ( tok1 , " %var% = new " ) ) {
2010-04-02 07:30:58 +02:00
if ( tok1 - > str ( ) = = var - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
}
tok1 = tok1 - > next ( ) ;
}
}
}
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
{
2011-04-01 01:53:35 +02:00
const Token * last = func - > start - > link ( ) ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok = func - > start ; tok & & tok ! = last ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " if ( " ) ) {
2010-08-28 11:23:23 +02:00
const Token * tok1 = tok - > tokAt ( 2 ) ;
const Token * tok2 = tok - > tokAt ( 1 ) - > 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 ( ) ) {
if ( Token : : Match ( tok1 , " this ==|!= & %var% " ) ) {
2010-04-02 07:30:58 +02:00
if ( tok1 - > tokAt ( 3 ) - > str ( ) = = rhs - > str ( ) )
2010-01-16 08:47:46 +01:00
return true ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok1 , " & %var% ==|!= this " ) ) {
2010-04-02 07:30:58 +02:00
if ( tok1 - > tokAt ( 1 ) - > str ( ) = = 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
{
2010-11-14 06:50:33 +01:00
reportError ( tok , Severity : : warning , " operatorEqToSelf " , " 'operator=' should check for assignment to self " ) ;
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
2010-08-13 18:34:02 +02:00
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-01-16 08:47:46 +01:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2010-08-13 18:34:02 +02:00
// Skip base classes and namespaces
2011-01-17 18:29:19 +01:00
if ( scope - > derivedFrom . empty ( ) )
2010-08-13 18:34:02 +02:00
continue ;
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
if ( ! destructor | | ! destructor - > hasBody )
continue ;
// Empty destructor
2010-09-10 07:02:49 +02:00
if ( destructor - > token - > tokAt ( 3 ) - > link ( ) = = 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 ;
2010-01-16 08:47:46 +01:00
const Token * derivedClass = derived - > tokAt ( 1 ) ;
// Iterate through each base class...
2011-10-13 20:53:06 +02:00
for ( unsigned int j = 0 ; j < scope - > derivedFrom . size ( ) ; + + j ) {
2010-08-13 18:34:02 +02:00
// Check if base class is public and exists in database
2011-10-13 20:53:06 +02:00
if ( scope - > derivedFrom [ j ] . access ! = Private & & scope - > derivedFrom [ j ] . scope ) {
2011-01-21 07:42:41 +01:00
const Scope * derivedFrom = scope - > derivedFrom [ j ] . scope ;
2010-01-16 08:47:46 +01:00
2010-08-13 23:57:53 +02:00
// Name of base class..
2011-01-21 07:42:41 +01:00
const std : : string baseName = derivedFrom - > className ;
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 ;
// 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 ( ) ) {
2011-04-20 18:03:16 +02:00
// Declaring base class pointer
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok , baseName . c_str ( ) ) ) {
2011-04-20 18:03:16 +02:00
if ( Token : : Match ( tok - > previous ( ) , ( " [;{}] " + baseName + " * %var% ; " ) . c_str ( ) ) )
basepointer . insert ( tok - > tokAt ( 2 ) - > varId ( ) ) ;
}
// Assign base class pointer with pointer to derived class instance
if ( Token : : Match ( tok , " [;{}] %var% = " ) & &
tok - > next ( ) - > varId ( ) > 0 & &
2011-10-13 20:53:06 +02:00
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
if ( Token : : Match ( tok , " delete %var% ; " ) & &
tok - > next ( ) - > varId ( ) & &
2011-10-13 20:53:06 +02:00
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.
2011-01-21 07:42:41 +01:00
const Function * base_destructor = derivedFrom - > getDestructor ( ) ;
2010-08-13 23:57:53 +02:00
const Token * base = 0 ;
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 ) {
2011-01-21 07:42:41 +01:00
if ( derivedFrom - > derivedFrom . empty ( ) )
virtualDestructorError ( derivedFrom - > classDef , baseName , derivedClass - > str ( ) ) ;
2011-10-13 20:53:06 +02: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.)
2011-01-17 07:21:59 +01:00
if ( base_destructor - > access = = Public )
2010-08-13 23:57:53 +02:00
virtualDestructorError ( base , baseName , derivedClass - > str ( ) ) ;
}
}
}
2010-01-16 08:47:46 +01:00
}
}
}
2010-11-14 06:50:33 +01:00
void CheckClass : : virtualDestructorError ( const Token * tok , const std : : string & Base , const std : : string & Derived )
2010-01-16 08:47:46 +01:00
{
2010-11-14 06:50:33 +01:00
reportError ( tok , Severity : : error , " virtualDestructor " , " Class " + Base + " which is inherited by class " + Derived + " does not have a virtual destructor " ) ;
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 ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
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 ( ; ; ) {
2010-01-16 08:47:46 +01:00
tok = Token : : findmatch ( tok , " this - %var% " ) ;
2010-04-02 07:30:58 +02:00
if ( ! tok )
2010-01-16 08:47:46 +01:00
break ;
2010-04-02 07:30:58 +02:00
if ( ! Token : : simpleMatch ( tok - > previous ( ) , " * " ) )
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 )
{
reportError ( tok , Severity : : warning , " thisSubtraction " , " Suspicious pointer subtraction " ) ;
}
//---------------------------------------------------------------------------
// 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 ( )
{
2011-01-05 21:20:21 +01:00
if ( ! _settings - > isEnabled ( " information " ) | | _settings - > ifcfg )
2010-01-23 09:38:35 +01:00
return ;
2010-10-20 22:15:35 +02:00
// Don't check C# and JAVA classes
2011-10-13 20:53:06 +02:00
if ( _tokenizer - > isJavaOrCSharp ( ) ) {
2010-10-20 22:15:35 +02:00
return ;
}
2010-08-07 12:41:11 +02:00
createSymbolDatabase ( ) ;
2011-03-11 01:43:29 +01:00
std : : list < Scope > : : const_iterator scope ;
2010-01-23 09:19:22 +01:00
2011-10-13 20:53:06 +02:00
for ( scope = symbolDatabase - > scopeList . begin ( ) ; scope ! = symbolDatabase - > scopeList . end ( ) ; + + scope ) {
2010-11-20 07:26:50 +01:00
// only check classes and structures
2011-01-17 18:29:19 +01:00
if ( ! scope - > isClassOrStruct ( ) )
2010-11-20 07:26:50 +01:00
continue ;
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?
2011-10-13 20:53:06 +02:00
if ( func - > type = = Function : : eFunction & & func - > hasBody & & ! func - > isFriend & & ! func - > isStatic & & ! func - > isConst & & ! func - > isVirtual ) {
2010-07-26 16:46:37 +02:00
// get last token of return type
2010-11-02 18:28:55 +01:00
const Token * previous = func - > tokenDef - > isName ( ) ? func - > token - > previous ( ) : func - > token - > tokAt ( - 2 ) ;
2010-11-20 07:26:50 +01:00
while ( previous & & previous - > str ( ) = = " :: " )
2010-07-26 16:46:37 +02:00
previous = previous - > tokAt ( - 2 ) ;
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 , " *|& " ) ) {
2010-11-02 18:28:55 +01:00
const Token * temp = func - > token - > previous ( ) ;
2010-01-23 09:19:22 +01:00
2010-07-26 16:46:37 +02:00
while ( ! Token : : Match ( temp - > previous ( ) , " ;|}|{|public:|protected:|private: " ) )
temp = temp - > previous ( ) ;
2010-01-24 13:45:56 +01:00
2010-07-26 16:46:37 +02:00
if ( temp - > 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 ( ) , " *|& > " ) ) {
2010-11-02 18:28:55 +01:00
const Token * temp = func - > token - > previous ( ) ;
2010-07-26 16:46:37 +02:00
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 ( ) ;
if ( temp - > str ( ) = = " const " )
2010-04-09 19:15:39 +02:00
break ;
}
2010-07-26 16:46:37 +02:00
if ( temp - > str ( ) ! = " const " )
2010-04-09 19:15:39 +02: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
2011-10-13 20:53:06 +02:00
if ( previous - > isName ( ) ) {
2010-07-26 16:46:37 +02:00
bool allupper = true ;
const std : : string s ( previous - > str ( ) ) ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type pos = 0 ; pos < s . size ( ) ; + + pos ) {
2010-08-06 21:02:43 +02:00
const char ch = s [ pos ] ;
2011-10-13 20:53:06 +02:00
if ( ! ( ch = = ' _ ' | | ( ch > = ' A ' & & ch < = ' Z ' ) ) ) {
2010-07-26 16:46:37 +02:00
allupper = false ;
break ;
2010-01-23 20:59:20 +01:00
}
2010-03-05 17:06:25 +01:00
}
2010-07-26 16:46:37 +02:00
2010-09-06 19:04:14 +02:00
if ( allupper & & previous - > str ( ) . size ( ) > 2 )
2010-07-26 16:46:37 +02:00
continue ;
2010-03-05 17:06:25 +01:00
}
}
2010-01-23 20:59:20 +01:00
2011-01-01 17:54:37 +01:00
const Token * paramEnd = func - > arg - > link ( ) ;
2010-03-05 17:06:25 +01:00
2010-07-26 16:46:37 +02:00
// check if base class function is virtual
2011-10-13 20:53:06 +02:00
if ( ! scope - > derivedFrom . empty ( ) ) {
2011-03-11 01:43:29 +01:00
if ( isVirtualFunc ( & ( * scope ) , func - > tokenDef ) )
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
2010-07-26 16:46:37 +02:00
// if nothing non-const was found. write error..
2011-10-13 20:53:06 +02:00
if ( checkConstFunc ( & ( * scope ) , paramEnd ) ) {
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
2010-11-02 18:28:55 +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 + = " ] " ;
2010-11-02 18:28:55 +01:00
if ( func - > isInline )
checkConstError ( func - > token , classname , functionName ) ;
2010-07-26 16:46:37 +02:00
else // not inline
2010-11-02 18:28:55 +01:00
checkConstError2 ( func - > token , func - > tokenDef , classname , functionName ) ;
2010-04-18 15:40:31 +02:00
}
2010-03-28 11:46:42 +02:00
}
}
}
}
2011-01-17 18:29:19 +01:00
bool CheckClass : : isMemberVar ( const Scope * scope , const Token * tok )
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 ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok - > tokAt ( - 2 ) , " %var% . %var% " ) ) {
2011-03-27 19:59:12 +02:00
tok = tok - > tokAt ( - 2 ) ;
again = true ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok - > tokAt ( - 2 ) , " ] . %var% " ) ) {
2011-03-27 19:59:12 +02:00
tok = tok - > tokAt ( - 2 ) - > link ( ) - > previous ( ) ;
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 " ) ;
2011-01-18 07:32:06 +01:00
return ! var - > isMutable ( ) ;
2011-01-16 11:18:12 +01:00
}
}
// not found in this class
2011-10-13 20:53:06 +02:00
if ( ! scope - > derivedFrom . empty ( ) ) {
2011-01-16 11:18:12 +01:00
// check each base class
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < scope - > derivedFrom . size ( ) ; + + i ) {
2011-01-16 11:18:12 +01:00
// find the base class
2011-01-21 07:42:41 +01:00
const Scope * derivedFrom = scope - > derivedFrom [ i ] . scope ;
2011-01-16 11:18:12 +01:00
// find the function in the base class
2011-10-13 20:53:06 +02:00
if ( derivedFrom ) {
2011-01-21 07:42:41 +01:00
if ( isMemberVar ( derivedFrom , tok ) )
2011-01-16 11:18:12 +01:00
return true ;
}
}
}
return false ;
}
2011-03-23 00:23:36 +01:00
static int countParameters ( const Token * tok )
{
if ( Token : : Match ( tok - > tokAt ( 2 ) , " void| ) " ) )
return 0 ;
int numpar = 1 ;
int parlevel = 0 ;
2011-10-13 20:53:06 +02:00
for ( ; tok ; tok = tok - > next ( ) ) {
2011-03-23 00:23:36 +01:00
if ( tok - > str ( ) = = " ( " )
+ + parlevel ;
2011-10-13 20:53:06 +02:00
else if ( tok - > str ( ) = = " ) " ) {
2011-03-23 00:23:36 +01:00
if ( parlevel < = 1 )
break ;
- - parlevel ;
}
2011-10-13 20:53:06 +02:00
else if ( parlevel = = 1 & & tok - > str ( ) = = " , " ) {
2011-03-23 00:23:36 +01:00
+ + numpar ;
}
}
return numpar ;
}
2011-01-17 18:29:19 +01:00
bool CheckClass : : isConstMemberFunc ( const Scope * scope , const Token * tok )
2011-01-16 11:18:12 +01:00
{
2011-03-23 00:23:36 +01:00
unsigned int args = countParameters ( tok ) ;
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
2011-08-23 02:34:00 +02:00
unsigned int matches = 0 ;
unsigned int consts = 0 ;
2011-01-16 11:18:12 +01:00
2011-10-13 20:53:06 +02:00
for ( func = scope - > functionList . begin ( ) ; func ! = scope - > functionList . end ( ) ; + + func ) {
2011-03-23 00:23:36 +01:00
/** @todo we need to look at the argument types when there are overloaded functions
* with the same number of arguments */
2011-10-13 20:53:06 +02:00
if ( func - > tokenDef - > str ( ) = = tok - > str ( ) & & func - > argCount ( ) = = args ) {
2011-08-23 02:34:00 +02:00
matches + + ;
if ( func - > isConst )
consts + + ;
}
2011-01-16 11:18:12 +01:00
}
2011-08-23 02:34:00 +02:00
// if there are multiple matches that are all const, return const
if ( matches > 0 & & matches = = consts )
return true ;
2011-01-16 11:18:12 +01:00
// not found in this class
2011-10-13 20:53:06 +02:00
if ( ! scope - > derivedFrom . empty ( ) ) {
2011-01-16 11:18:12 +01:00
// check each base class
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < scope - > derivedFrom . size ( ) ; + + i ) {
2011-01-16 11:18:12 +01:00
// find the base class
2011-01-21 07:42:41 +01:00
const Scope * derivedFrom = scope - > derivedFrom [ i ] . scope ;
2011-01-16 11:18:12 +01:00
// find the function in the base class
2011-10-13 20:53:06 +02:00
if ( derivedFrom ) {
2011-01-21 07:42:41 +01:00
if ( isConstMemberFunc ( derivedFrom , tok ) )
2011-01-16 11:18:12 +01:00
return true ;
}
}
}
return false ;
}
2011-01-17 18:29:19 +01:00
bool CheckClass : : checkConstFunc ( const Scope * scope , const Token * tok )
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..
unsigned int indentlevel = 0 ;
bool isconst = true ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok1 = tok ; tok1 ; tok1 = tok1 - > next ( ) ) {
2011-01-16 11:18:12 +01:00
if ( tok1 - > str ( ) = = " { " )
+ + indentlevel ;
2011-10-13 20:53:06 +02:00
else if ( tok1 - > str ( ) = = " } " ) {
2011-01-16 11:18:12 +01:00
if ( indentlevel < = 1 )
break ;
- - indentlevel ;
}
// assignment.. = += |= ..
2011-10-13 20:53:06 +02:00
else if ( tok1 - > isAssignmentOp ( ) ) {
if ( tok1 - > next ( ) - > str ( ) = = " this " ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
2011-10-13 20:53:06 +02:00
} else if ( isMemberVar ( scope , tok1 - > previous ( ) ) ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
}
}
// streaming: <<
2011-10-13 20:53:06 +02:00
else if ( tok1 - > str ( ) = = " << " & & isMemberVar ( scope , tok1 - > previous ( ) ) ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : simpleMatch ( tok1 - > previous ( ) , " ) << " ) & &
isMemberVar ( scope , tok1 - > tokAt ( - 2 ) ) ) {
2011-02-21 02:01:54 +01:00
isconst = false ;
break ;
}
2011-01-16 11:18:12 +01:00
2011-09-17 00:07:25 +02:00
// streaming: >>
2011-10-13 20:53:06 +02:00
else if ( tok1 - > str ( ) = = " >> " & & isMemberVar ( scope , tok1 - > next ( ) ) ) {
2011-09-17 00:07:25 +02:00
isconst = false ;
break ;
}
2011-01-16 11:18:12 +01:00
// increment/decrement (member variable?)..
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok1 , " ++|-- " ) ) {
2011-03-26 03:37:32 +01:00
// var++ and var--
2011-03-20 18:29:52 +01:00
if ( Token : : Match ( tok1 - > previous ( ) , " %var% " ) & &
2011-10-13 20:53:06 +02:00
tok1 - > previous ( ) - > str ( ) ! = " return " ) {
if ( isMemberVar ( scope , tok1 - > previous ( ) ) ) {
2011-03-20 18:29:52 +01:00
isconst = false ;
2011-03-26 04:22:28 +01:00
break ;
}
2011-03-20 18:29:52 +01:00
}
2011-03-26 03:37:32 +01:00
// var[...]++ and var[...]--
2011-10-13 20:53:06 +02:00
else if ( tok1 - > previous ( ) - > str ( ) = = " ] " ) {
if ( isMemberVar ( scope , tok1 - > previous ( ) - > link ( ) - > previous ( ) ) ) {
2011-03-26 03:37:32 +01:00
isconst = false ;
2011-03-26 04:22:28 +01:00
break ;
}
2011-03-26 03:37:32 +01:00
}
// ++var and --var
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok1 - > next ( ) , " %var% " ) ) {
if ( isMemberVar ( scope , tok1 - > next ( ) ) ) {
2011-03-20 18:29:52 +01:00
isconst = false ;
2011-03-26 04:22:28 +01:00
break ;
}
2011-03-20 18:29:52 +01:00
}
2011-01-16 11:18:12 +01:00
}
2011-06-04 04:00:27 +02:00
// std::map variable member
2011-10-13 20:53:06 +02:00
else if ( Token : : Match ( tok1 , " %var% [ " ) & & isMemberVar ( scope , tok1 ) ) {
2011-06-04 04:00:27 +02:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( tok1 - > varId ( ) ) ;
if ( var & & ( var - > typeStartToken ( ) - > str ( ) = = " map " | |
2011-10-13 20:53:06 +02:00
Token : : simpleMatch ( var - > typeStartToken ( ) , " std :: map " ) ) ) {
2011-06-04 04:00:27 +02:00
isconst = false ;
break ;
}
}
2011-01-16 11:18:12 +01:00
// function call..
else if ( Token : : Match ( tok1 , " %var% ( " ) & &
2011-10-13 20:53:06 +02:00
! ( Token : : Match ( tok1 , " return|c_str|if|string|switch|while|catch|for " ) | | tok1 - > isStandardType ( ) ) ) {
if ( ! isConstMemberFunc ( scope , tok1 ) ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
}
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok1 , " %var% < %any% > ( " ) ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok1 , " %var% . size|empty ( ) " ) && tok1->varId()) {
2011-07-30 16:30:31 +02:00
// assume all std::*::size() and std::*::empty() are const
2011-07-30 14:48:11 +02:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( tok1 - > varId ( ) ) ;
2011-07-30 16:30:31 +02:00
if ( var & & Token : : simpleMatch ( var - > typeStartToken ( ) , " std :: " ) )
tok1 = tok1 - > tokAt ( 4 ) ;
2011-07-30 14:48:11 +02:00
}
2011-01-16 11:18:12 +01:00
// delete..
2011-10-13 20:53:06 +02:00
else if ( tok1 - > str ( ) = = " delete " ) {
2011-01-16 11:18:12 +01:00
isconst = false ;
break ;
}
}
return isconst ;
}
// check if this function is defined virtual in the base classes
2011-01-17 18:29:19 +01:00
bool CheckClass : : isVirtualFunc ( const Scope * scope , const Token * functionToken ) const
2011-01-16 11:18:12 +01:00
{
// check each base class
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < scope - > derivedFrom . size ( ) ; + + i ) {
2011-01-16 11:18:12 +01:00
// check if base class exists in database
2011-10-13 20:53:06 +02:00
if ( scope - > derivedFrom [ i ] . scope ) {
2011-01-21 07:42:41 +01:00
const Scope * derivedFrom = scope - > derivedFrom [ i ] . scope ;
2011-01-16 11:18:12 +01:00
2011-01-17 18:29:19 +01:00
std : : list < Function > : : const_iterator func ;
2011-01-16 11:18:12 +01:00
// check if function defined in base class
2011-10-13 20:53:06 +02:00
for ( func = derivedFrom - > functionList . begin ( ) ; func ! = derivedFrom - > functionList . end ( ) ; + + func ) {
if ( func - > isVirtual ) {
2011-01-16 11:18:12 +01:00
const Token * tok = func - > tokenDef ;
2011-10-13 20:53:06 +02:00
if ( tok - > str ( ) = = functionToken - > str ( ) ) {
2011-01-16 11:18:12 +01:00
const Token * temp1 = tok - > previous ( ) ;
const Token * temp2 = functionToken - > previous ( ) ;
bool returnMatch = true ;
// check for matching return parameters
2011-10-13 20:53:06 +02:00
while ( temp1 - > str ( ) ! = " virtual " ) {
if ( temp1 - > str ( ) ! = temp2 - > str ( ) ) {
2011-01-16 11:18:12 +01:00
returnMatch = false ;
break ;
}
temp1 = temp1 - > previous ( ) ;
temp2 = temp2 - > previous ( ) ;
}
// check for matching function parameters
2011-10-13 20:53:06 +02:00
if ( returnMatch & & symbolDatabase - > argsMatch ( scope , tok - > tokAt ( 2 ) , functionToken - > tokAt ( 2 ) , std : : string ( " " ) , 0 ) ) {
2011-01-16 11:18:12 +01:00
return true ;
}
}
}
}
2011-10-13 20:53:06 +02:00
if ( ! derivedFrom - > derivedFrom . empty ( ) ) {
2011-01-16 11:18:12 +01:00
if ( isVirtualFunc ( derivedFrom , functionToken ) )
return true ;
}
2011-10-13 20:53:06 +02:00
} else {
2011-01-16 11:18:12 +01:00
// unable to find base class so assume it has a virtual function
return true ;
}
}
return false ;
}
2010-01-23 09:19:22 +01:00
void CheckClass : : checkConstError ( const Token * tok , const std : : string & classname , const std : : string & funcname )
{
2011-10-05 19:44:00 +02:00
reportError ( tok , Severity : : style , " functionConst " ,
2010-12-26 22:34:00 +01:00
" 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 function 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 task of the function first - is "
" it a function that must not change object internal state? " ) ;
2010-01-23 09:19:22 +01:00
}
2010-03-10 07:47:01 +01:00
void CheckClass : : checkConstError2 ( const Token * tok1 , const Token * tok2 , const std : : string & classname , const std : : string & funcname )
{
std : : list < const Token * > toks ;
toks . push_back ( tok1 ) ;
toks . push_back ( tok2 ) ;
2011-10-05 19:44:00 +02:00
reportError ( toks , Severity : : style , " functionConst " ,
2010-12-26 22:34:00 +01:00
" 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 function 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 task of the function first - is "
" it a function that must not change object internal state? " ) ;
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 ;
} ;
void CheckClass : : initializerList ( )
{
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 ;
createSymbolDatabase ( ) ;
std : : list < Scope > : : const_iterator info ;
// iterate through all scopes looking for classes and structures
2011-10-13 20:53:06 +02:00
for ( info = symbolDatabase - > scopeList . begin ( ) ; info ! = symbolDatabase - > scopeList . end ( ) ; + + info ) {
2011-09-28 03:07:37 +02:00
if ( ! info - > isClassOrStruct ( ) )
continue ;
std : : list < Function > : : const_iterator func ;
// iterate through all member functions looking for constructors
2011-10-13 20:53:06 +02:00
for ( func = info - > functionList . begin ( ) ; func ! = info - > functionList . end ( ) ; + + func ) {
if ( func - > type = = Function : : eConstructor & & 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
2011-10-13 20:53:06 +02:00
while ( tok & & tok - > str ( ) ! = " { " ) {
if ( Token : : Match ( tok , " %var% ( " ) ) {
2011-09-28 03:07:37 +02:00
const Variable * var = info - > getVariable ( tok - > str ( ) ) ;
if ( var )
vars . push_back ( VarInfo ( var , tok ) ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok - > tokAt ( 2 ) , " %var% = " ) ) {
2011-09-28 03:07:37 +02:00
var = info - > getVariable ( tok - > strAt ( 2 ) ) ;
if ( var )
vars . push_back ( VarInfo ( var , tok - > tokAt ( 2 ) ) ) ;
}
}
tok = tok - > next ( ) ;
}
// need at least 2 members to have out of order initialization
2011-10-13 20:53:06 +02:00
if ( vars . size ( ) > 1 ) {
for ( unsigned int i = 1 ; i < vars . size ( ) ; i + + ) {
2011-09-28 03:07:37 +02:00
// check for out of order initialization
if ( vars [ i ] . var - > index ( ) < vars [ i - 1 ] . var - > index ( ) )
initializerListError ( vars [ i ] . tok , vars [ i ] . var - > nameToken ( ) , info - > className , vars [ i ] . var - > name ( ) ) ;
}
}
}
}
}
}
}
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 ) ;
reportError ( toks , Severity : : style , " initializerList " ,
" Member variable ' " + classname + " :: " +
varname + " ' is in the wrong order in the initializer list. \n "
" Members are initialized in the order they are declared, not the "
" order they are in the initializer list. Keeping the initializer list "
" in the same order that the members were declared prevents order dependent "
" initialization errors. " ) ;
}