2010-10-31 12:31:11 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2010-10-31 12:31:11 +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 "checkuninitvar.h"
2017-05-27 04:33:47 +02:00
2015-08-03 09:20:50 +02:00
# include "astutils.h"
2023-10-21 16:58:29 +02:00
# include "ctu.h"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# include "library.h"
# include "mathlib.h"
# include "settings.h"
2011-12-13 21:57:27 +01:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
2022-01-27 19:03:20 +01:00
# include "checknullpointer.h" // CheckNullPointer::isPointerDeref
# include <algorithm>
2013-01-24 19:41:15 +01:00
# include <cassert>
2022-01-27 19:03:20 +01:00
# include <functional>
# include <initializer_list>
2017-05-27 04:33:47 +02:00
# include <list>
# include <map>
2022-01-27 19:03:20 +01:00
# include <unordered_set>
2017-05-27 04:33:47 +02:00
# include <utility>
2022-01-27 19:03:20 +01:00
# include <vector>
2020-04-13 13:44:48 +02:00
namespace tinyxml2 {
class XMLElement ;
}
2010-10-31 12:31:11 +01:00
//---------------------------------------------------------------------------
2022-09-24 11:59:13 +02:00
// CWE ids used:
2023-10-08 09:10:17 +02:00
static const CWE CWE_USE_OF_UNINITIALIZED_VARIABLE ( 457U ) ;
2022-09-24 11:59:13 +02:00
2010-10-31 12:31:11 +01:00
// Register this check class (by creating a static instance of it)
2011-10-13 20:53:06 +02:00
namespace {
2011-11-30 18:57:52 +01:00
CheckUninitVar instance ;
2010-10-31 12:31:11 +01:00
}
//---------------------------------------------------------------------------
2019-02-26 19:26:46 +01:00
// get ast parent, skip possible address-of and casts
static const Token * getAstParentSkipPossibleCastAndAddressOf ( const Token * vartok , bool * unknown )
{
if ( unknown )
* unknown = false ;
if ( ! vartok )
return nullptr ;
const Token * parent = vartok - > astParent ( ) ;
while ( Token : : Match ( parent , " .|:: " ) )
parent = parent - > astParent ( ) ;
if ( ! parent )
return nullptr ;
if ( parent - > isUnaryOp ( " & " ) )
parent = parent - > astParent ( ) ;
else if ( parent - > str ( ) = = " & " & & vartok = = parent - > astOperand2 ( ) & & Token : : Match ( parent - > astOperand1 ( ) - > previous ( ) , " ( %type% ) " ) ) {
parent = parent - > astParent ( ) ;
if ( unknown )
* unknown = true ;
}
while ( parent & & parent - > isCast ( ) )
parent = parent - > astParent ( ) ;
return parent ;
}
2023-10-12 13:56:47 +02:00
static std : : map < nonneg int , VariableValue > getVariableValues ( const Token * tok ) {
std : : map < nonneg int , VariableValue > ret ;
if ( ! tok | | ! tok - > scope ( ) - > isExecutable ( ) )
return ret ;
while ( tok & & tok - > str ( ) ! = " { " ) {
if ( tok - > str ( ) = = " } " ) {
if ( tok - > link ( ) - > isBinaryOp ( ) )
tok = tok - > link ( ) - > previous ( ) ;
else
break ;
}
if ( Token : : Match ( tok , " %var% =|{ " ) & & tok - > next ( ) - > isBinaryOp ( ) & & tok - > varId ( ) & & ret . count ( tok - > varId ( ) ) = = 0 ) {
const Token * rhs = tok - > next ( ) - > astOperand2 ( ) ;
if ( rhs & & rhs - > hasKnownIntValue ( ) )
ret [ tok - > varId ( ) ] = VariableValue ( rhs - > getKnownIntValue ( ) ) ;
}
tok = tok - > previous ( ) ;
}
return ret ;
}
2022-04-27 17:37:37 +02:00
bool CheckUninitVar : : diag ( const Token * tok )
{
if ( ! tok )
return true ;
while ( Token : : Match ( tok - > astParent ( ) , " *|&|. " ) )
tok = tok - > astParent ( ) ;
return ! mUninitDiags . insert ( tok ) . second ;
}
2011-12-13 21:57:27 +01:00
void CheckUninitVar : : check ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckUninitVar::check " ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2011-12-13 21:57:27 +01:00
2016-12-21 23:11:11 +01:00
std : : set < std : : string > arrayTypeDefs ;
2018-06-16 16:10:28 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2016-12-21 23:11:11 +01:00
if ( Token : : Match ( tok , " %name% [ " ) & & tok - > variable ( ) & & Token : : Match ( tok - > variable ( ) - > typeStartToken ( ) , " %type% %var% ; " ) )
arrayTypeDefs . insert ( tok - > variable ( ) - > typeStartToken ( ) - > str ( ) ) ;
}
2013-01-26 07:28:11 +01:00
// check every executable scope
2018-10-24 11:29:15 +02:00
for ( const Scope & scope : symbolDatabase - > scopeList ) {
if ( scope . isExecutable ( ) ) {
checkScope ( & scope , arrayTypeDefs ) ;
2011-12-13 21:57:27 +01:00
}
}
}
2016-12-21 23:11:11 +01:00
void CheckUninitVar : : checkScope ( const Scope * scope , const std : : set < std : : string > & arrayTypeDefs )
2011-12-13 21:57:27 +01:00
{
2018-10-24 11:29:15 +02:00
for ( const Variable & var : scope - > varlist ) {
2019-08-02 21:14:29 +02:00
if ( ( mTokenizer - > isCPP ( ) & & var . type ( ) & & ! var . isPointer ( ) & & var . type ( ) - > needInitialization ! = Type : : NeedInitialization : : True ) | |
2018-10-24 11:29:15 +02:00
var . isStatic ( ) | | var . isExtern ( ) | | var . isReference ( ) )
2012-05-25 13:40:18 +02:00
continue ;
2015-01-21 12:20:03 +01:00
2013-12-24 07:39:15 +01:00
// don't warn for try/catch exception variable
2018-10-24 11:29:15 +02:00
if ( var . isThrow ( ) )
2015-01-21 12:20:03 +01:00
continue ;
2018-10-24 11:29:15 +02:00
if ( Token : : Match ( var . nameToken ( ) - > next ( ) , " [({:] " ) )
2012-05-25 13:40:18 +02:00
continue ;
2015-01-21 12:20:03 +01:00
2018-10-24 11:29:15 +02:00
if ( Token : : Match ( var . nameToken ( ) , " %name% = " ) ) { // Variable is initialized, but Rhs might be not
checkRhs ( var . nameToken ( ) , var , NO_ALLOC , 0U , emptyString ) ;
2015-01-21 12:20:03 +01:00
continue ;
}
2018-10-24 11:29:15 +02:00
if ( Token : : Match ( var . nameToken ( ) , " %name% ) ( " ) & & Token : : simpleMatch ( var . nameToken ( ) - > linkAt ( 2 ) , " ) = " ) ) { // Function pointer is initialized, but Rhs might be not
checkRhs ( var . nameToken ( ) - > linkAt ( 2 ) - > next ( ) , var , NO_ALLOC , 0U , emptyString ) ;
2015-01-30 19:16:25 +01:00
continue ;
}
2015-01-21 12:20:03 +01:00
2018-10-24 11:29:15 +02:00
if ( var . isArray ( ) | | var . isPointerToArray ( ) ) {
const Token * tok = var . nameToken ( ) - > next ( ) ;
if ( var . isPointerToArray ( ) )
2015-08-25 21:19:19 +02:00
tok = tok - > next ( ) ;
2015-07-23 14:51:38 +02:00
while ( Token : : simpleMatch ( tok - > link ( ) , " ] [ " ) )
tok = tok - > link ( ) - > next ( ) ;
2015-10-24 12:06:40 +02:00
if ( Token : : Match ( tok - > link ( ) , " ] =|{ " ) )
2015-07-23 14:51:38 +02:00
continue ;
}
2018-10-24 11:29:15 +02:00
bool stdtype = mTokenizer - > isC ( ) & & arrayTypeDefs . find ( var . typeStartToken ( ) - > str ( ) ) = = arrayTypeDefs . end ( ) ;
const Token * tok = var . typeStartToken ( ) ;
for ( ; tok ! = var . nameToken ( ) & & tok - > str ( ) ! = " < " ; tok = tok - > next ( ) ) {
2016-04-22 06:02:54 +02:00
if ( tok - > isStandardType ( ) | | tok - > isEnumType ( ) )
2012-05-25 13:40:18 +02:00
stdtype = true ;
2012-06-19 20:04:10 +02:00
}
2022-01-27 19:45:27 +01:00
if ( var . isArray ( ) & & ! stdtype ) { // std::array
if ( ! ( var . isStlType ( ) & & Token : : simpleMatch ( var . typeStartToken ( ) , " std :: array " ) & & var . valueType ( ) & &
var . valueType ( ) - > containerTypeToken & & var . valueType ( ) - > containerTypeToken - > isStandardType ( ) ) )
continue ;
}
2015-01-21 12:20:03 +01:00
2012-06-22 16:26:43 +02:00
while ( tok & & tok - > str ( ) ! = " ; " )
tok = tok - > next ( ) ;
2013-12-12 12:36:49 +01:00
if ( ! tok )
continue ;
2015-01-21 12:20:03 +01:00
2016-08-04 14:39:54 +02:00
if ( tok - > astParent ( ) & & Token : : simpleMatch ( tok - > astParent ( ) - > previous ( ) , " for ( " ) & &
2018-10-24 11:29:15 +02:00
checkLoopBody ( tok - > astParent ( ) - > link ( ) - > next ( ) , var , var . isArray ( ) ? ARRAY : NO_ALLOC , emptyString , true ) )
2016-08-04 14:39:54 +02:00
continue ;
2018-10-24 11:29:15 +02:00
if ( var . isArray ( ) ) {
2019-07-24 18:20:23 +02:00
bool init = false ;
for ( const Token * parent = var . nameToken ( ) ; parent ; parent = parent - > astParent ( ) ) {
2020-01-16 16:37:10 +01:00
if ( parent - > str ( ) = = " = " ) {
2019-07-24 18:20:23 +02:00
init = true ;
2020-01-16 16:37:10 +01:00
break ;
}
2019-07-24 18:20:23 +02:00
}
2022-08-21 17:21:02 +02:00
if ( ! init ) {
Alloc alloc = ARRAY ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > variableValue = getVariableValues ( var . typeStartToken ( ) ) ;
2019-07-24 18:20:23 +02:00
checkScopeForVariable ( tok , var , nullptr , nullptr , & alloc , emptyString , variableValue ) ;
2022-08-21 17:21:02 +02:00
}
2015-07-23 14:51:38 +02:00
continue ;
}
2018-10-24 11:29:15 +02:00
if ( stdtype | | var . isPointer ( ) ) {
2015-01-23 19:38:39 +01:00
Alloc alloc = NO_ALLOC ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > variableValue = getVariableValues ( var . typeStartToken ( ) ) ;
2018-10-24 11:29:15 +02:00
checkScopeForVariable ( tok , var , nullptr , nullptr , & alloc , emptyString , variableValue ) ;
2013-12-04 20:32:20 +01:00
}
2018-10-24 11:29:15 +02:00
if ( var . type ( ) )
checkStruct ( tok , var ) ;
2013-12-09 15:58:42 +01:00
}
2013-09-30 06:35:31 +02:00
2013-12-09 15:58:42 +01:00
if ( scope - > function ) {
2018-10-24 11:29:15 +02:00
for ( const Variable & arg : scope - > function - > argumentList ) {
if ( arg . declarationId ( ) & & Token : : Match ( arg . typeStartToken ( ) , " %type% * %name% [,)] " ) ) {
2013-12-09 15:58:42 +01:00
// Treat the pointer as initialized until it is assigned by malloc
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2021-05-13 20:21:02 +02:00
if ( ! Token : : Match ( tok , " [;{}] %varid% = " , arg . declarationId ( ) ) )
2020-06-28 21:00:50 +02:00
continue ;
2021-05-13 20:21:02 +02:00
const Token * allocFuncCallToken = findAllocFuncCallToken ( tok - > tokAt ( 2 ) - > astOperand2 ( ) , mSettings - > library ) ;
if ( ! allocFuncCallToken )
continue ;
const Library : : AllocFunc * allocFunc = mSettings - > library . getAllocFuncInfo ( allocFuncCallToken ) ;
2020-06-28 21:00:50 +02:00
if ( ! allocFunc | | allocFunc - > initData )
continue ;
if ( arg . typeStartToken ( ) - > strAt ( - 1 ) = = " struct " | | ( arg . type ( ) & & arg . type ( ) - > isStructType ( ) ) )
checkStruct ( tok , arg ) ;
else if ( arg . typeStartToken ( ) - > isStandardType ( ) | | arg . typeStartToken ( ) - > isEnumType ( ) ) {
Alloc alloc = NO_ALLOC ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > variableValue ;
2020-06-28 21:00:50 +02:00
checkScopeForVariable ( tok - > next ( ) , arg , nullptr , nullptr , & alloc , emptyString , variableValue ) ;
2013-12-09 15:58:42 +01:00
}
}
}
}
}
}
2013-09-30 06:35:31 +02:00
2015-01-22 13:51:43 +01:00
void CheckUninitVar : : checkStruct ( const Token * tok , const Variable & structvar )
2013-12-09 15:58:42 +01:00
{
const Token * typeToken = structvar . typeStartToken ( ) ;
2022-01-25 12:14:20 +01:00
while ( Token : : Match ( typeToken , " %name% :: " ) )
typeToken = typeToken - > tokAt ( 2 ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-10-24 11:29:15 +02:00
for ( const Scope * scope2 : symbolDatabase - > classAndStructScopes ) {
2015-01-23 19:38:39 +01:00
if ( scope2 - > className = = typeToken - > str ( ) & & scope2 - > numConstructors = = 0U ) {
2018-10-24 11:29:15 +02:00
for ( const Variable & var : scope2 - > varlist ) {
2018-09-22 16:52:34 +02:00
if ( var . isStatic ( ) | | var . hasDefault ( ) | | var . isArray ( ) | |
2019-08-02 21:14:29 +02:00
( ! mTokenizer - > isC ( ) & & var . isClass ( ) & & ( ! var . type ( ) | | var . type ( ) - > needInitialization ! = Type : : NeedInitialization : : True ) ) )
2015-01-21 13:10:38 +01:00
continue ;
// is the variable declared in a inner union?
bool innerunion = false ;
2018-10-24 11:29:15 +02:00
for ( const Scope * innerScope : scope2 - > nestedList ) {
if ( innerScope - > type = = Scope : : eUnion ) {
if ( var . typeStartToken ( ) - > linenr ( ) > = innerScope - > bodyStart - > linenr ( ) & &
var . typeStartToken ( ) - > linenr ( ) < = innerScope - > bodyEnd - > linenr ( ) ) {
2015-01-21 13:10:38 +01:00
innerunion = true ;
break ;
2013-09-30 06:35:31 +02:00
}
2013-12-09 15:58:42 +01:00
}
2013-01-16 20:28:29 +01:00
}
2015-01-21 13:10:38 +01:00
if ( ! innerunion ) {
2015-01-23 19:38:39 +01:00
Alloc alloc = NO_ALLOC ;
2015-01-21 13:10:38 +01:00
const Token * tok2 = tok ;
if ( tok - > str ( ) = = " } " )
tok2 = tok2 - > next ( ) ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > variableValue = getVariableValues ( structvar . typeStartToken ( ) ) ;
2017-09-07 23:08:55 +02:00
checkScopeForVariable ( tok2 , structvar , nullptr , nullptr , & alloc , var . name ( ) , variableValue ) ;
2015-01-21 13:10:38 +01:00
}
2013-01-16 20:28:29 +01:00
}
}
2012-05-25 13:40:18 +02:00
}
}
2015-11-18 15:16:50 +01:00
static VariableValue operator ! ( VariableValue v )
{
v . notEqual = ! v . notEqual ;
return v ;
}
2015-11-18 14:56:45 +01:00
static bool operator = = ( const VariableValue & v , MathLib : : bigint i )
{
2015-11-18 15:16:50 +01:00
return v . notEqual ? ( i ! = v . value ) : ( i = = v . value ) ;
2015-11-18 14:56:45 +01:00
}
static bool operator ! = ( const VariableValue & v , MathLib : : bigint i )
{
2015-11-18 15:16:50 +01:00
return v . notEqual ? ( i = = v . value ) : ( i ! = v . value ) ;
2015-11-18 14:56:45 +01:00
}
2021-01-27 19:49:13 +01:00
static void conditionAlwaysTrueOrFalse ( const Token * tok , const std : : map < nonneg int , VariableValue > & variableValue , bool * alwaysTrue , bool * alwaysFalse )
2013-01-25 18:20:57 +01:00
{
2015-11-19 13:09:45 +01:00
if ( ! tok )
return ;
2013-01-25 18:20:57 +01:00
2019-10-29 20:36:58 +01:00
if ( tok - > hasKnownIntValue ( ) ) {
if ( tok - > getKnownIntValue ( ) = = 0 )
* alwaysFalse = true ;
else
* alwaysTrue = true ;
return ;
}
2015-11-19 13:09:45 +01:00
if ( tok - > isName ( ) | | tok - > str ( ) = = " . " ) {
while ( tok & & tok - > str ( ) = = " . " )
tok = tok - > astOperand2 ( ) ;
2021-01-27 19:49:13 +01:00
const std : : map < nonneg int , VariableValue > : : const_iterator it = variableValue . find ( tok ? tok - > varId ( ) : ~ 0U ) ;
2015-11-19 13:09:45 +01:00
if ( it ! = variableValue . end ( ) ) {
* alwaysTrue = ( it - > second ! = 0LL ) ;
* alwaysFalse = ( it - > second = = 0LL ) ;
}
}
2013-01-25 18:20:57 +01:00
2015-11-19 13:09:45 +01:00
else if ( tok - > isComparisonOp ( ) ) {
2020-02-17 16:56:27 +01:00
if ( variableValue . empty ( ) ) {
return ;
}
2015-11-19 13:09:45 +01:00
const Token * vartok , * numtok ;
if ( tok - > astOperand2 ( ) & & tok - > astOperand2 ( ) - > isNumber ( ) ) {
vartok = tok - > astOperand1 ( ) ;
numtok = tok - > astOperand2 ( ) ;
} else if ( tok - > astOperand1 ( ) & & tok - > astOperand1 ( ) - > isNumber ( ) ) {
vartok = tok - > astOperand2 ( ) ;
numtok = tok - > astOperand1 ( ) ;
} else {
return ;
}
2013-01-25 18:20:57 +01:00
2015-11-19 13:09:45 +01:00
while ( vartok & & vartok - > str ( ) = = " . " )
vartok = vartok - > astOperand2 ( ) ;
2021-01-27 19:49:13 +01:00
const std : : map < nonneg int , VariableValue > : : const_iterator it = variableValue . find ( vartok ? vartok - > varId ( ) : ~ 0U ) ;
2015-11-19 13:09:45 +01:00
if ( it = = variableValue . end ( ) )
return ;
2013-01-25 18:20:57 +01:00
2015-11-19 13:09:45 +01:00
if ( tok - > str ( ) = = " == " )
2023-10-05 19:21:42 +02:00
* alwaysTrue = ( it - > second = = MathLib : : toBigNumber ( numtok - > str ( ) ) ) ;
2015-11-19 13:09:45 +01:00
else if ( tok - > str ( ) = = " != " )
2023-10-05 19:21:42 +02:00
* alwaysTrue = ( it - > second ! = MathLib : : toBigNumber ( numtok - > str ( ) ) ) ;
2013-01-25 18:20:57 +01:00
else
2015-11-19 13:09:45 +01:00
return ;
* alwaysFalse = ! ( * alwaysTrue ) ;
2013-01-25 18:20:57 +01:00
}
2015-11-19 13:09:45 +01:00
else if ( tok - > str ( ) = = " ! " ) {
bool t = false , f = false ;
conditionAlwaysTrueOrFalse ( tok - > astOperand1 ( ) , variableValue , & t , & f ) ;
2021-08-07 20:51:18 +02:00
if ( t | | f ) {
2015-11-19 13:09:45 +01:00
* alwaysTrue = ! t ;
* alwaysFalse = ! f ;
}
}
else if ( tok - > str ( ) = = " || " ) {
bool t1 = false , f1 = false ;
conditionAlwaysTrueOrFalse ( tok - > astOperand1 ( ) , variableValue , & t1 , & f1 ) ;
bool t2 = false , f2 = false ;
if ( ! t1 )
2015-11-21 08:32:25 +01:00
conditionAlwaysTrueOrFalse ( tok - > astOperand2 ( ) , variableValue , & t2 , & f2 ) ;
* alwaysTrue = ( t1 | | t2 ) ;
* alwaysFalse = ( f1 & & f2 ) ;
2015-11-19 13:09:45 +01:00
}
else if ( tok - > str ( ) = = " && " ) {
bool t1 = false , f1 = false ;
conditionAlwaysTrueOrFalse ( tok - > astOperand1 ( ) , variableValue , & t1 , & f1 ) ;
bool t2 = false , f2 = false ;
if ( ! f1 )
2015-11-21 08:32:25 +01:00
conditionAlwaysTrueOrFalse ( tok - > astOperand2 ( ) , variableValue , & t2 , & f2 ) ;
* alwaysTrue = ( t1 & & t2 ) ;
* alwaysFalse = ( f1 | | f2 ) ;
2013-01-25 18:20:57 +01:00
}
}
2015-07-22 20:31:58 +02:00
static bool isVariableUsed ( const Token * tok , const Variable & var )
{
if ( ! tok )
return false ;
if ( tok - > str ( ) = = " & " & & ! tok - > astOperand2 ( ) )
return false ;
2015-07-22 22:17:12 +02:00
if ( tok - > isConstOp ( ) )
2015-07-22 20:31:58 +02:00
return isVariableUsed ( tok - > astOperand1 ( ) , var ) | | isVariableUsed ( tok - > astOperand2 ( ) , var ) ;
2015-08-29 13:19:28 +02:00
if ( tok - > varId ( ) ! = var . declarationId ( ) )
return false ;
if ( ! var . isArray ( ) )
return true ;
const Token * parent = tok - > astParent ( ) ;
while ( Token : : Match ( parent , " [?:] " ) )
parent = parent - > astParent ( ) ;
// no dereference, then array is not "used"
if ( ! Token : : Match ( parent , " *|[ " ) )
return false ;
const Token * parent2 = parent - > astParent ( ) ;
// TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays
return ! parent2 | | parent2 - > isConstOp ( ) | | ( parent2 - > str ( ) = = " = " & & parent2 - > astOperand2 ( ) = = parent ) ;
2015-07-22 20:31:58 +02:00
}
2023-10-12 13:56:47 +02:00
bool CheckUninitVar : : checkScopeForVariable ( const Token * tok , const Variable & var , bool * const possibleInit , bool * const noreturn , Alloc * const alloc , const std : : string & membervar , std : : map < nonneg int , VariableValue > & variableValue )
2012-05-25 13:40:18 +02:00
{
2022-03-15 14:32:33 +01:00
const bool suppressErrors ( possibleInit & & * possibleInit ) ; // Assume that this is a variable declaration, rather than a fundef
2018-06-16 16:10:28 +02:00
const bool printDebug = mSettings - > debugwarnings ;
2011-12-26 12:36:35 +01:00
if ( possibleInit )
* possibleInit = false ;
2019-07-13 15:31:17 +02:00
int number_of_if = 0 ;
2011-12-13 21:57:27 +01:00
2013-12-04 20:32:20 +01:00
if ( var . declarationId ( ) = = 0U )
return true ;
2011-12-13 21:57:27 +01:00
for ( ; tok ; tok = tok - > next ( ) ) {
// End of scope..
if ( tok - > str ( ) = = " } " ) {
2011-12-26 12:36:35 +01:00
if ( number_of_if & & possibleInit )
* possibleInit = true ;
2011-12-16 19:56:13 +01:00
2011-12-13 21:57:27 +01:00
// might be a noreturn function..
2019-11-20 15:37:09 +01:00
if ( mTokenizer - > isScopeNoReturn ( tok ) ) {
2013-01-28 18:08:20 +01:00
if ( noreturn )
* noreturn = true ;
2015-03-14 17:59:11 +01:00
return false ;
2013-01-28 18:08:20 +01:00
}
2011-12-13 21:57:27 +01:00
break ;
}
2022-03-15 14:32:33 +01:00
// Unconditional inner scope, try, lambda, init list
if ( tok - > str ( ) = = " { " & & Token : : Match ( tok - > previous ( ) , " ,|;|{|}|]|try " ) ) {
2021-05-20 18:38:59 +02:00
bool possibleInitInner = false ;
if ( checkScopeForVariable ( tok - > next ( ) , var , & possibleInitInner , noreturn , alloc , membervar , variableValue ) )
2011-12-26 17:52:32 +01:00
return true ;
tok = tok - > link ( ) ;
2021-05-20 18:38:59 +02:00
if ( possibleInitInner ) {
number_of_if = 1 ;
if ( possibleInit )
* possibleInit = true ;
}
2011-12-26 17:52:32 +01:00
continue ;
}
2023-06-10 21:35:17 +02:00
// track values of other variables..
2023-10-12 13:56:47 +02:00
if ( Token : : Match ( tok - > previous ( ) , " [;{}.] %var% = " ) ) {
2023-06-10 21:35:17 +02:00
if ( tok - > next ( ) - > astOperand2 ( ) & & tok - > next ( ) - > astOperand2 ( ) - > hasKnownIntValue ( ) )
variableValue [ tok - > varId ( ) ] = VariableValue ( tok - > next ( ) - > astOperand2 ( ) - > getKnownIntValue ( ) ) ;
else if ( Token : : Match ( tok - > previous ( ) , " [ ; { } ] % var % = - % name % ; " ))
variableValue [ tok - > varId ( ) ] = ! VariableValue ( 0 ) ;
else
variableValue . erase ( tok - > varId ( ) ) ;
}
2012-02-06 07:32:29 +01:00
2011-12-13 21:57:27 +01:00
// Inner scope..
2015-01-22 13:51:43 +01:00
else if ( Token : : simpleMatch ( tok , " if ( " ) ) {
2012-12-24 19:11:13 +01:00
bool alwaysTrue = false ;
2013-01-25 18:20:57 +01:00
bool alwaysFalse = false ;
2012-12-24 19:11:13 +01:00
2019-01-13 12:10:26 +01:00
// Is variable assigned in condition?
if ( ! membervar . empty ( ) ) {
for ( const Token * cond = tok - > linkAt ( 1 ) ; cond ! = tok ; cond = cond - > previous ( ) ) {
if ( cond - > varId ( ) = = var . declarationId ( ) & & isMemberVariableAssignment ( cond , membervar ) )
return true ;
}
}
2015-11-19 13:09:45 +01:00
conditionAlwaysTrueOrFalse ( tok - > next ( ) - > astOperand2 ( ) , variableValue , & alwaysTrue , & alwaysFalse ) ;
2012-12-24 19:11:13 +01:00
2011-12-26 18:32:42 +01:00
// initialization / usage in condition..
2015-01-23 19:38:39 +01:00
if ( ! alwaysTrue & & checkIfForWhileHead ( tok - > next ( ) , var , suppressErrors , bool ( number_of_if = = 0 ) , * alloc , membervar ) )
2011-12-26 18:32:42 +01:00
return true ;
2011-12-14 17:17:24 +01:00
2012-02-06 07:32:29 +01:00
// checking if a not-zero variable is zero => bail out
2021-01-27 19:49:13 +01:00
nonneg int condVarId = 0 ;
2015-11-18 14:56:45 +01:00
VariableValue condVarValue ( 0 ) ;
2015-07-25 17:55:01 +02:00
const Token * condVarTok = nullptr ;
2015-11-21 10:00:21 +01:00
if ( alwaysFalse )
;
2022-08-21 17:21:02 +02:00
else if ( astIsVariableComparison ( tok - > next ( ) - > astOperand2 ( ) , " != " , " 0 " , & condVarTok ) ) {
2021-01-27 19:49:13 +01:00
const std : : map < nonneg int , VariableValue > : : const_iterator it = variableValue . find ( condVarTok - > varId ( ) ) ;
2022-12-30 15:13:47 +01:00
if ( it ! = variableValue . cend ( ) & & it - > second ! = 0 )
2012-12-24 19:11:13 +01:00
return true ; // this scope is not fully analysed => return true
2023-06-20 18:43:21 +02:00
condVarId = condVarTok - > varId ( ) ;
condVarValue = ! VariableValue ( 0 ) ;
2022-08-21 17:21:02 +02:00
} else if ( Token : : Match ( tok - > next ( ) - > astOperand2 ( ) , " ==|!= " ) ) {
2015-11-19 13:09:45 +01:00
const Token * condition = tok - > next ( ) - > astOperand2 ( ) ;
const Token * lhs = condition - > astOperand1 ( ) ;
const Token * rhs = condition - > astOperand2 ( ) ;
2020-05-07 22:25:08 +02:00
const Token * vartok = ( lhs & & lhs - > hasKnownIntValue ( ) ) ? rhs : lhs ;
const Token * numtok = ( lhs = = vartok ) ? rhs : lhs ;
2015-11-19 13:09:45 +01:00
while ( Token : : simpleMatch ( vartok , " . " ) )
vartok = vartok - > astOperand2 ( ) ;
2020-05-07 22:25:08 +02:00
if ( vartok & & vartok - > varId ( ) & & numtok & & numtok - > hasKnownIntValue ( ) ) {
2021-01-27 19:49:13 +01:00
const std : : map < nonneg int , VariableValue > : : const_iterator it = variableValue . find ( vartok - > varId ( ) ) ;
2022-12-30 15:13:47 +01:00
if ( it ! = variableValue . cend ( ) & & it - > second ! = numtok - > getKnownIntValue ( ) )
2015-11-19 13:09:45 +01:00
return true ; // this scope is not fully analysed => return true
2020-05-07 22:25:08 +02:00
condVarId = vartok - > varId ( ) ;
condVarValue = VariableValue ( numtok - > getKnownIntValue ( ) ) ;
if ( condition - > str ( ) = = " != " )
condVarValue = ! condVarValue ;
2015-11-19 13:09:45 +01:00
}
2012-12-24 19:11:13 +01:00
}
2012-02-06 07:32:29 +01:00
2011-12-13 21:57:27 +01:00
// goto the {
tok = tok - > next ( ) - > link ( ) - > next ( ) ;
2012-01-13 23:30:43 +01:00
if ( ! tok )
break ;
if ( tok - > str ( ) = = " { " ) {
2015-11-21 10:00:21 +01:00
bool possibleInitIf ( ( ! alwaysTrue & & number_of_if > 0 ) | | suppressErrors ) ;
2012-11-29 18:41:48 +01:00
bool noreturnIf = false ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > varValueIf ( variableValue ) ;
const bool initif = ! alwaysFalse & & checkScopeForVariable ( tok - > next ( ) , var , & possibleInitIf , & noreturnIf , alloc , membervar , varValueIf ) ;
2011-12-13 21:57:27 +01:00
2012-12-27 18:45:00 +01:00
// bail out for such code:
// if (a) x=0; // conditional initialization
// if (b) return; // cppcheck doesn't know if b can be false when a is false.
// x++; // it's possible x is always initialized
2012-12-28 11:42:50 +01:00
if ( ! alwaysTrue & & noreturnIf & & number_of_if > 0 ) {
2015-04-10 14:18:52 +02:00
if ( printDebug ) {
2012-12-28 11:42:50 +01:00
std : : string condition ;
for ( const Token * tok2 = tok - > linkAt ( - 1 ) ; tok2 ! = tok ; tok2 = tok2 - > next ( ) ) {
condition + = tok2 - > str ( ) ;
if ( tok2 - > isName ( ) & & tok2 - > next ( ) - > isName ( ) )
condition + = ' ' ;
}
2021-04-16 15:19:29 +02:00
reportError ( tok , Severity : : debug , " bailoutUninitVar " , " bailout uninitialized variable checking for ' " + var . name ( ) + " '. can't determine if this condition can be false when previous condition is false: " + condition ) ;
2012-12-28 11:42:50 +01:00
}
2012-12-27 18:45:00 +01:00
return true ;
2012-12-28 11:42:50 +01:00
}
2012-12-27 18:45:00 +01:00
2016-01-30 20:48:28 +01:00
if ( alwaysTrue & & ( initif | | noreturnIf ) )
2013-04-08 19:34:39 +02:00
return true ;
2023-10-12 13:56:47 +02:00
if ( ! alwaysFalse & & ! initif & & ! noreturnIf )
variableValue = varValueIf ;
2012-12-23 16:27:04 +01:00
2021-01-27 19:49:13 +01:00
if ( initif & & condVarId > 0 )
2015-11-18 15:16:50 +01:00
variableValue [ condVarId ] = ! condVarValue ;
2012-12-24 19:11:13 +01:00
2012-01-13 23:30:43 +01:00
// goto the }
tok = tok - > link ( ) ;
2011-12-13 21:57:27 +01:00
2012-01-13 23:30:43 +01:00
if ( ! Token : : simpleMatch ( tok , " } else { " ) ) {
if ( initif | | possibleInitIf ) {
+ + number_of_if ;
if ( number_of_if > = 2 )
return true ;
}
} else {
// goto the {
tok = tok - > tokAt ( 2 ) ;
2011-12-13 21:57:27 +01:00
2015-11-21 10:00:21 +01:00
bool possibleInitElse ( ( ! alwaysFalse & & number_of_if > 0 ) | | suppressErrors ) ;
2012-11-29 18:41:48 +01:00
bool noreturnElse = false ;
2023-10-12 13:56:47 +02:00
std : : map < nonneg int , VariableValue > varValueElse ( variableValue ) ;
const bool initelse = ! alwaysTrue & & checkScopeForVariable ( tok - > next ( ) , var , & possibleInitElse , & noreturnElse , alloc , membervar , varValueElse ) ;
if ( ! alwaysTrue & & ! initelse & & ! noreturnElse )
variableValue = varValueElse ;
2012-12-23 16:27:04 +01:00
2021-01-27 19:49:13 +01:00
if ( initelse & & condVarId > 0 & & ! noreturnIf & & ! noreturnElse )
2012-12-24 19:11:13 +01:00
variableValue [ condVarId ] = condVarValue ;
2012-01-13 23:30:43 +01:00
// goto the }
tok = tok - > link ( ) ;
2011-12-13 21:57:27 +01:00
2013-05-06 18:45:00 +02:00
if ( ( alwaysFalse | | initif | | noreturnIf ) & &
( alwaysTrue | | initelse | | noreturnElse ) )
2012-11-29 18:41:48 +01:00
return true ;
2015-04-08 15:35:04 +02:00
if ( initif | | initelse | | possibleInitElse )
2012-01-13 23:30:43 +01:00
+ + number_of_if ;
2015-04-08 15:35:04 +02:00
if ( ! initif & & ! noreturnIf )
2022-12-30 15:13:47 +01:00
variableValue . insert ( varValueIf . cbegin ( ) , varValueIf . cend ( ) ) ;
2015-04-08 15:35:04 +02:00
if ( ! initelse & & ! noreturnElse )
2022-12-30 15:13:47 +01:00
variableValue . insert ( varValueElse . cbegin ( ) , varValueElse . cend ( ) ) ;
2012-01-13 23:30:43 +01:00
}
2011-12-13 21:57:27 +01:00
}
}
2011-12-17 07:56:46 +01:00
// = { .. }
2023-05-13 22:07:09 +02:00
else if ( Token : : simpleMatch ( tok , " = { " ) | | ( Token : : Match ( tok , " %name% { " ) && tok->variable() && tok == tok->variable()->nameToken())) {
2011-12-17 07:56:46 +01:00
// end token
const Token * end = tok - > next ( ) - > link ( ) ;
// If address of variable is taken in the block then bail out
2016-02-07 18:48:57 +01:00
if ( var . isPointer ( ) | | var . isArray ( ) ) {
if ( Token : : findmatch ( tok - > tokAt ( 2 ) , " %varid% " , end , var . declarationId ( ) ) )
return true ;
} else if ( Token : : findmatch ( tok - > tokAt ( 2 ) , " & %varid% " , end , var . declarationId ( ) ) ) {
2011-12-17 07:56:46 +01:00
return true ;
2016-02-07 18:48:57 +01:00
}
2011-12-17 07:56:46 +01:00
2019-08-24 14:43:35 +02:00
const Token * errorToken = nullptr ;
visitAstNodes ( tok - > next ( ) ,
2021-08-07 20:51:18 +02:00
[ & ] ( const Token * child ) {
2019-08-24 14:43:35 +02:00
if ( child - > isUnaryOp ( " & " ) )
return ChildrenToVisit : : none ;
if ( child - > str ( ) = = " , " | | child - > str ( ) = = " { " | | child - > isConstOp ( ) )
return ChildrenToVisit : : op1_and_op2 ;
2019-08-24 15:43:31 +02:00
if ( child - > str ( ) = = " . " & & Token : : Match ( child - > astOperand1 ( ) , " %varid% " , var . declarationId ( ) ) & & child - > astOperand2 ( ) & & child - > astOperand2 ( ) - > str ( ) = = membervar ) {
2019-08-24 14:43:35 +02:00
errorToken = child ;
return ChildrenToVisit : : done ;
}
return ChildrenToVisit : : none ;
} ) ;
if ( errorToken ) {
uninitStructMemberError ( errorToken - > astOperand2 ( ) , errorToken - > astOperand1 ( ) - > str ( ) + " . " + membervar ) ;
return true ;
}
2011-12-17 07:56:46 +01:00
// Skip block
tok = end ;
continue ;
}
2011-12-16 18:04:10 +01:00
2011-12-17 09:51:45 +01:00
// skip sizeof / offsetof
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok ) )
2021-05-16 22:27:04 +02:00
tok = tok - > linkAt ( 1 ) ;
2011-12-17 09:51:45 +01:00
2012-11-30 06:30:04 +01:00
// for/while..
2015-01-22 13:51:43 +01:00
else if ( Token : : Match ( tok , " for|while ( " ) | | Token : : simpleMatch ( tok , " do { " )) {
2013-06-25 18:40:41 +02:00
const bool forwhile = Token : : Match ( tok , " for|while ( " ) ;
2018-01-02 23:19:33 +01:00
// is variable initialized in for-head?
if ( forwhile & & checkIfForWhileHead ( tok - > next ( ) , var , tok - > str ( ) = = " for " , false , * alloc , membervar ) )
2011-12-27 08:18:05 +01:00
return true ;
2011-12-26 18:32:42 +01:00
// goto the {
2013-06-25 18:40:41 +02:00
const Token * tok2 = forwhile ? tok - > next ( ) - > link ( ) - > next ( ) : tok - > next ( ) ;
2011-12-26 18:32:42 +01:00
2012-01-13 23:30:43 +01:00
if ( tok2 & & tok2 - > str ( ) = = " { " ) {
2018-04-04 21:51:31 +02:00
const bool init = checkLoopBody ( tok2 , var , * alloc , membervar , ( number_of_if > 0 ) | | suppressErrors ) ;
2011-12-26 18:32:42 +01:00
2012-01-13 23:30:43 +01:00
// variable is initialized in the loop..
2013-05-29 16:16:12 +02:00
if ( init )
2012-01-13 23:30:43 +01:00
return true ;
2011-12-26 18:32:42 +01:00
2012-01-13 23:30:43 +01:00
// is variable used in for-head?
2013-12-13 13:27:01 +01:00
bool initcond = false ;
2012-01-13 23:30:43 +01:00
if ( ! suppressErrors ) {
2013-06-25 18:40:41 +02:00
const Token * startCond = forwhile ? tok - > next ( ) : tok - > next ( ) - > link ( ) - > tokAt ( 2 ) ;
2015-01-23 19:38:39 +01:00
initcond = checkIfForWhileHead ( startCond , var , false , bool ( number_of_if = = 0 ) , * alloc , membervar ) ;
2012-01-13 23:30:43 +01:00
}
2012-11-29 18:41:48 +01:00
// goto "}"
tok = tok2 - > link ( ) ;
2013-06-25 18:40:41 +02:00
// do-while => goto ")"
2013-07-28 12:41:38 +02:00
if ( ! forwhile ) {
// Assert that the tokens are '} while ('
2013-11-06 17:53:09 +01:00
if ( ! Token : : simpleMatch ( tok , " } while ( " ) ) {
2015-04-10 14:18:52 +02:00
if ( printDebug )
2022-07-10 10:57:29 +02:00
reportError ( tok , Severity : : debug , emptyString , " assertion failed '} while (' " ) ;
2013-11-06 17:53:09 +01:00
break ;
}
2013-07-28 12:41:38 +02:00
// Goto ')'
2013-06-25 18:40:41 +02:00
tok = tok - > linkAt ( 2 ) ;
2013-07-28 12:41:38 +02:00
if ( ! tok )
// bailout : invalid code / bad tokenizer
break ;
2013-12-13 13:27:01 +01:00
if ( initcond )
// variable is initialized in while-condition
return true ;
2013-07-28 12:41:38 +02:00
}
2011-12-27 08:18:05 +01:00
}
2011-12-26 18:32:42 +01:00
}
2015-01-22 13:51:43 +01:00
// Unknown or unhandled inner scope
2023-05-13 22:07:09 +02:00
else if ( Token : : simpleMatch ( tok , " ) { " ) || (Token::Match(tok, " % name % { " ) && tok->str() != " try " && !(tok->variable() && tok == tok->variable()->nameToken()))) {
2015-01-22 13:51:43 +01:00
if ( tok - > str ( ) = = " struct " | | tok - > str ( ) = = " union " ) {
tok = tok - > linkAt ( 1 ) ;
continue ;
}
2011-12-15 20:29:57 +01:00
return true ;
}
2015-01-06 07:44:04 +01:00
// bailout if there is ({
if ( Token : : simpleMatch ( tok , " ( { " ) ) {
return true ;
}
2015-01-21 16:49:34 +01:00
// bailout if there is assembler code or setjmp
if ( Token : : Match ( tok , " asm|setjmp ( " ) ) {
2011-12-15 20:29:57 +01:00
return true ;
2011-12-14 18:54:03 +01:00
}
2015-10-27 12:40:52 +01:00
// bailout if there is a goto label
if ( Token : : Match ( tok , " [;{}] %name% : " ) ) {
return true ;
}
2023-11-04 13:40:06 +01:00
// bailout if there is a pointer to member
if ( Token : : Match ( tok , " %varid% . * " , var . declarationId ( ) ) ) {
return true ;
}
2015-10-27 12:40:52 +01:00
2015-01-21 22:26:44 +01:00
if ( tok - > str ( ) = = " ? " ) {
2015-07-22 22:17:12 +02:00
if ( ! tok - > astOperand2 ( ) )
return true ;
2015-07-22 20:31:58 +02:00
const bool used1 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand1 ( ) , var ) ;
const bool used0 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand2 ( ) , var ) ;
const bool err = ( number_of_if = = 0 ) ? ( used1 | | used0 ) : ( used1 & & used0 ) ;
2015-07-23 08:46:59 +02:00
if ( err )
uninitvarError ( tok , var . nameToken ( ) - > str ( ) , * alloc ) ;
2015-07-22 20:31:58 +02:00
// Todo: skip expression if there is no error
2015-01-21 22:02:25 +01:00
return true ;
}
2012-11-29 18:41:48 +01:00
if ( Token : : Match ( tok , " return|break|continue|throw|goto " ) ) {
if ( noreturn )
* noreturn = true ;
2012-12-25 10:37:21 +01:00
2015-08-16 14:22:46 +02:00
tok = tok - > next ( ) ;
2012-12-25 10:37:21 +01:00
while ( tok & & tok - > str ( ) ! = " ; " ) {
// variable is seen..
2013-07-20 12:31:04 +02:00
if ( tok - > varId ( ) = = var . declarationId ( ) ) {
2013-01-20 17:54:32 +01:00
if ( ! membervar . empty ( ) ) {
2021-05-10 19:24:03 +02:00
if ( ! suppressErrors & & Token : : Match ( tok , " %name% . %name% " ) & & tok - > strAt ( 2 ) = = membervar & & Token : : Match ( tok - > next ( ) - > astParent ( ) , " %cop%|return|throw|? " ) )
2013-01-20 17:54:32 +01:00
uninitStructMemberError ( tok , tok - > str ( ) + " . " + membervar ) ;
2023-08-12 23:46:31 +02:00
else if ( mTokenizer - > isCPP ( ) & & ! suppressErrors & & Token : : Match ( tok , " %name% " ) & & Token : : Match ( tok - > astParent ( ) , " return|throw|? " ) ) {
2023-08-14 10:27:00 +02:00
if ( std : : any_of ( tok - > values ( ) . cbegin ( ) , tok - > values ( ) . cend ( ) , [ ] ( const ValueFlow : : Value & v ) {
return v . isUninitValue ( ) & & ! v . isInconclusive ( ) ;
} ) )
2023-08-12 23:46:31 +02:00
uninitStructMemberError ( tok , tok - > str ( ) + " . " + membervar ) ;
}
2013-01-20 17:54:32 +01:00
}
2012-12-25 10:37:21 +01:00
// Use variable
2015-07-23 09:21:53 +02:00
else if ( ! suppressErrors & & isVariableUsage ( tok , var . isPointer ( ) , * alloc ) )
2015-07-23 08:46:59 +02:00
uninitvarError ( tok , tok - > str ( ) , * alloc ) ;
2014-12-18 06:37:15 +01:00
2019-03-02 13:17:15 +01:00
return true ;
2012-12-25 10:37:21 +01:00
}
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok ) )
2012-12-25 13:31:54 +01:00
tok = tok - > linkAt ( 1 ) ;
2015-07-22 20:31:58 +02:00
else if ( tok - > str ( ) = = " ? " ) {
2015-07-22 22:17:12 +02:00
if ( ! tok - > astOperand2 ( ) )
return true ;
2015-07-22 20:31:58 +02:00
const bool used1 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand1 ( ) , var ) ;
const bool used0 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand2 ( ) , var ) ;
const bool err = ( number_of_if = = 0 ) ? ( used1 | | used0 ) : ( used1 & & used0 ) ;
2015-07-23 08:46:59 +02:00
if ( err )
uninitvarError ( tok , var . nameToken ( ) - > str ( ) , * alloc ) ;
2013-07-10 16:44:35 +02:00
return true ;
2015-07-22 20:31:58 +02:00
}
2013-07-10 16:44:35 +02:00
2012-12-25 10:37:21 +01:00
tok = tok - > next ( ) ;
}
2017-06-01 00:49:40 +02:00
return ( noreturn = = nullptr ) ;
2012-12-25 10:37:21 +01:00
}
2011-12-13 21:57:27 +01:00
// variable is seen..
2013-07-20 12:31:04 +02:00
if ( tok - > varId ( ) = = var . declarationId ( ) ) {
2013-12-04 20:32:20 +01:00
// calling function that returns uninit data through pointer..
2019-02-10 19:00:01 +01:00
if ( var . isPointer ( ) & & Token : : simpleMatch ( tok - > next ( ) , " = " ) ) {
const Token * rhs = tok - > next ( ) - > astOperand2 ( ) ;
while ( rhs & & rhs - > isCast ( ) )
2020-02-23 19:53:17 +01:00
rhs = rhs - > astOperand2 ( ) ? rhs - > astOperand2 ( ) : rhs - > astOperand1 ( ) ;
2020-06-28 21:00:50 +02:00
if ( rhs & & Token : : Match ( rhs - > previous ( ) , " %name% ( " ) ) {
const Library : : AllocFunc * allocFunc = mSettings - > library . getAllocFuncInfo ( rhs - > astOperand1 ( ) ) ;
if ( allocFunc & & ! allocFunc - > initData ) {
* alloc = NO_CTOR_CALL ;
continue ;
}
2019-02-10 19:00:01 +01:00
}
2013-12-04 20:32:20 +01:00
}
2019-11-03 17:08:28 +01:00
if ( mTokenizer - > isCPP ( ) & & var . isPointer ( ) & & ( var . typeStartToken ( ) - > isStandardType ( ) | | var . typeStartToken ( ) - > isEnumType ( ) | | ( var . type ( ) & & var . type ( ) - > needInitialization = = Type : : NeedInitialization : : True ) ) & & Token : : simpleMatch ( tok - > next ( ) , " = new " ) ) {
2015-02-17 19:32:59 +01:00
* alloc = CTOR_CALL ;
2016-10-07 21:30:23 +02:00
// type has constructor(s)
2015-02-17 19:32:59 +01:00
if ( var . typeScope ( ) & & var . typeScope ( ) - > numConstructors > 0 )
return true ;
2016-10-07 21:30:23 +02:00
// standard or enum type: check if new initializes the allocated memory
if ( var . typeStartToken ( ) - > isStandardType ( ) | | var . typeStartToken ( ) - > isEnumType ( ) ) {
// scalar new with initialization
2016-10-08 11:34:25 +02:00
if ( Token : : Match ( tok - > next ( ) , " = new %type% ( " ) )
2016-10-07 21:30:23 +02:00
return true ;
// array new
2019-11-16 11:20:14 +01:00
if ( Token : : Match ( tok - > next ( ) , " = new %type% [ " ) & & Token : : simpleMatch ( tok - > linkAt ( 4 ) , " ] ( " ) )
return true ;
2016-10-07 21:30:23 +02:00
}
2015-01-21 23:46:52 +01:00
continue ;
}
2013-12-04 20:32:20 +01:00
2013-01-19 12:48:56 +01:00
if ( ! membervar . empty ( ) ) {
2013-06-27 16:53:15 +02:00
if ( isMemberVariableAssignment ( tok , membervar ) ) {
2015-07-23 08:46:59 +02:00
checkRhs ( tok , var , * alloc , number_of_if , membervar ) ;
2013-01-17 17:21:21 +01:00
return true ;
2013-06-27 16:53:15 +02:00
}
2013-01-24 19:41:15 +01:00
2019-03-02 13:17:15 +01:00
if ( isMemberVariableUsage ( tok , var . isPointer ( ) , * alloc , membervar ) ) {
2013-01-19 12:48:56 +01:00
uninitStructMemberError ( tok , tok - > str ( ) + " . " + membervar ) ;
2019-03-02 13:17:15 +01:00
return true ;
}
2013-01-24 19:41:15 +01:00
2019-12-19 20:18:25 +01:00
if ( Token : : Match ( tok - > previous ( ) , " [(,] %name% [,)] " ) )
return true ;
if ( Token : : Match ( tok - > previous ( ) , " = %var% . %var% ; " ) & & membervar = = tok - > strAt ( 2 ) )
2013-04-10 20:04:32 +02:00
return true ;
2013-01-16 20:28:29 +01:00
} else {
// Use variable
2019-03-02 13:17:15 +01:00
if ( ! suppressErrors & & isVariableUsage ( tok , var . isPointer ( ) , * alloc ) ) {
2015-07-23 08:46:59 +02:00
uninitvarError ( tok , tok - > str ( ) , * alloc ) ;
2019-03-02 13:17:15 +01:00
return true ;
}
2011-12-14 06:00:17 +01:00
2023-06-20 18:43:21 +02:00
const Token * parent = tok ;
while ( parent - > astParent ( ) & & ( ( astIsLHS ( parent ) & & parent - > astParent ( ) - > str ( ) = = " [ " ) | | parent - > astParent ( ) - > isUnaryOp ( " * " ) ) ) {
parent = parent - > astParent ( ) ;
if ( parent - > str ( ) = = " [ " ) {
if ( const Token * errorToken = checkExpr ( parent - > astOperand2 ( ) , var , * alloc , number_of_if = = 0 ) ) {
2021-05-21 14:30:47 +02:00
if ( ! suppressErrors )
uninitvarError ( errorToken , errorToken - > expressionString ( ) , * alloc ) ;
return true ;
}
}
2013-06-27 16:53:15 +02:00
}
2023-06-20 18:43:21 +02:00
if ( Token : : simpleMatch ( parent - > astParent ( ) , " = " ) & & astIsLHS ( parent ) ) {
const Token * eq = parent - > astParent ( ) ;
if ( const Token * errorToken = checkExpr ( eq - > astOperand2 ( ) , var , * alloc , number_of_if = = 0 ) ) {
if ( ! suppressErrors )
uninitvarError ( errorToken , errorToken - > expressionString ( ) , * alloc ) ;
return true ;
}
}
// assume that variable is assigned
return true ;
2013-01-16 20:28:29 +01:00
}
2011-12-13 21:57:27 +01:00
}
}
2012-12-25 10:37:21 +01:00
return false ;
2011-12-13 21:57:27 +01:00
}
2023-05-26 17:24:13 +02:00
const Token * CheckUninitVar : : checkExpr ( const Token * tok , const Variable & var , const Alloc alloc , bool known , bool * bailout ) const
2021-05-21 14:30:47 +02:00
{
if ( ! tok )
return nullptr ;
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok - > previous ( ) ) )
2021-05-22 11:04:42 +02:00
return nullptr ;
2021-05-21 14:30:47 +02:00
if ( tok - > astOperand1 ( ) ) {
bool bailout1 = false ;
const Token * errorToken = checkExpr ( tok - > astOperand1 ( ) , var , alloc , known , & bailout1 ) ;
if ( bailout & & bailout1 )
* bailout = true ;
if ( errorToken )
return errorToken ;
if ( ( bailout1 | | ! known ) & & Token : : Match ( tok , " %oror%|&&|? " ) )
return nullptr ;
}
if ( tok - > astOperand2 ( ) )
return checkExpr ( tok - > astOperand2 ( ) , var , alloc , known , bailout ) ;
if ( tok - > varId ( ) = = var . declarationId ( ) ) {
const Token * errorToken = isVariableUsage ( tok , var . isPointer ( ) , alloc ) ;
if ( errorToken )
return errorToken ;
2023-06-20 18:43:21 +02:00
if ( bailout )
2021-05-21 14:30:47 +02:00
* bailout = true ;
}
return nullptr ;
}
2015-01-23 19:38:39 +01:00
bool CheckUninitVar : : checkIfForWhileHead ( const Token * startparentheses , const Variable & var , bool suppressErrors , bool isuninit , Alloc alloc , const std : : string & membervar )
2011-12-26 18:32:42 +01:00
{
2013-01-16 15:37:07 +01:00
const Token * const endpar = startparentheses - > link ( ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( startparentheses , " ( ! %name% %oror% " ) & & startparentheses - > tokAt ( 2 ) - > getValue ( 0 ) )
2014-11-01 14:03:02 +01:00
suppressErrors = true ;
2013-01-16 15:37:07 +01:00
for ( const Token * tok = startparentheses - > next ( ) ; tok & & tok ! = endpar ; tok = tok - > next ( ) ) {
2013-07-20 12:31:04 +02:00
if ( tok - > varId ( ) = = var . declarationId ( ) ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% . %name% " ) ) {
2015-11-21 19:31:18 +01:00
if ( membervar . empty ( ) )
return true ;
2013-02-05 17:01:46 +01:00
if ( tok - > strAt ( 2 ) = = membervar ) {
2013-02-23 15:57:58 +01:00
if ( isMemberVariableAssignment ( tok , membervar ) )
2013-02-05 17:01:46 +01:00
return true ;
2013-02-23 15:57:58 +01:00
2013-12-04 20:32:20 +01:00
if ( ! suppressErrors & & isMemberVariableUsage ( tok , var . isPointer ( ) , alloc , membervar ) )
2013-02-05 17:01:46 +01:00
uninitStructMemberError ( tok , tok - > str ( ) + " . " + membervar ) ;
}
2013-01-19 12:48:56 +01:00
continue ;
}
2021-05-17 09:22:19 +02:00
if ( const Token * errorToken = isVariableUsage ( tok , var . isPointer ( ) , alloc ) ) {
2015-03-20 09:03:11 +01:00
if ( suppressErrors )
2011-12-27 08:18:05 +01:00
continue ;
2021-05-17 09:22:19 +02:00
uninitvarError ( errorToken , errorToken - > expressionString ( ) , alloc ) ;
2011-12-27 08:18:05 +01:00
}
2011-12-26 18:32:42 +01:00
return true ;
}
2021-05-16 22:27:04 +02:00
// skip sizeof / offsetof
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok ) )
2021-05-16 22:27:04 +02:00
tok = tok - > linkAt ( 1 ) ;
2013-12-04 20:32:20 +01:00
if ( ( ! isuninit | | ! membervar . empty ( ) ) & & tok - > str ( ) = = " && " )
2011-12-26 18:32:42 +01:00
suppressErrors = true ;
}
return false ;
}
2021-05-11 20:35:15 +02:00
/** recursively check loop, return error token */
const Token * CheckUninitVar : : checkLoopBodyRecursive ( const Token * start , const Variable & var , const Alloc alloc , const std : : string & membervar , bool & bailout ) const
2013-01-24 19:41:15 +01:00
{
2021-05-11 20:35:15 +02:00
assert ( start - > str ( ) = = " { " ) ;
2013-01-24 19:41:15 +01:00
2021-05-11 20:35:15 +02:00
const Token * errorToken = nullptr ;
2013-01-24 19:41:15 +01:00
2021-05-11 20:35:15 +02:00
const Token * const end = start - > link ( ) ;
for ( const Token * tok = start - > next ( ) ; tok ! = end ; tok = tok - > next ( ) ) {
2021-05-16 22:27:04 +02:00
// skip sizeof / offsetof
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok ) ) {
2021-05-11 20:35:15 +02:00
tok = tok - > linkAt ( 1 ) ;
2017-06-30 12:45:48 +02:00
continue ;
}
2021-05-11 20:35:15 +02:00
if ( Token : : Match ( tok , " asm ( %str% ) ; " ) ) {
bailout = true ;
return nullptr ;
}
2021-05-14 21:36:51 +02:00
// for loop; skip third expression until loop body has been analyzed..
if ( tok - > str ( ) = = " ; " & & Token : : simpleMatch ( tok - > astParent ( ) , " ; " ) & & Token : : simpleMatch ( tok - > astParent ( ) - > astParent ( ) , " ( " ) ) {
const Token * top = tok - > astParent ( ) - > astParent ( ) ;
if ( ! Token : : simpleMatch ( top - > previous ( ) , " for ( " ) | | ! Token : : simpleMatch ( top - > link ( ) , " ) { " ) )
continue ;
const Token * bodyStart = top - > link ( ) - > next ( ) ;
2021-05-14 23:05:27 +02:00
const Token * errorToken1 = checkLoopBodyRecursive ( bodyStart , var , alloc , membervar , bailout ) ;
2021-05-24 17:02:19 +02:00
if ( ! errorToken )
errorToken = errorToken1 ;
2021-05-14 21:36:51 +02:00
if ( bailout )
return nullptr ;
}
// for loop; skip loop body if there is third expression
if ( Token : : simpleMatch ( tok , " ) { " ) & &
Token : : simpleMatch ( tok - > link ( ) - > previous ( ) , " for ( " ) & &
2021-08-07 20:51:18 +02:00
Token : : simpleMatch ( tok - > link ( ) - > astOperand2 ( ) , " ; " ) & &
2021-05-14 21:36:51 +02:00
Token : : simpleMatch ( tok - > link ( ) - > astOperand2 ( ) - > astOperand2 ( ) , " ; " ) ) {
tok = tok - > linkAt ( 1 ) ;
}
2021-05-11 20:35:15 +02:00
if ( tok - > str ( ) = = " { " ) {
2021-05-19 13:06:44 +02:00
// switch => bailout
if ( tok - > scope ( ) & & tok - > scope ( ) - > type = = Scope : : ScopeType : : eSwitch ) {
bailout = true ;
return nullptr ;
}
2021-05-11 20:35:15 +02:00
const Token * errorToken1 = checkLoopBodyRecursive ( tok , var , alloc , membervar , bailout ) ;
2021-05-15 11:16:32 +02:00
tok = tok - > link ( ) ;
if ( Token : : simpleMatch ( tok , " } else { " ) ) {
const Token * elseBody = tok - > tokAt ( 2 ) ;
2021-05-11 20:35:15 +02:00
const Token * errorToken2 = checkLoopBodyRecursive ( elseBody , var , alloc , membervar , bailout ) ;
2021-05-15 11:16:32 +02:00
tok = elseBody - > link ( ) ;
2021-05-11 20:35:15 +02:00
if ( errorToken1 & & errorToken2 )
return errorToken1 ;
if ( errorToken2 )
errorToken = errorToken2 ;
}
if ( bailout )
return nullptr ;
2021-05-15 11:16:32 +02:00
if ( ! errorToken )
2021-05-11 20:35:15 +02:00
errorToken = errorToken1 ;
}
2017-06-30 12:45:48 +02:00
if ( tok - > varId ( ) ! = var . declarationId ( ) )
continue ;
2021-05-20 22:56:14 +02:00
bool conditionalUsage = false ;
for ( const Token * parent = tok ; parent ; parent = parent - > astParent ( ) ) {
if ( Token : : Match ( parent - > astParent ( ) , " %oror%|&&|? " ) & & astIsRHS ( parent ) ) {
conditionalUsage = true ;
break ;
}
}
2017-06-30 12:45:48 +02:00
if ( ! membervar . empty ( ) ) {
if ( isMemberVariableAssignment ( tok , membervar ) ) {
bool assign = true ;
bool rhs = false ;
2017-06-30 13:41:19 +02:00
// Used for tracking if an ")" is inner or outer
const Token * rpar = nullptr ;
2017-06-30 12:45:48 +02:00
for ( const Token * tok2 = tok - > next ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " = " )
rhs = true ;
2017-06-30 13:41:19 +02:00
// Look at inner expressions but not outer expressions
if ( ! rpar & & tok2 - > str ( ) = = " ( " )
rpar = tok2 - > link ( ) ;
else if ( tok2 - > str ( ) = = " ) " ) {
// No rpar => this is an outer right parenthesis
if ( ! rpar )
break ;
if ( rpar = = tok2 )
rpar = nullptr ;
}
if ( tok2 - > str ( ) = = " ; " | | ( ! rpar & & tok2 - > str ( ) = = " , " ) )
2017-06-30 12:45:48 +02:00
break ;
if ( rhs & & tok2 - > varId ( ) = = var . declarationId ( ) & & isMemberVariableUsage ( tok2 , var . isPointer ( ) , alloc , membervar ) ) {
assign = false ;
break ;
2013-06-25 18:40:41 +02:00
}
}
2021-05-11 20:35:15 +02:00
if ( assign ) {
bailout = true ;
return nullptr ;
}
2017-06-30 12:45:48 +02:00
}
2021-05-20 22:56:14 +02:00
if ( isMemberVariableUsage ( tok , var . isPointer ( ) , alloc , membervar ) ) {
if ( ! conditionalUsage )
return tok ;
if ( ! errorToken )
errorToken = tok ;
} else if ( Token : : Match ( tok - > previous ( ) , " [(,] %name% [,)] " ) ) {
2021-05-11 20:35:15 +02:00
bailout = true ;
return nullptr ;
}
2017-06-30 12:45:48 +02:00
} else {
2021-05-20 22:56:14 +02:00
if ( const Token * errtok = isVariableUsage ( tok , var . isPointer ( ) , alloc ) ) {
if ( ! conditionalUsage )
return errtok ;
if ( ! errorToken )
errorToken = errtok ;
} else if ( tok - > strAt ( 1 ) = = " = " ) {
2020-02-18 16:38:59 +01:00
bool varIsUsedInRhs = false ;
2020-07-11 00:02:28 +02:00
visitAstNodes ( tok - > next ( ) - > astOperand2 ( ) , [ & ] ( const Token * t ) {
2017-06-30 12:45:48 +02:00
if ( ! t )
2020-07-11 00:02:28 +02:00
return ChildrenToVisit : : none ;
2017-06-30 12:45:48 +02:00
if ( t - > varId ( ) = = var . declarationId ( ) ) {
2020-02-18 16:38:59 +01:00
varIsUsedInRhs = true ;
2020-07-11 00:02:28 +02:00
return ChildrenToVisit : : done ;
2013-06-25 18:40:41 +02:00
}
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( t - > previous ( ) ) )
2020-07-11 00:02:28 +02:00
return ChildrenToVisit : : none ;
return ChildrenToVisit : : op1_and_op2 ;
} ) ;
2021-05-11 20:35:15 +02:00
if ( ! varIsUsedInRhs ) {
bailout = true ;
return nullptr ;
}
2017-06-30 12:45:48 +02:00
} else {
2021-05-11 20:35:15 +02:00
bailout = true ;
return nullptr ;
2013-01-24 19:41:15 +01:00
}
}
}
2021-05-11 20:35:15 +02:00
return errorToken ;
}
bool CheckUninitVar : : checkLoopBody ( const Token * tok , const Variable & var , const Alloc alloc , const std : : string & membervar , const bool suppressErrors )
{
bool bailout = false ;
const Token * errorToken = checkLoopBodyRecursive ( tok , var , alloc , membervar , bailout ) ;
if ( ! suppressErrors & & ! bailout & & errorToken ) {
2013-01-24 19:41:15 +01:00
if ( membervar . empty ( ) )
2021-05-17 16:51:30 +02:00
uninitvarError ( errorToken , errorToken - > expressionString ( ) , alloc ) ;
2013-01-24 19:41:15 +01:00
else
2021-05-17 16:51:30 +02:00
uninitStructMemberError ( errorToken , errorToken - > expressionString ( ) + " . " + membervar ) ;
2013-01-24 19:41:15 +01:00
return true ;
}
2021-05-11 20:35:15 +02:00
return bailout ;
2013-01-24 19:41:15 +01:00
}
2019-07-14 12:22:33 +02:00
void CheckUninitVar : : checkRhs ( const Token * tok , const Variable & var , Alloc alloc , nonneg int number_of_if , const std : : string & membervar )
2013-06-29 09:33:51 +02:00
{
bool rhs = false ;
2019-07-13 15:31:17 +02:00
int indent = 0 ;
2014-02-15 08:46:28 +01:00
while ( nullptr ! = ( tok = tok - > next ( ) ) ) {
2013-06-29 09:33:51 +02:00
if ( tok - > str ( ) = = " = " )
rhs = true ;
2013-07-20 12:31:04 +02:00
else if ( rhs & & tok - > varId ( ) = = var . declarationId ( ) ) {
2015-01-21 13:52:03 +01:00
if ( membervar . empty ( ) & & isVariableUsage ( tok , var . isPointer ( ) , alloc ) )
2015-07-23 08:46:59 +02:00
uninitvarError ( tok , tok - > str ( ) , alloc ) ;
2013-12-04 20:32:20 +01:00
else if ( ! membervar . empty ( ) & & isMemberVariableUsage ( tok , var . isPointer ( ) , alloc , membervar ) )
2013-06-29 09:33:51 +02:00
uninitStructMemberError ( tok , tok - > str ( ) + " . " + membervar ) ;
2016-12-25 22:43:29 +01:00
else if ( Token : : Match ( tok , " %var% = " ) )
break ;
2020-04-10 12:33:15 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " [(,&] " ) )
break ;
2013-06-29 09:33:51 +02:00
} else if ( tok - > str ( ) = = " ; " || (indent==0 && tok->str() == " , " ))
break ;
else if ( tok - > str ( ) = = " ( " )
+ + indent ;
else if ( tok - > str ( ) = = " ) " ) {
if ( indent = = 0 )
break ;
- - indent ;
2015-07-23 08:46:59 +02:00
} else if ( tok - > str ( ) = = " ? " & & tok - > astOperand2 ( ) ) {
const bool used1 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand1 ( ) , var ) ;
const bool used0 = isVariableUsed ( tok - > astOperand2 ( ) - > astOperand2 ( ) , var ) ;
const bool err = ( number_of_if = = 0 ) ? ( used1 | | used0 ) : ( used1 & & used0 ) ;
if ( err )
uninitvarError ( tok , var . nameToken ( ) - > str ( ) , alloc ) ;
break ;
2023-08-23 12:07:47 +02:00
} else if ( isUnevaluated ( tok ) )
2021-05-16 22:27:04 +02:00
tok = tok - > linkAt ( 1 ) ;
2013-06-29 09:33:51 +02:00
}
}
2021-05-16 22:27:04 +02:00
static bool astIsLhs ( const Token * tok )
2011-12-14 18:28:30 +01:00
{
2021-05-16 22:27:04 +02:00
return tok & & tok - > astParent ( ) & & tok = = tok - > astParent ( ) - > astOperand1 ( ) ;
}
2015-01-21 16:17:58 +01:00
2021-05-16 22:27:04 +02:00
static bool astIsRhs ( const Token * tok )
{
return tok & & tok - > astParent ( ) & & tok = = tok - > astParent ( ) - > astOperand2 ( ) ;
}
2019-02-27 17:58:25 +01:00
2021-05-17 05:59:28 +02:00
static bool isVoidCast ( const Token * tok )
{
return Token : : simpleMatch ( tok , " ( " ) & & tok - > isCast ( ) & & tok - > valueType ( ) & & tok - > valueType ( ) - > type = = ValueType : : Type : : VOID & & tok - > valueType ( ) - > pointer = = 0 ;
}
2023-11-03 18:27:11 +01:00
const Token * CheckUninitVar : : isVariableUsage ( const Token * vartok , const Library & library , bool pointer , Alloc alloc , int indirect )
2021-05-16 22:27:04 +02:00
{
2023-11-03 18:27:11 +01:00
const bool cpp = vartok - > isCpp ( ) ;
2021-05-16 22:27:04 +02:00
const Token * valueExpr = vartok ; // non-dereferenced , no address of value as variable
2021-05-17 10:31:02 +02:00
while ( Token : : Match ( valueExpr - > astParent ( ) , " .|:: " ) & & astIsRhs ( valueExpr ) )
valueExpr = valueExpr - > astParent ( ) ;
2021-05-24 17:50:28 +02:00
// stuff we ignore..
while ( valueExpr - > astParent ( ) ) {
// *&x
if ( valueExpr - > astParent ( ) - > isUnaryOp ( " & " ) & & valueExpr - > astParent ( ) - > astParent ( ) & & valueExpr - > astParent ( ) - > astParent ( ) - > isUnaryOp ( " * " ) )
valueExpr = valueExpr - > astParent ( ) - > astParent ( ) ;
// (type &)x
else if ( valueExpr - > astParent ( ) - > isCast ( ) & & valueExpr - > astParent ( ) - > isUnaryOp ( " ( " ) & & Token : : simpleMatch ( valueExpr - > astParent ( ) - > link ( ) - > previous ( ) , " & ) " ) )
valueExpr = valueExpr - > astParent ( ) ;
2023-05-26 11:59:10 +02:00
// designated initializers: {.x | { ... , .x
else if ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " . " ) & &
Token : : Match ( valueExpr - > astParent ( ) - > previous ( ) , " ,|{ " ) )
valueExpr = valueExpr - > astParent ( ) ;
2021-05-24 17:50:28 +02:00
else
break ;
}
2021-05-16 22:27:04 +02:00
if ( ! pointer ) {
2021-05-17 11:50:31 +02:00
if ( Token : : Match ( vartok , " %name% [.(] " ) & & vartok - > variable ( ) & & ! vartok - > variable ( ) - > isPointer ( ) )
2021-05-16 22:27:04 +02:00
return nullptr ;
while ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " . " ) & & astIsLhs ( valueExpr ) & & valueExpr - > astParent ( ) - > valueType ( ) & & valueExpr - > astParent ( ) - > valueType ( ) - > pointer = = 0 )
valueExpr = valueExpr - > astParent ( ) ;
2014-09-12 08:19:00 +02:00
}
2021-05-16 22:27:04 +02:00
const Token * derefValue = nullptr ; // dereferenced value expression
if ( alloc ! = NO_ALLOC ) {
const int arrayDim = ( vartok - > variable ( ) & & vartok - > variable ( ) - > isArray ( ) ) ? vartok - > variable ( ) - > dimensions ( ) . size ( ) : 1 ;
int deref = 0 ;
derefValue = valueExpr ;
2021-05-24 20:55:04 +02:00
while ( Token : : Match ( derefValue - > astParent ( ) , " +|-|*|[|. " ) | |
( derefValue - > astParent ( ) & & derefValue - > astParent ( ) - > isCast ( ) ) | |
2021-05-24 21:14:54 +02:00
( deref < arrayDim & & Token : : simpleMatch ( derefValue - > astParent ( ) , " & " ) & & derefValue - > astParent ( ) - > isBinaryOp ( ) ) ) {
2021-05-21 14:30:47 +02:00
const Token * const derefValueParent = derefValue - > astParent ( ) ;
if ( derefValueParent - > str ( ) = = " * " ) {
if ( derefValueParent - > isUnaryOp ( " * " ) )
+ + deref ;
else
break ;
} else if ( derefValueParent - > str ( ) = = " [ " ) {
2021-05-16 22:27:04 +02:00
if ( astIsLhs ( derefValue ) )
+ + deref ;
else
break ;
2021-05-21 14:30:47 +02:00
} else if ( Token : : Match ( derefValueParent , " [+-] " ) ) {
2021-05-24 20:55:04 +02:00
if ( deref > = arrayDim )
2021-05-21 14:30:47 +02:00
break ;
} else if ( derefValueParent - > str ( ) = = " . " )
2021-05-16 22:27:04 +02:00
+ + deref ;
2021-05-21 14:30:47 +02:00
derefValue = derefValueParent ;
2021-05-16 22:27:04 +02:00
if ( deref < arrayDim )
valueExpr = derefValue ;
2019-09-12 13:29:52 +02:00
}
2021-05-16 22:27:04 +02:00
if ( deref < arrayDim ) {
// todo compare deref with array dimensions
derefValue = nullptr ;
2021-05-14 11:54:18 +02:00
}
2021-05-16 23:39:10 +02:00
} else if ( vartok - > astParent ( ) & & vartok - > astParent ( ) - > isUnaryOp ( " & " ) ) {
2021-05-16 22:27:04 +02:00
const Token * child = vartok - > astParent ( ) ;
const Token * parent = child - > astParent ( ) ;
while ( parent & & ( parent - > isCast ( ) | | parent - > str ( ) = = " + " ) ) {
child = parent ;
parent = child - > astParent ( ) ;
2019-02-26 19:26:46 +01:00
}
2021-05-16 22:27:04 +02:00
if ( parent & & ( parent - > isUnaryOp ( " * " ) | | ( parent - > str ( ) = = " [ " & & astIsLhs ( child ) ) ) )
derefValue = parent ;
}
2019-02-27 18:44:30 +01:00
2021-05-16 22:27:04 +02:00
if ( ! valueExpr - > astParent ( ) )
return nullptr ;
// FIXME handle address of!!
if ( derefValue & & derefValue - > astParent ( ) & & derefValue - > astParent ( ) - > isUnaryOp ( " & " ) )
return nullptr ;
2021-05-21 15:27:13 +02:00
// BAILOUT for binary & without parent
2021-05-17 09:22:19 +02:00
if ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " & " ) & & astIsRhs ( valueExpr ) & & Token : : Match ( valueExpr - > astParent ( ) - > tokAt ( - 3 ) , " ( %name% ) & " ) )
return nullptr ;
2021-05-16 22:27:04 +02:00
// safe operations
2021-05-17 05:59:28 +02:00
if ( isVoidCast ( valueExpr - > astParent ( ) ) )
return nullptr ;
if ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " . " ) ) {
const Token * parent = valueExpr - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " . " ) )
parent = parent - > astParent ( ) ;
if ( isVoidCast ( parent ) )
return nullptr ;
}
2021-05-16 22:27:04 +02:00
if ( alloc ! = NO_ALLOC ) {
if ( Token : : Match ( valueExpr - > astParent ( ) , " %comp%|%oror%|&&|?|! " ) )
return nullptr ;
if ( Token : : Match ( valueExpr - > astParent ( ) , " %or%|& " ) & & valueExpr - > astParent ( ) - > isBinaryOp ( ) )
return nullptr ;
if ( alloc = = CTOR_CALL & & derefValue & & Token : : simpleMatch ( derefValue - > astParent ( ) , " ( " ) & & astIsLhs ( derefValue ) )
return nullptr ;
2021-05-21 17:10:49 +02:00
if ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " return " ) )
return nullptr ;
2012-12-20 19:45:30 +01:00
}
2021-05-16 22:27:04 +02:00
// Passing variable to function..
if ( Token : : Match ( valueExpr - > astParent ( ) , " [(,] " ) & & ( valueExpr - > astParent ( ) - > str ( ) = = " , " | | astIsRhs ( valueExpr ) ) ) {
const Token * parent = valueExpr - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " , " ) )
2019-08-24 08:01:55 +02:00
parent = parent - > astParent ( ) ;
if ( Token : : simpleMatch ( parent , " { " ) )
2021-05-16 22:27:04 +02:00
return valueExpr ;
2021-07-01 22:08:00 +02:00
const int use = isFunctionParUsage ( valueExpr , library , pointer , alloc , indirect ) ;
2021-05-16 22:27:04 +02:00
return ( use > 0 ) ? valueExpr : nullptr ;
2019-08-24 08:01:55 +02:00
}
2021-05-16 22:27:04 +02:00
if ( derefValue & & Token : : Match ( derefValue - > astParent ( ) , " [(,] " ) & & ( derefValue - > astParent ( ) - > str ( ) = = " , " | | astIsRhs ( derefValue ) ) ) {
2021-07-01 22:08:00 +02:00
const int use = isFunctionParUsage ( derefValue , library , false , NO_ALLOC , indirect ) ;
2021-05-16 22:27:04 +02:00
return ( use > 0 ) ? derefValue : nullptr ;
2011-12-14 18:28:30 +01:00
}
2021-05-23 14:36:56 +02:00
if ( valueExpr - > astParent ( ) - > isUnaryOp ( " & " ) ) {
const Token * parent = valueExpr - > astParent ( ) ;
if ( Token : : Match ( parent - > astParent ( ) , " [(,] " ) & & ( parent - > astParent ( ) - > str ( ) = = " , " | | astIsRhs ( parent ) ) ) {
2021-07-01 22:08:00 +02:00
const int use = isFunctionParUsage ( valueExpr , library , pointer , alloc , indirect ) ;
2021-05-23 14:36:56 +02:00
return ( use > 0 ) ? valueExpr : nullptr ;
}
return nullptr ;
}
2013-11-16 18:07:33 +01:00
2021-05-16 22:27:04 +02:00
// Assignments;
// * Is this LHS in assignment
// * Passing address in RHS to pointer variable
{
const Token * tok = derefValue ? derefValue : valueExpr ;
2021-05-25 16:13:32 +02:00
if ( alloc = = NO_ALLOC ) {
while ( tok - > valueType ( ) & & tok - > valueType ( ) - > pointer = = 0 & & Token : : simpleMatch ( tok - > astParent ( ) , " . " ) )
tok = tok - > astParent ( ) ;
}
2021-05-16 22:27:04 +02:00
if ( Token : : simpleMatch ( tok - > astParent ( ) , " = " ) ) {
2023-06-20 18:06:57 +02:00
if ( astIsLhs ( tok ) ) {
2023-07-14 19:14:33 +02:00
if ( alloc = = ARRAY | | ! derefValue | | ! derefValue - > isUnaryOp ( " * " ) | | ! pointer )
2023-06-20 18:06:57 +02:00
return nullptr ;
const Token * deref = derefValue - > astOperand1 ( ) ;
while ( deref & & deref - > isCast ( ) )
deref = deref - > astOperand1 ( ) ;
2023-06-21 21:19:22 +02:00
if ( deref = = vartok | | Token : : simpleMatch ( deref , " + " ) )
2023-06-20 18:06:57 +02:00
return nullptr ;
}
2021-05-16 22:27:04 +02:00
if ( alloc ! = NO_ALLOC & & astIsRhs ( valueExpr ) )
return nullptr ;
}
2011-12-27 10:18:49 +01:00
}
2011-12-26 14:01:46 +01:00
2021-05-16 22:27:04 +02:00
// Initialize reference variable
if ( Token : : Match ( ( derefValue ? derefValue : vartok ) - > astParent ( ) , " (|= " ) & & astIsRhs ( derefValue ? derefValue : vartok ) ) {
const Token * rhstok = derefValue ? derefValue : vartok ;
const Token * lhstok = rhstok - > astParent ( ) - > astOperand1 ( ) ;
const Variable * lhsvar = lhstok - > variable ( ) ;
if ( lhsvar & & lhsvar - > isReference ( ) & & lhsvar - > nameToken ( ) = = lhstok )
return nullptr ;
2012-06-13 19:09:51 +02:00
}
2021-05-19 21:12:11 +02:00
// range for loop
2021-05-17 13:31:16 +02:00
if ( Token : : simpleMatch ( valueExpr - > astParent ( ) , " : " ) & &
valueExpr - > astParent ( ) - > astParent ( ) & &
2021-05-19 21:12:11 +02:00
Token : : simpleMatch ( valueExpr - > astParent ( ) - > astParent ( ) - > previous ( ) , " for ( " ) ) {
if ( astIsLhs ( valueExpr ) )
return nullptr ;
// Taking value by reference?
const Token * lhs = valueExpr - > astParent ( ) - > astOperand1 ( ) ;
if ( lhs & & lhs - > variable ( ) & & lhs - > variable ( ) - > nameToken ( ) = = lhs & & lhs - > variable ( ) - > isReference ( ) )
return nullptr ;
}
2021-05-17 13:31:16 +02:00
2021-05-16 22:27:04 +02:00
// Stream read/write
// FIXME this code is a hack!!
2021-07-01 22:08:00 +02:00
if ( cpp & & Token : : Match ( valueExpr - > astParent ( ) , " <<|>> " ) ) {
if ( isLikelyStreamRead ( cpp , vartok - > previous ( ) ) )
2021-05-16 22:27:04 +02:00
return nullptr ;
2019-02-10 19:00:01 +01:00
2022-02-02 12:24:32 +01:00
if ( const auto * vt = valueExpr - > valueType ( ) ) {
if ( vt - > type = = ValueType : : Type : : VOID )
return nullptr ;
// passing a char* to a stream will dereference it
if ( ( alloc = = CTOR_CALL | | alloc = = ARRAY ) & & vt - > pointer & & vt - > type ! = ValueType : : Type : : CHAR & & vt - > type ! = ValueType : : Type : : WCHAR_T )
return nullptr ;
}
2021-05-16 22:27:04 +02:00
}
2021-07-01 22:08:00 +02:00
if ( astIsRhs ( derefValue ) & & isLikelyStreamRead ( cpp , derefValue - > astParent ( ) ) )
2021-05-18 20:13:27 +02:00
return nullptr ;
2021-05-18 21:03:43 +02:00
// Assignment with overloaded &
2021-07-01 22:08:00 +02:00
if ( cpp & & Token : : simpleMatch ( valueExpr - > astParent ( ) , " & " ) & & astIsRhs ( valueExpr ) ) {
2021-05-18 21:03:43 +02:00
const Token * parent = valueExpr - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " & " ) & & parent - > isBinaryOp ( ) )
parent = parent - > astParent ( ) ;
if ( ! parent ) {
const Token * lhs = valueExpr - > astParent ( ) ;
while ( Token : : simpleMatch ( lhs , " & " ) & & lhs - > isBinaryOp ( ) )
lhs = lhs - > astOperand1 ( ) ;
if ( lhs & & lhs - > isName ( ) & & ( ! lhs - > valueType ( ) | | lhs - > valueType ( ) - > type < = ValueType : : Type : : CONTAINER ) )
return nullptr ; // <- possible assignment
}
}
2020-05-28 21:28:18 +02:00
2021-05-16 22:27:04 +02:00
return derefValue ? derefValue : valueExpr ;
2011-12-14 18:28:30 +01:00
}
2021-07-01 22:08:00 +02:00
const Token * CheckUninitVar : : isVariableUsage ( const Token * vartok , bool pointer , Alloc alloc , int indirect ) const
{
2023-11-03 18:27:11 +01:00
return isVariableUsage ( vartok , mSettings - > library , pointer , alloc , indirect ) ;
2021-07-01 22:08:00 +02:00
}
2015-06-19 18:21:46 +02:00
/***
* Is function parameter " used " so a " usage of uninitialized variable " can
* be written ? If parameter is passed " by value " then it is " used " . If it
* is passed " by reference " then it is not necessarily " used " .
* @ return - 1 = > unknown 0 = > not used 1 = > used
*/
2021-07-01 22:08:00 +02:00
int CheckUninitVar : : isFunctionParUsage ( const Token * vartok , const Library & library , bool pointer , Alloc alloc , int indirect )
2015-06-19 18:21:46 +02:00
{
2019-02-26 19:26:46 +01:00
bool unknown = false ;
const Token * parent = getAstParentSkipPossibleCastAndAddressOf ( vartok , & unknown ) ;
if ( unknown | | ! Token : : Match ( parent , " [(,] " ) )
2015-06-19 18:21:46 +02:00
return - 1 ;
// locate start parentheses in function call..
2019-07-13 15:31:17 +02:00
int argumentNumber = 0 ;
2015-06-19 18:21:46 +02:00
const Token * start = vartok ;
while ( start & & ! Token : : Match ( start , " [;{}(] " ) ) {
if ( start - > str ( ) = = " ) " )
start = start - > link ( ) ;
else if ( start - > str ( ) = = " , " )
+ + argumentNumber ;
start = start - > previous ( ) ;
}
2017-05-13 11:06:33 +02:00
if ( ! start )
return - 1 ;
2015-06-19 18:21:46 +02:00
2018-02-04 22:30:08 +01:00
if ( Token : : simpleMatch ( start - > link ( ) , " ) { " ) & & Token : : Match ( start - > previous ( ) , " if|for|while|switch " ) )
2017-04-23 18:05:14 +02:00
return ( ! pointer | | alloc = = NO_ALLOC ) ;
2015-06-19 18:21:46 +02:00
// is this a function call?
2017-05-13 11:06:33 +02:00
if ( Token : : Match ( start - > previous ( ) , " %name% ( " ) ) {
2015-06-19 18:21:46 +02:00
const bool address ( vartok - > previous ( ) - > str ( ) = = " & " ) ;
2015-08-05 10:19:17 +02:00
const bool array ( vartok - > variable ( ) & & vartok - > variable ( ) - > isArray ( ) ) ;
2015-06-19 18:21:46 +02:00
// check how function handle uninitialized data arguments..
const Function * func = start - > previous ( ) - > function ( ) ;
if ( func ) {
const Variable * arg = func - > getArgumentVar ( argumentNumber ) ;
if ( arg ) {
const Token * argStart = arg - > typeStartToken ( ) ;
2016-08-04 09:06:32 +02:00
if ( ! address & & ! array & & Token : : Match ( argStart , " %type% %name%| [,)] " ) )
2015-06-19 18:21:46 +02:00
return 1 ;
2016-08-04 09:06:32 +02:00
if ( pointer & & ! address & & alloc = = NO_ALLOC & & Token : : Match ( argStart , " %type% * %name% [,)] " ) )
2015-06-19 18:21:46 +02:00
return 1 ;
while ( argStart - > previous ( ) & & argStart - > previous ( ) - > isName ( ) )
argStart = argStart - > previous ( ) ;
2018-08-15 18:04:36 +02:00
if ( Token : : Match ( argStart , " const %type% & %name% [,)] " ) ) {
// If it's a record it's ok to pass a partially uninitialized struct.
if ( vartok - > variable ( ) & & vartok - > variable ( ) - > valueType ( ) & & vartok - > variable ( ) - > valueType ( ) - > type = = ValueType : : Type : : RECORD )
return - 1 ;
2015-06-19 18:21:46 +02:00
return 1 ;
2018-08-15 18:04:36 +02:00
}
2015-06-19 18:21:46 +02:00
if ( ( pointer | | address ) & & Token : : Match ( argStart , " const %type% %name% [ " ) & & Token : : Match ( argStart - > linkAt ( 3 ) , " ] [,)] " ) )
return 1 ;
}
} else if ( Token : : Match ( start - > previous ( ) , " if|while|for " ) ) {
// control-flow statement reading the variable "by value"
return alloc = = NO_ALLOC ;
} else {
2021-07-01 22:08:00 +02:00
const bool isnullbad = library . isnullargbad ( start - > previous ( ) , argumentNumber + 1 ) ;
2019-11-13 12:46:54 +01:00
if ( indirect = = 0 & & pointer & & ! address & & isnullbad & & alloc = = NO_ALLOC )
2016-10-29 21:41:55 +02:00
return 1 ;
2020-06-07 20:18:54 +02:00
bool hasIndirect = false ;
2021-07-01 22:08:00 +02:00
const bool isuninitbad = library . isuninitargbad ( start - > previous ( ) , argumentNumber + 1 , indirect , & hasIndirect ) ;
2015-06-19 18:21:46 +02:00
if ( alloc ! = NO_ALLOC )
2020-06-07 20:18:54 +02:00
return ( isnullbad | | hasIndirect ) & & isuninitbad ;
2015-06-19 18:21:46 +02:00
return isuninitbad & & ( ! address | | isnullbad ) ;
}
}
// unknown
return - 1 ;
}
2021-07-01 22:08:00 +02:00
int CheckUninitVar : : isFunctionParUsage ( const Token * vartok , bool pointer , Alloc alloc , int indirect ) const
{
return CheckUninitVar : : isFunctionParUsage ( vartok , mSettings - > library , pointer , alloc , indirect ) ;
}
2015-06-19 18:21:46 +02:00
bool CheckUninitVar : : isMemberVariableAssignment ( const Token * tok , const std : : string & membervar ) const
2013-01-24 19:41:15 +01:00
{
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok , " %name% . %name% " ) & & tok - > strAt ( 2 ) = = membervar ) {
2013-01-24 19:41:15 +01:00
if ( Token : : Match ( tok - > tokAt ( 3 ) , " [=.[] " ) )
return true ;
2023-06-20 18:43:21 +02:00
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " [(,=] & " ) )
2013-01-24 19:41:15 +01:00
return true ;
2023-06-20 18:43:21 +02:00
if ( isLikelyStreamRead ( mTokenizer - > isCPP ( ) , tok - > previous ( ) ) )
2015-05-10 14:27:15 +02:00
return true ;
2023-06-20 18:43:21 +02:00
if ( ( tok - > previous ( ) & & tok - > previous ( ) - > isConstOp ( ) ) | | Token : : Match ( tok - > previous ( ) , " [|= " ) )
2013-01-24 19:41:15 +01:00
; // member variable usage
2013-05-02 20:34:15 +02:00
else if ( tok - > tokAt ( 3 ) - > isConstOp ( ) )
; // member variable usage
2015-06-19 18:21:46 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " [(,] %name% . %name% [,)] " ) & &
1 = = isFunctionParUsage ( tok , false , NO_ALLOC ) ) {
return false ;
} else
2013-01-24 19:41:15 +01:00
return true ;
} else if ( tok - > strAt ( 1 ) = = " = " )
return true ;
2015-11-22 16:20:46 +01:00
else if ( Token : : Match ( tok , " %var% . %name% ( " ) ) {
const Token * ftok = tok - > tokAt ( 2 ) ;
if ( ! ftok - > function ( ) | | ! ftok - > function ( ) - > isConst ( ) )
// TODO: Try to determine if membervar is assigned in method
return true ;
} else if ( tok - > strAt ( - 1 ) = = " & " ) {
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( tok - > tokAt ( - 2 ) , " [(,] & %name% " ) ) {
2014-03-20 06:48:17 +01:00
// locate start parentheses in function call..
2019-07-13 15:31:17 +02:00
int argumentNumber = 0 ;
2014-03-20 06:48:17 +01:00
const Token * ftok = tok ;
while ( ftok & & ! Token : : Match ( ftok , " [;{}(] " ) ) {
if ( ftok - > str ( ) = = " ) " )
ftok = ftok - > link ( ) ;
else if ( ftok - > str ( ) = = " , " )
+ + argumentNumber ;
ftok = ftok - > previous ( ) ;
}
// is this a function call?
2015-11-30 22:13:49 +01:00
ftok = ftok ? ftok - > previous ( ) : nullptr ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( ftok , " %name% ( " ) ) {
2014-03-20 06:48:17 +01:00
// check how function handle uninitialized data arguments..
const Function * function = ftok - > function ( ) ;
2019-03-06 19:26:38 +01:00
if ( ! function & & mSettings ) {
// Function definition not seen, check if direction is specified in the library configuration
const Library : : ArgumentChecks : : Direction argDirection = mSettings - > library . getArgDirection ( ftok , 1 + argumentNumber ) ;
if ( argDirection = = Library : : ArgumentChecks : : Direction : : DIR_IN )
return false ;
2023-06-20 18:43:21 +02:00
if ( argDirection = = Library : : ArgumentChecks : : Direction : : DIR_OUT )
2019-03-08 18:42:24 +01:00
return true ;
2019-03-06 19:26:38 +01:00
}
2015-11-30 22:13:49 +01:00
const Variable * arg = function ? function - > getArgumentVar ( argumentNumber ) : nullptr ;
const Token * argStart = arg ? arg - > typeStartToken ( ) : nullptr ;
2014-03-20 06:48:17 +01:00
while ( argStart & & argStart - > previous ( ) & & argStart - > previous ( ) - > isName ( ) )
argStart = argStart - > previous ( ) ;
2015-01-31 10:50:39 +01:00
if ( Token : : Match ( argStart , " const struct| %type% * const| %name% [,)] " ) )
2014-03-20 06:48:17 +01:00
return false ;
}
2014-05-10 20:20:55 +02:00
2014-09-09 11:11:41 +02:00
else if ( ftok & & Token : : simpleMatch ( ftok - > previous ( ) , " = * ( " ) )
2014-05-10 20:20:55 +02:00
return false ;
2014-03-20 06:48:17 +01:00
}
2013-01-24 19:41:15 +01:00
return true ;
2014-03-20 06:48:17 +01:00
}
2013-01-24 19:41:15 +01:00
return false ;
}
2015-01-23 19:38:39 +01:00
bool CheckUninitVar : : isMemberVariableUsage ( const Token * tok , bool isPointer , Alloc alloc , const std : : string & membervar ) const
2013-01-24 19:41:15 +01:00
{
2015-06-19 18:21:46 +02:00
if ( Token : : Match ( tok - > previous ( ) , " [(,] %name% . %name% [,)] " ) & &
tok - > strAt ( 2 ) = = membervar ) {
2018-04-04 21:51:31 +02:00
const int use = isFunctionParUsage ( tok , isPointer , alloc ) ;
2015-06-19 18:21:46 +02:00
if ( use = = 1 )
return true ;
}
2013-01-24 19:41:15 +01:00
if ( isMemberVariableAssignment ( tok , membervar ) )
return false ;
2019-06-02 20:19:53 +02:00
if ( Token : : Match ( tok , " %name% . %name% " ) & & tok - > strAt ( 2 ) = = membervar & & ! ( tok - > tokAt ( - 2 ) - > variable ( ) & & tok - > tokAt ( - 2 ) - > variable ( ) - > isReference ( ) ) ) {
const Token * parent = tok - > next ( ) - > astParent ( ) ;
2023-03-17 13:51:55 +01:00
return ! parent | | ! parent - > isUnaryOp ( " & " ) ;
2023-06-20 18:43:21 +02:00
}
if ( ! isPointer & & ! Token : : simpleMatch ( tok - > astParent ( ) , " . " ) & & Token : : Match ( tok - > previous ( ) , " [(,] %name% [,)] " ) & & isVariableUsage ( tok , isPointer , alloc ) )
2013-01-24 19:41:15 +01:00
return true ;
2023-08-12 23:46:31 +02:00
if ( ! isPointer & & Token : : Match ( tok - > previous ( ) , " = %name% ; " ) ) {
const Token * lhs = tok - > previous ( ) - > astOperand1 ( ) ;
return ! ( lhs & & lhs - > variable ( ) & & lhs - > variable ( ) - > isReference ( ) & & lhs = = lhs - > variable ( ) - > nameToken ( ) ) ;
}
2014-05-10 19:56:44 +02:00
2014-05-10 20:20:55 +02:00
// = *(&var);
2023-06-20 18:43:21 +02:00
if ( ! isPointer & &
Token : : simpleMatch ( tok - > astParent ( ) , " & " ) & &
Token : : simpleMatch ( tok - > astParent ( ) - > astParent ( ) , " * " ) & &
Token : : Match ( tok - > astParent ( ) - > astParent ( ) - > astParent ( ) , " = * (| & " ) & &
tok - > astParent ( ) - > astParent ( ) - > astParent ( ) - > astOperand2 ( ) = = tok - > astParent ( ) - > astParent ( ) )
2014-05-10 20:20:55 +02:00
return true ;
2023-03-12 14:15:29 +01:00
// TODO: this used to be experimental - enable or remove see #5586
2023-06-20 18:43:21 +02:00
if ( ( false ) & & // NOLINT(readability-simplify-boolean-expr)
! isPointer & &
Token : : Match ( tok - > tokAt ( - 2 ) , " [(,] & %name% [,)] " ) & &
isVariableUsage ( tok , isPointer , alloc ) )
2014-03-20 08:01:48 +01:00
return true ;
2014-03-20 07:19:35 +01:00
2013-01-24 19:41:15 +01:00
return false ;
}
2011-12-13 21:57:27 +01:00
2010-10-31 12:31:11 +01:00
void CheckUninitVar : : uninitdataError ( const Token * tok , const std : : string & varname )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " uninitdata " , " $symbol: " + varname + " \n Memory is allocated but not initialized: $symbol " , CWE_USE_OF_UNINITIALIZED_VARIABLE , Certainty : : normal ) ;
2010-10-31 12:31:11 +01:00
}
2019-08-15 10:44:55 +02:00
void CheckUninitVar : : uninitvarError ( const Token * tok , const std : : string & varname , ErrorPath errorPath )
2010-10-31 12:31:11 +01:00
{
2022-04-27 17:37:37 +02:00
if ( diag ( tok ) )
return ;
2019-08-15 10:44:55 +02:00
errorPath . emplace_back ( tok , " " ) ;
2022-04-27 17:37:37 +02:00
reportError ( errorPath ,
Severity : : error ,
" legacyUninitvar " ,
" $symbol: " + varname + " \n Uninitialized variable: $symbol " ,
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
Certainty : : normal ) ;
2010-10-31 12:31:11 +01:00
}
2013-01-17 21:04:22 +01:00
2021-10-30 07:43:37 +02:00
void CheckUninitVar : : uninitvarError ( const Token * tok , const ValueFlow : : Value & v )
{
2022-06-28 22:52:35 +02:00
if ( ! mSettings - > isEnabled ( & v ) )
return ;
2022-04-27 17:37:37 +02:00
if ( diag ( tok ) )
return ;
2021-10-30 07:43:37 +02:00
const Token * ltok = tok ;
if ( tok & & Token : : simpleMatch ( tok - > astParent ( ) , " . " ) & & astIsRHS ( tok ) )
ltok = tok - > astParent ( ) ;
const std : : string & varname = ltok ? ltok - > expressionString ( ) : " x " ;
ErrorPath errorPath = v . errorPath ;
errorPath . emplace_back ( tok , " " ) ;
2022-06-28 22:52:35 +02:00
auto severity = v . isKnown ( ) ? Severity : : error : Severity : : warning ;
auto certainty = v . isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ;
2021-10-30 07:43:37 +02:00
if ( v . subexpressions . empty ( ) ) {
reportError ( errorPath ,
2022-06-28 22:52:35 +02:00
severity ,
2021-10-30 07:43:37 +02:00
" uninitvar " ,
" $symbol: " + varname + " \n Uninitialized variable: $symbol " ,
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
2022-06-28 22:52:35 +02:00
certainty ) ;
2021-10-30 07:43:37 +02:00
return ;
}
std : : string vars = v . subexpressions . size ( ) = = 1 ? " variable: " : " variables: " ;
std : : string prefix ;
for ( const std : : string & var : v . subexpressions ) {
vars + = prefix + varname + " . " + var ;
prefix = " , " ;
}
reportError ( errorPath ,
2022-06-28 22:52:35 +02:00
severity ,
2021-10-30 07:43:37 +02:00
" uninitvar " ,
" $symbol: " + varname + " \n Uninitialized " + vars ,
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
2022-06-28 22:52:35 +02:00
certainty ) ;
2021-10-30 07:43:37 +02:00
}
2013-01-17 21:04:22 +01:00
void CheckUninitVar : : uninitStructMemberError ( const Token * tok , const std : : string & membername )
{
reportError ( tok ,
2013-01-17 23:18:37 +01:00
Severity : : error ,
2013-01-17 21:04:22 +01:00
" uninitStructMember " ,
2021-02-24 22:00:06 +01:00
" $symbol: " + membername + " \n Uninitialized struct member: $symbol " , CWE_USE_OF_UNINITIALIZED_VARIABLE , Certainty : : normal ) ;
2013-01-17 21:04:22 +01:00
}
2014-08-05 06:24:23 +02:00
2017-04-23 18:05:14 +02:00
void CheckUninitVar : : valueFlowUninit ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckUninitVar::valueFlowUninit " ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2017-04-23 18:05:14 +02:00
2021-10-04 07:50:23 +02:00
std : : unordered_set < nonneg int > ids ;
2022-10-02 07:12:40 +02:00
for ( const bool subfunction : { false , true } ) {
2021-10-04 07:50:23 +02:00
// check every executable scope
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2023-08-23 12:07:47 +02:00
if ( isUnevaluated ( tok ) ) {
2021-10-04 07:50:23 +02:00
tok = tok - > linkAt ( 1 ) ;
continue ;
}
if ( ids . count ( tok - > exprId ( ) ) > 0 )
continue ;
2022-04-11 07:23:44 +02:00
if ( ! tok - > variable ( ) & & ! tok - > isUnaryOp ( " * " ) & & ! tok - > isUnaryOp ( " & " ) )
2021-10-04 07:50:23 +02:00
continue ;
if ( Token : : Match ( tok , " %name% ( " ) )
2021-05-24 19:59:22 +02:00
continue ;
2021-10-04 07:50:23 +02:00
const Token * parent = tok - > astParent ( ) ;
while ( Token : : simpleMatch ( parent , " . " ) )
parent = parent - > astParent ( ) ;
if ( parent & & parent - > isUnaryOp ( " & " ) )
2021-05-23 14:36:45 +02:00
continue ;
2021-10-04 07:50:23 +02:00
if ( isVoidCast ( parent ) )
2019-09-03 06:46:34 +02:00
continue ;
2021-10-04 07:50:23 +02:00
auto v = std : : find_if (
2022-12-30 15:13:47 +01:00
tok - > values ( ) . cbegin ( ) , tok - > values ( ) . cend ( ) , std : : mem_fn ( & ValueFlow : : Value : : isUninitValue ) ) ;
if ( v = = tok - > values ( ) . cend ( ) )
2021-10-04 07:50:23 +02:00
continue ;
if ( v - > tokvalue & & ids . count ( v - > tokvalue - > exprId ( ) ) > 0 )
continue ;
if ( subfunction = = ( v - > path = = 0 ) )
continue ;
if ( v - > isInconclusive ( ) )
continue ;
if ( v - > indirect > 1 | | v - > indirect < 0 )
continue ;
bool uninitderef = false ;
if ( tok - > variable ( ) ) {
bool unknown ;
2023-03-12 19:57:11 +01:00
const bool isarray = tok - > variable ( ) - > isArray ( ) ;
2023-06-17 10:20:20 +02:00
if ( isarray & & tok - > variable ( ) - > isMember ( ) )
continue ; // Todo: this is a bailout
2021-10-04 07:50:23 +02:00
const bool deref = CheckNullPointer : : isPointerDeRef ( tok , unknown , mSettings ) ;
uninitderef = deref & & v - > indirect = = 0 ;
const bool isleaf = isLeafDot ( tok ) | | uninitderef ;
2023-03-04 11:57:12 +01:00
if ( ! isleaf & & Token : : Match ( tok - > astParent ( ) , " . %name% " ) & & ( tok - > astParent ( ) - > next ( ) - > varId ( ) | | tok - > astParent ( ) - > next ( ) - > isEnumerator ( ) ) )
2021-10-04 07:50:23 +02:00
continue ;
}
2023-08-12 23:46:31 +02:00
const ExprUsage usage = getExprUsage ( tok , v - > indirect , mSettings , mTokenizer - > isCPP ( ) ) ;
2023-06-17 10:20:20 +02:00
if ( usage = = ExprUsage : : NotUsed | | usage = = ExprUsage : : Inconclusive )
2022-04-11 07:23:44 +02:00
continue ;
2022-03-24 06:35:44 +01:00
if ( ! v - > subexpressions . empty ( ) & & usage = = ExprUsage : : PassedByReference )
2021-10-30 07:43:37 +02:00
continue ;
2022-03-24 06:35:44 +01:00
if ( usage ! = ExprUsage : : Used ) {
2021-10-20 20:51:59 +02:00
if ( ! ( Token : : Match ( tok - > astParent ( ) , " . %name% ( " ) & & uninitderef ) & &
isVariableChanged ( tok , v - > indirect , mSettings , mTokenizer - > isCPP ( ) ) )
continue ;
bool inconclusive = false ;
if ( isVariableChangedByFunctionCall ( tok , v - > indirect , mSettings , & inconclusive ) | | inconclusive )
continue ;
}
2021-10-30 07:43:37 +02:00
uninitvarError ( tok , * v ) ;
2021-10-04 07:50:23 +02:00
ids . insert ( tok - > exprId ( ) ) ;
if ( v - > tokvalue )
ids . insert ( v - > tokvalue - > exprId ( ) ) ;
2019-09-03 06:46:34 +02:00
}
2017-04-23 18:05:14 +02:00
}
}
}
2022-09-16 18:58:59 +02:00
// NOLINTNEXTLINE(readability-non-const-parameter) - used as callback so we need to preserve the signature
2023-11-03 18:27:11 +01:00
static bool isVariableUsage ( const Settings * settings , const Token * vartok , MathLib : : bigint * value )
2018-12-26 19:17:49 +01:00
{
2019-03-23 08:36:10 +01:00
( void ) value ;
2023-11-03 18:27:11 +01:00
return CheckUninitVar : : isVariableUsage ( vartok , settings - > library , true , CheckUninitVar : : Alloc : : ARRAY ) ;
2018-12-26 19:17:49 +01:00
}
2023-12-14 17:44:09 +01:00
// a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now
// see https://trac.cppcheck.net/ticket/12108 for more details
# ifdef __clang__
inline namespace CheckUninitVar_internal
# else
namespace
# endif
{
2023-10-21 16:58:29 +02:00
/* data for multifile checking */
class MyFileInfo : public Check : : FileInfo {
public :
/** function arguments that data are unconditionally read */
std : : list < CTU : : FileInfo : : UnsafeUsage > unsafeUsage ;
/** Convert data into xml string */
std : : string toString ( ) const override
{
return CTU : : toString ( unsafeUsage ) ;
}
} ;
}
2023-11-03 18:27:11 +01:00
Check : : FileInfo * CheckUninitVar : : getFileInfo ( const Tokenizer * tokenizer , const Settings * settings ) const
2018-12-18 07:56:33 +01:00
{
2023-11-03 18:27:11 +01:00
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : getUnsafeUsage ( tokenizer , settings , : : isVariableUsage ) ;
2018-12-26 19:17:49 +01:00
if ( unsafeUsage . empty ( ) )
return nullptr ;
2018-12-18 07:56:33 +01:00
MyFileInfo * fileInfo = new MyFileInfo ;
2020-02-04 18:56:01 +01:00
fileInfo - > unsafeUsage = unsafeUsage ;
2018-12-18 07:56:33 +01:00
return fileInfo ;
}
Check : : FileInfo * CheckUninitVar : : loadFileInfoFromXml ( const tinyxml2 : : XMLElement * xmlElement ) const
{
2018-12-25 21:11:23 +01:00
const std : : list < CTU : : FileInfo : : UnsafeUsage > & unsafeUsage = CTU : : loadUnsafeUsageListFromXml ( xmlElement ) ;
if ( unsafeUsage . empty ( ) )
return nullptr ;
MyFileInfo * fileInfo = new MyFileInfo ;
fileInfo - > unsafeUsage = unsafeUsage ;
2018-12-18 07:56:33 +01:00
return fileInfo ;
}
2018-12-25 21:11:23 +01:00
bool CheckUninitVar : : analyseWholeProgram ( const CTU : : FileInfo * ctu , const std : : list < Check : : FileInfo * > & fileInfo , const Settings & settings , ErrorLogger & errorLogger )
2018-12-18 07:56:33 +01:00
{
2018-12-25 21:11:23 +01:00
if ( ! ctu )
2018-12-18 07:56:33 +01:00
return false ;
2018-12-25 21:11:23 +01:00
bool foundErrors = false ;
2018-12-18 07:56:33 +01:00
( void ) settings ; // This argument is unused
2018-12-30 16:23:25 +01:00
const std : : map < std : : string , std : : list < const CTU : : FileInfo : : CallBase * > > callsMap = ctu - > getCallsMap ( ) ;
2018-12-25 21:11:23 +01:00
2023-05-31 16:51:03 +02:00
for ( const Check : : FileInfo * fi1 : fileInfo ) {
2022-12-19 20:01:12 +01:00
const MyFileInfo * fi = dynamic_cast < const MyFileInfo * > ( fi1 ) ;
2018-12-18 07:56:33 +01:00
if ( ! fi )
continue ;
2018-12-25 21:11:23 +01:00
for ( const CTU : : FileInfo : : UnsafeUsage & unsafeUsage : fi - > unsafeUsage ) {
2018-12-26 15:56:10 +01:00
const CTU : : FileInfo : : FunctionCall * functionCall = nullptr ;
2020-05-23 07:16:49 +02:00
const std : : list < ErrorMessage : : FileLocation > & locationList =
2021-01-09 20:32:38 +01:00
CTU : : FileInfo : : getErrorPath ( CTU : : FileInfo : : InvalidValueType : : uninit ,
2021-01-10 14:46:19 +01:00
unsafeUsage ,
callsMap ,
" Using argument ARG " ,
& functionCall ,
false ) ;
2018-12-26 15:56:10 +01:00
if ( locationList . empty ( ) )
continue ;
2018-12-18 07:56:33 +01:00
2020-05-23 07:16:49 +02:00
const ErrorMessage errmsg ( locationList ,
emptyString ,
Severity : : error ,
" Using argument " + unsafeUsage . myArgumentName + " that points at uninitialized variable " + functionCall - > callArgumentExpression ,
" ctuuninitvar " ,
CWE_USE_OF_UNINITIALIZED_VARIABLE ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
2018-12-26 15:56:10 +01:00
errorLogger . reportErr ( errmsg ) ;
2018-12-25 21:11:23 +01:00
2018-12-26 15:56:10 +01:00
foundErrors = true ;
2018-12-18 07:56:33 +01:00
}
}
return foundErrors ;
}