2009-10-19 20:57:11 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and Cppcheck team .
2009-10-19 20:57:11 +02: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 "checkexceptionsafety.h"
2011-12-09 19:53:00 +01:00
# include "symboldatabase.h"
2009-10-19 20:57:11 +02:00
# include "token.h"
//---------------------------------------------------------------------------
// Register CheckExceptionSafety..
2011-10-13 20:53:06 +02:00
namespace {
CheckExceptionSafety instance ;
2009-10-19 20:57:11 +02:00
}
//---------------------------------------------------------------------------
void CheckExceptionSafety : : destructors ( )
{
2011-12-09 19:53:00 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2009-10-19 20:57:11 +02:00
2011-12-09 19:53:00 +01:00
// Perform check..
2012-10-14 17:40:17 +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 ] ;
const Function * j = scope - > function ;
if ( j ) {
2011-12-09 19:53:00 +01:00
// only looking for destructors
2012-10-14 17:40:17 +02:00
if ( j - > type = = Function : : eDestructor ) {
2011-12-09 19:53:00 +01:00
// Inspect this destructor..
2012-10-14 17:40:17 +02:00
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2012-06-17 14:33:18 +02:00
// Skip try blocks
if ( Token : : simpleMatch ( tok , " try { " ) ) {
tok = tok - > next ( ) - > link ( ) ;
}
2011-12-09 19:53:00 +01:00
// throw found within a destructor
if ( tok - > str ( ) = = " throw " ) {
destructorsError ( tok ) ;
break ;
}
}
2009-10-19 20:57:11 +02:00
}
}
}
}
2009-10-31 18:58:42 +01:00
2009-11-08 09:54:08 +01:00
void CheckExceptionSafety : : deallocThrow ( )
{
2013-03-03 11:41:59 +01:00
if ( ! _settings - > isEnabled ( " warning " ) )
2012-01-21 19:11:06 +01:00
return ;
2011-12-09 19:53:00 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2010-12-31 13:58:17 +01:00
// Deallocate a global/member pointer and then throw exception
// the pointer will be a dead pointer
2012-10-14 17:40:17 +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 ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
// only looking for delete now
if ( tok - > str ( ) ! = " delete " )
continue ;
// Check if this is something similar with: "delete p;"
tok = tok - > next ( ) ;
if ( Token : : simpleMatch ( tok , " [ ] " ) )
tok = tok - > tokAt ( 2 ) ;
if ( ! tok )
break ;
if ( ! Token : : Match ( tok , " %var% ; " ) )
continue ;
// we only look for global variables
2013-02-06 06:39:58 +01:00
const Variable * var = tok - > variable ( ) ;
2012-10-14 17:40:17 +02:00
if ( ! var | | ! ( var - > isGlobal ( ) | | var - > isStatic ( ) ) )
continue ;
2013-02-06 06:39:58 +01:00
const unsigned int varid ( tok - > varId ( ) ) ;
2012-10-14 17:40:17 +02:00
// Token where throw occurs
2014-02-16 11:47:52 +01:00
const Token * ThrowToken = nullptr ;
2012-10-14 17:40:17 +02:00
// is there a throw after the deallocation?
const Token * const end2 = tok - > scope ( ) - > classEnd ;
for ( const Token * tok2 = tok ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
// Throw after delete -> Dead pointer
if ( tok2 - > str ( ) = = " throw " ) {
if ( _settings - > inconclusive ) { // For inconclusive checking, throw directly.
deallocThrowError ( tok2 , tok - > str ( ) ) ;
break ;
}
ThrowToken = tok2 ;
2012-01-21 19:11:06 +01:00
}
2009-11-08 09:54:08 +01:00
2012-10-14 17:40:17 +02:00
// Variable is assigned -> Bail out
else if ( Token : : Match ( tok2 , " %varid% = " , varid ) ) {
2013-01-16 15:37:07 +01:00
if ( ThrowToken ) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer.
2012-10-14 17:40:17 +02:00
deallocThrowError ( ThrowToken , tok2 - > str ( ) ) ;
break ;
}
// Variable passed to function. Assume it becomes assigned -> Bail out
else if ( Token : : Match ( tok2 , " [,(] &| %varid% [,)] " , varid ) ) // TODO: No bailout if passed by value or as const reference
break ;
2009-11-08 09:54:08 +01:00
}
}
}
}
2011-02-05 10:11:09 +01:00
//---------------------------------------------------------------------------
// catch(const exception & err)
// {
// throw err; // <- should be just "throw;"
// }
//---------------------------------------------------------------------------
void CheckExceptionSafety : : checkRethrowCopy ( )
{
2011-08-07 09:28:08 +02:00
if ( ! _settings - > isEnabled ( " style " ) )
2011-02-05 10:11:09 +01:00
return ;
2012-01-26 16:50:59 +01:00
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
2011-02-05 10:11:09 +01:00
2012-01-26 16:50:59 +01:00
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eCatch )
continue ;
const unsigned int varid = i - > classStart - > tokAt ( - 2 ) - > varId ( ) ;
if ( varid ) {
2012-02-02 16:17:42 +01:00
for ( const Token * tok = i - > classStart - > next ( ) ; tok & & tok ! = i - > classEnd ; tok = tok - > next ( ) ) {
if ( Token : : simpleMatch ( tok , " catch ( " ) & & tok - > next ( ) - > link ( ) & & tok - > next ( ) - > link ( ) - > next ( ) ) // Don't check inner catch - it is handled in another iteration of outer loop.
tok = tok - > next ( ) - > link ( ) - > next ( ) - > link ( ) ;
else if ( Token : : Match ( tok , " throw %varid% ; " , varid ) )
rethrowCopyError ( tok , tok - > strAt ( 1 ) ) ;
}
2012-01-26 16:50:59 +01:00
}
2011-02-05 10:11:09 +01:00
}
}
2012-02-02 16:17:42 +01:00
//---------------------------------------------------------------------------
// try {} catch (std::exception err) {} <- Should be "std::exception& err"
//---------------------------------------------------------------------------
void CheckExceptionSafety : : checkCatchExceptionByValue ( )
{
if ( ! _settings - > isEnabled ( " style " ) )
return ;
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( std : : list < Scope > : : const_iterator i = symbolDatabase - > scopeList . begin ( ) ; i ! = symbolDatabase - > scopeList . end ( ) ; + + i ) {
if ( i - > type ! = Scope : : eCatch )
continue ;
// Find a pass-by-value declaration in the catch(), excluding basic types
// e.g. catch (std::exception err)
2013-02-06 06:39:58 +01:00
const Variable * var = i - > classStart - > tokAt ( - 2 ) - > variable ( ) ;
2012-02-02 16:17:42 +01:00
if ( var & & var - > isClass ( ) & & ! var - > isPointer ( ) & & ! var - > isReference ( ) )
catchExceptionByValueError ( i - > classDef ) ;
}
}
2014-04-10 16:17:10 +02:00
2014-04-20 20:40:55 +02:00
static const Token * functionThrowsRecursive ( const Function * function , std : : set < const Function * > & recursive )
2014-04-10 16:17:10 +02:00
{
2014-04-20 20:40:55 +02:00
// check for recursion and bail if found
if ( recursive . find ( function ) ! = recursive . end ( ) )
return nullptr ;
2014-04-21 08:01:01 +02:00
if ( ! function - > functionScope )
return nullptr ;
2014-04-20 20:40:55 +02:00
for ( const Token * tok = function - > functionScope - > classStart - > next ( ) ;
tok ! = function - > functionScope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " try " ) {
// just bail for now
break ;
}
if ( tok - > str ( ) = = " throw " ) {
return tok ;
} else if ( tok - > function ( ) ) {
const Function * called = tok - > function ( ) ;
// check if called function has an exception specification
if ( called - > isThrow & & called - > throwArg ) {
return tok ;
} else if ( called - > isNoExcept & & called - > noexceptArg & &
called - > noexceptArg - > str ( ) ! = " true " ) {
return tok ;
} else if ( functionThrowsRecursive ( called , recursive ) ) {
return tok ;
2014-04-10 16:17:10 +02:00
}
}
}
2014-04-20 20:40:55 +02:00
return nullptr ;
}
static const Token * functionThrows ( const Function * function )
{
std : : set < const Function * > recursive ;
return functionThrowsRecursive ( function , recursive ) ;
2014-04-10 16:17:10 +02:00
}
//--------------------------------------------------------------------------
2014-04-20 20:40:55 +02:00
// void func() noexcept { throw x; }
2014-04-10 16:17:10 +02:00
// void func() throw() { throw x; }
2014-04-20 20:40:55 +02:00
// void func() __attribute__((nothrow)); void func() { throw x; }
2014-04-10 16:17:10 +02:00
//--------------------------------------------------------------------------
void CheckExceptionSafety : : nothrowThrows ( )
{
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
2014-04-20 20:40:55 +02:00
// check noexcept functions
if ( scope - > function & & scope - > function - > isNoExcept & &
( ! scope - > function - > noexceptArg | | scope - > function - > noexceptArg - > str ( ) = = " true " ) ) {
const Token * throws = functionThrows ( scope - > function ) ;
if ( throws )
noexceptThrowError ( throws ) ;
}
// check throw() functions
else if ( scope - > function & & scope - > function - > isThrow & & ! scope - > function - > throwArg ) {
const Token * throws = functionThrows ( scope - > function ) ;
if ( throws )
nothrowThrowError ( throws ) ;
}
// check __attribute__((nothrow)) functions
else if ( scope - > function & & scope - > function - > isAttributeNothrow ( ) ) {
const Token * throws = functionThrows ( scope - > function ) ;
if ( throws )
nothrowAttributeThrowError ( throws ) ;
2014-04-20 08:58:36 +02:00
}
}
}
//--------------------------------------------------------------------------
// void func() { functionWithExceptionSpecification(); }
//--------------------------------------------------------------------------
void CheckExceptionSafety : : unhandledExceptionSpecification ( )
{
const SymbolDatabase * const symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
// only check functions without exception epecification
if ( scope - > function & & ! scope - > function - > isThrow ) {
for ( const Token * tok = scope - > function - > functionScope - > classStart - > next ( ) ;
tok ! = scope - > function - > functionScope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > str ( ) = = " try " ) {
break ;
} else if ( tok - > function ( ) ) {
const Function * called = tok - > function ( ) ;
// check if called function has an exception specification
if ( called - > isThrow & & called - > throwArg ) {
unhandledExceptionSpecificationError ( tok , called - > tokenDef , scope - > function - > name ( ) ) ;
break ;
}
2014-04-10 16:17:10 +02:00
}
}
}
}
}