2015-11-21 20:24:30 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2022-02-05 11:45:17 +01:00
* Copyright ( C ) 2007 - 2022 Cppcheck team .
2015-11-21 20:24:30 +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/>.
*/
//---------------------------------------------------------------------------
// Check functions
//---------------------------------------------------------------------------
# include "checkfunctions.h"
2017-05-27 04:33:47 +02:00
2017-04-20 19:57:39 +02:00
# include "astutils.h"
2017-05-27 04:33:47 +02:00
# include "mathlib.h"
# include "standards.h"
2015-11-21 20:24:30 +01:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
# include "valueflow.h"
2018-07-15 22:47:56 +02:00
# include <iomanip>
2022-01-27 19:03:20 +01:00
# include <ostream>
# include <unordered_map>
2017-05-27 04:33:47 +02:00
# include <vector>
2015-11-21 20:24:30 +01:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckFunctions instance ;
}
CWE mapping of duplicateExpression, duplicateBreak (CWE561), unreachableCode, unsignedLessThanZero, unsignedPositive, pointerLessThanZero, pointerPositive, varFuncNullUB, nanInArithmeticExpression, commaSeparatedReturn, (#797)
ignoredReturnValue
2016-05-22 13:17:38 +02:00
static const CWE CWE252 ( 252U ) ; // Unchecked Return Value
2016-08-04 13:55:38 +02:00
static const CWE CWE477 ( 477U ) ; // Use of Obsolete Functions
2016-05-14 11:13:33 +02:00
static const CWE CWE758 ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
static const CWE CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
2017-04-23 07:53:41 +02:00
static const CWE CWE686 ( 686U ) ; // Function Call With Incorrect Argument Type
static const CWE CWE687 ( 687U ) ; // Function Call With Incorrectly Specified Argument Value
static const CWE CWE688 ( 688U ) ; // Function Call With Incorrect Variable or Reference as Argument
2016-01-25 20:01:48 +01:00
2015-11-22 13:48:55 +01:00
void CheckFunctions : : checkProhibitedFunctions ( )
2015-11-21 20:24:30 +01:00
{
2021-02-24 22:00:06 +01:00
const bool checkAlloca = mSettings - > severity . isEnabled ( Severity : : warning ) & & ( ( mSettings - > standards . c > = Standards : : C99 & & mTokenizer - > isC ( ) ) | | mSettings - > standards . cpp > = Standards : : CPP11 ) ;
2015-11-21 20:24:30 +01:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-04-20 17:42:25 +02:00
if ( ! Token : : Match ( tok , " %name% ( " ) & & tok - > varId ( ) = = 0 )
continue ;
// alloca() is special as it depends on the code being C or C++, so it is not in Library
if ( checkAlloca & & Token : : simpleMatch ( tok , " alloca ( " ) & & ( ! tok - > function ( ) | | tok - > function ( ) - > nestedIn - > type = = Scope : : eGlobal ) ) {
2018-06-16 16:10:28 +02:00
if ( mTokenizer - > isC ( ) ) {
if ( mSettings - > standards . c > Standards : : C89 )
2015-11-21 20:24:30 +01:00
reportError ( tok , Severity : : warning , " allocaCalled " ,
2018-04-09 06:43:48 +02:00
" $symbol:alloca \n "
2017-04-20 17:42:25 +02:00
" Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead. \n "
" The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or "
2015-11-21 20:24:30 +01:00
" a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
" (http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca). " ) ;
2017-04-20 17:42:25 +02:00
} else
reportError ( tok , Severity : : warning , " allocaCalled " ,
2018-04-09 06:43:48 +02:00
" $symbol:alloca \n "
2017-04-20 17:42:25 +02:00
" Obsolete function 'alloca' called. \n "
" The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or "
" a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
" (http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca). " ) ;
} else {
if ( tok - > function ( ) & & tok - > function ( ) - > hasBody ( ) )
continue ;
2015-11-21 20:24:30 +01:00
2018-06-16 16:10:28 +02:00
const Library : : WarnInfo * wi = mSettings - > library . getWarnInfo ( tok ) ;
2017-04-20 17:42:25 +02:00
if ( wi ) {
2021-02-24 22:00:06 +01:00
if ( mSettings - > severity . isEnabled ( wi - > severity ) & & mSettings - > standards . c > = wi - > standards . c & & mSettings - > standards . cpp > = wi - > standards . cpp ) {
2020-09-27 21:42:07 +02:00
const std : : string daca = mSettings - > daca ? " prohibited " : " " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , wi - > severity , daca + tok - > str ( ) + " Called " , wi - > message , CWE477 , Certainty : : normal ) ;
2015-11-21 20:24:30 +01:00
}
}
}
}
}
}
2015-11-22 13:48:55 +01:00
//---------------------------------------------------------------------------
2022-06-02 01:09:09 +02:00
// Check <valid>, <strz> and <not-bool>
2015-11-22 13:48:55 +01:00
//---------------------------------------------------------------------------
void CheckFunctions : : invalidFunctionUsage ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-04-20 19:57:39 +02:00
if ( ! Token : : Match ( tok , " %name% ( !!) " ) )
2015-11-22 13:48:55 +01:00
continue ;
const Token * const functionToken = tok ;
2017-04-20 19:57:39 +02:00
const std : : vector < const Token * > arguments = getArguments ( tok ) ;
2019-07-16 08:49:02 +02:00
for ( int argnr = 1 ; argnr < = arguments . size ( ) ; + + argnr ) {
2017-04-20 19:57:39 +02:00
const Token * const argtok = arguments [ argnr - 1 ] ;
2015-11-22 13:48:55 +01:00
2017-04-20 19:57:39 +02:00
// check <valid>...</valid>
2018-06-16 16:10:28 +02:00
const ValueFlow : : Value * invalidValue = argtok - > getInvalidValue ( functionToken , argnr , mSettings ) ;
2017-04-20 22:14:54 +02:00
if ( invalidValue ) {
2018-06-16 16:10:28 +02:00
invalidFunctionArgError ( argtok , functionToken - > next ( ) - > astOperand1 ( ) - > expressionString ( ) , argnr , invalidValue , mSettings - > library . validarg ( functionToken , argnr ) ) ;
2015-11-22 13:48:55 +01:00
}
2017-04-20 19:57:39 +02:00
if ( astIsBool ( argtok ) ) {
// check <not-bool>
2018-06-16 16:10:28 +02:00
if ( mSettings - > library . isboolargbad ( functionToken , argnr ) )
2017-04-20 19:57:39 +02:00
invalidFunctionArgBoolError ( argtok , functionToken - > str ( ) , argnr ) ;
2017-04-20 22:14:54 +02:00
2017-04-20 19:57:39 +02:00
// Are the values 0 and 1 valid?
2018-07-15 23:05:48 +02:00
else if ( ! mSettings - > library . isIntArgValid ( functionToken , argnr , 0 ) )
2018-06-16 16:10:28 +02:00
invalidFunctionArgError ( argtok , functionToken - > str ( ) , argnr , nullptr , mSettings - > library . validarg ( functionToken , argnr ) ) ;
2018-07-15 23:05:48 +02:00
else if ( ! mSettings - > library . isIntArgValid ( functionToken , argnr , 1 ) )
2018-06-16 16:10:28 +02:00
invalidFunctionArgError ( argtok , functionToken - > str ( ) , argnr , nullptr , mSettings - > library . validarg ( functionToken , argnr ) ) ;
2017-04-20 19:57:39 +02:00
}
2022-06-03 13:10:45 +02:00
// check <strz>
2018-10-14 18:49:34 +02:00
if ( mSettings - > library . isargstrz ( functionToken , argnr ) ) {
2018-10-16 06:54:25 +02:00
if ( Token : : Match ( argtok , " & %var% !![ " ) & & argtok - > next ( ) & & argtok - > next ( ) - > valueType ( ) ) {
2018-10-14 18:49:34 +02:00
const ValueType * valueType = argtok - > next ( ) - > valueType ( ) ;
const Variable * variable = argtok - > next ( ) - > variable ( ) ;
2022-06-04 12:00:54 +02:00
if ( ( valueType - > type = = ValueType : : Type : : CHAR | | ( valueType - > type = = ValueType : : Type : : RECORD & & Token : : Match ( argtok , " & %var% . %var% ,|) " ) ) ) & &
2022-06-03 18:59:09 +02:00
! variable - > isArray ( ) & &
( variable - > isConst ( ) | | ! variable - > isGlobal ( ) ) & &
2018-10-14 18:49:34 +02:00
( ! argtok - > next ( ) - > hasKnownValue ( ) | | argtok - > next ( ) - > getValue ( 0 ) = = nullptr ) ) {
invalidFunctionArgStrError ( argtok , functionToken - > str ( ) , argnr ) ;
}
}
2022-06-02 01:09:09 +02:00
const ValueType * const valueType = argtok - > valueType ( ) ;
const Variable * const variable = argtok - > variable ( ) ;
// Is non-null terminated local variable of type char (e.g. char buf[] = {'x'};) ?
if ( variable & & variable - > isLocal ( )
2022-07-11 22:58:19 +02:00
& & valueType & & ( valueType - > type = = ValueType : : Type : : CHAR | | valueType - > type = = ValueType : : Type : : WCHAR_T )
& & ! isVariablesChanged ( variable - > declEndToken ( ) , functionToken , 0 /*indirect*/ , { variable } , mSettings , mTokenizer - > isCPP ( ) ) ) {
2022-06-02 01:09:09 +02:00
const Token * varTok = variable - > declEndToken ( ) ;
auto count = - 1 ; // Find out explicitly set count, e.g.: char buf[3] = {...}. Variable 'count' is set to 3 then.
2022-06-21 00:12:11 +02:00
if ( varTok & & Token : : simpleMatch ( varTok - > astOperand1 ( ) , " [ " ) )
2022-06-02 01:09:09 +02:00
{
2022-06-21 00:12:11 +02:00
const Token * const countTok = varTok - > astOperand1 ( ) - > astOperand2 ( ) ;
2022-06-02 01:09:09 +02:00
if ( countTok & & countTok - > hasKnownIntValue ( ) )
count = countTok - > getKnownIntValue ( ) ;
}
2022-06-02 07:27:19 +02:00
if ( Token : : simpleMatch ( varTok , " = { " ) ) {
varTok = varTok - > tokAt ( 1 ) ;
2022-06-02 19:30:29 +02:00
auto charsUntilFirstZero = 0 ;
2022-06-03 13:10:45 +02:00
bool search = true ;
while ( search & & varTok & & ! Token : : simpleMatch ( varTok - > next ( ) , " } " ) ) {
2022-06-02 01:09:09 +02:00
varTok = varTok - > next ( ) ;
2022-06-03 13:10:45 +02:00
if ( ! Token : : simpleMatch ( varTok , " , " ) ) {
if ( Token : : Match ( varTok , " %op% " ) ) {
varTok = varTok - > next ( ) ;
continue ;
}
+ + charsUntilFirstZero ;
if ( varTok & & varTok - > hasKnownIntValue ( ) & & varTok - > getKnownIntValue ( ) = = 0 )
search = false ; // stop counting for cases like char buf[3] = {'x', '\0', 'y'};
2022-06-02 19:30:29 +02:00
}
2022-06-02 01:09:09 +02:00
}
if ( varTok & & varTok - > hasKnownIntValue ( ) & & varTok - > getKnownIntValue ( ) ! = 0
2022-06-02 19:30:29 +02:00
& & ( count = = - 1 | | ( count > 0 & & count < = charsUntilFirstZero ) ) ) {
2022-06-02 01:09:09 +02:00
invalidFunctionArgStrError ( argtok , functionToken - > str ( ) , argnr ) ;
}
2022-06-20 20:02:22 +02:00
} else if ( count > - 1 & & Token : : Match ( varTok , " = %str% " ) ) {
const Token * strTok = varTok - > getValueTokenMinStrSize ( mSettings ) ;
if ( strTok ) {
const int strSize = Token : : getStrArraySize ( strTok ) ;
2022-06-21 13:33:45 +02:00
if ( strSize > count & & strTok - > str ( ) . find ( ' \0 ' ) = = std : : string : : npos )
2022-06-20 20:02:22 +02:00
invalidFunctionArgStrError ( argtok , functionToken - > str ( ) , argnr ) ;
}
2022-06-02 01:09:09 +02:00
}
}
2018-10-14 18:49:34 +02:00
}
2017-04-20 19:57:39 +02:00
}
2015-11-22 13:48:55 +01:00
}
}
}
2017-04-20 22:14:54 +02:00
void CheckFunctions : : invalidFunctionArgError ( const Token * tok , const std : : string & functionName , int argnr , const ValueFlow : : Value * invalidValue , const std : : string & validstr )
2015-11-22 13:48:55 +01:00
{
std : : ostringstream errmsg ;
2018-04-09 06:43:48 +02:00
errmsg < < " $symbol: " < < functionName < < ' \n ' ;
2017-04-20 22:14:54 +02:00
if ( invalidValue & & invalidValue - > condition )
errmsg < < ValueFlow : : eitherTheConditionIsRedundant ( invalidValue - > condition )
2018-04-09 06:43:48 +02:00
< < " or $symbol() argument nr " < < argnr < < " can have invalid value. " ;
2017-04-20 22:14:54 +02:00
else
2018-04-09 06:43:48 +02:00
errmsg < < " Invalid $symbol() argument nr " < < argnr < < ' . ' ;
2017-04-20 22:14:54 +02:00
if ( invalidValue )
2018-07-15 22:47:56 +02:00
errmsg < < " The value is " < < std : : setprecision ( 10 ) < < ( invalidValue - > isIntValue ( ) ? invalidValue - > intvalue : invalidValue - > floatValue ) < < " but the valid values are ' " < < validstr < < " '. " ;
2017-04-20 22:14:54 +02:00
else
errmsg < < " The value is 0 or 1 (boolean) but the valid values are ' " < < validstr < < " '. " ;
2017-05-23 11:57:25 +02:00
if ( invalidValue )
reportError ( getErrorPath ( tok , invalidValue , " Invalid argument " ) ,
2022-01-25 12:13:49 +01:00
invalidValue - > errorSeverity ( ) & & invalidValue - > isKnown ( ) ? Severity : : error : Severity : : warning ,
2017-05-23 11:57:25 +02:00
" invalidFunctionArg " ,
errmsg . str ( ) ,
CWE628 ,
2021-02-24 22:00:06 +01:00
invalidValue - > isInconclusive ( ) ? Certainty : : inconclusive : Certainty : : normal ) ;
2017-05-23 11:57:25 +02:00
else
reportError ( tok ,
Severity : : error ,
" invalidFunctionArg " ,
errmsg . str ( ) ,
CWE628 ,
2021-02-24 22:00:06 +01:00
Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
}
void CheckFunctions : : invalidFunctionArgBoolError ( const Token * tok , const std : : string & functionName , int argnr )
{
std : : ostringstream errmsg ;
2018-04-09 06:43:48 +02:00
errmsg < < " $symbol: " < < functionName < < ' \n ' ;
errmsg < < " Invalid $symbol() argument nr " < < argnr < < " . A non-boolean value is required. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " invalidFunctionArgBool " , errmsg . str ( ) , CWE628 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
}
2019-07-16 08:49:02 +02:00
void CheckFunctions : : invalidFunctionArgStrError ( const Token * tok , const std : : string & functionName , nonneg int argnr )
2018-10-14 18:49:34 +02:00
{
std : : ostringstream errmsg ;
errmsg < < " $symbol: " < < functionName < < ' \n ' ;
errmsg < < " Invalid $symbol() argument nr " < < argnr < < " . A nul-terminated string is required. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " invalidFunctionArgStr " , errmsg . str ( ) , CWE628 , Certainty : : normal ) ;
2018-10-14 18:49:34 +02:00
}
2015-11-22 13:48:55 +01:00
//---------------------------------------------------------------------------
// Check for ignored return values.
//---------------------------------------------------------------------------
void CheckFunctions : : checkIgnoredReturnValue ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) & & ! mSettings - > severity . isEnabled ( Severity : : style ) )
2015-11-22 13:48:55 +01:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-03-08 10:23:38 +01:00
// skip c++11 initialization, ({...})
2019-04-26 12:22:31 +02:00
if ( Token : : Match ( tok , " %var%|(|,|return { " ) )
2017-02-12 16:33:28 +01:00
tok = tok - > linkAt ( 1 ) ;
2017-06-10 08:23:26 +02:00
else if ( Token : : Match ( tok , " [(<] " ) & & tok - > link ( ) )
2017-04-12 10:44:08 +02:00
tok = tok - > link ( ) ;
2017-04-11 20:16:33 +02:00
2021-01-23 18:19:51 +01:00
if ( tok - > varId ( ) | | ! Token : : Match ( tok , " %name% ( " ) | | tok - > isKeyword ( ) )
2017-03-05 22:23:16 +01:00
continue ;
2021-04-04 18:26:07 +02:00
const Token * parent = tok - > next ( ) - > astParent ( ) ;
2021-04-05 05:17:29 +02:00
while ( Token : : Match ( parent , " %cop% " ) ) {
if ( Token : : Match ( parent , " <<|>> " ) & & ! parent - > astParent ( ) )
break ;
2021-04-04 18:26:07 +02:00
parent = parent - > astParent ( ) ;
2021-04-05 05:17:29 +02:00
}
2021-04-04 18:26:07 +02:00
if ( parent )
2015-11-22 13:48:55 +01:00
continue ;
if ( ! tok - > scope ( ) - > isExecutable ( ) ) {
2018-04-27 22:36:30 +02:00
tok = tok - > scope ( ) - > bodyEnd ;
2015-11-22 13:48:55 +01:00
continue ;
}
2018-05-29 21:43:56 +02:00
if ( ( ! tok - > function ( ) | | ! Token : : Match ( tok - > function ( ) - > retDef , " void %name% " ) ) & &
2020-11-06 19:50:05 +01:00
! WRONG_DATA ( ! tok - > next ( ) - > astOperand1 ( ) , tok ) ) {
2020-11-05 13:35:52 +01:00
const Library : : UseRetValType retvalTy = mSettings - > library . getUseRetValType ( tok ) ;
2021-02-24 22:00:06 +01:00
if ( mSettings - > severity . isEnabled ( Severity : : warning ) & &
2020-11-05 13:35:52 +01:00
( ( retvalTy = = Library : : UseRetValType : : DEFAULT ) | |
2020-11-06 19:50:05 +01:00
( tok - > function ( ) & & tok - > function ( ) - > isAttributeNodiscard ( ) ) ) )
ignoredReturnValueError ( tok , tok - > next ( ) - > astOperand1 ( ) - > expressionString ( ) ) ;
2021-02-24 22:00:06 +01:00
else if ( mSettings - > severity . isEnabled ( Severity : : style ) & &
2020-11-05 13:35:52 +01:00
retvalTy = = Library : : UseRetValType : : ERROR_CODE )
2020-11-06 19:50:05 +01:00
ignoredReturnErrorCode ( tok , tok - > next ( ) - > astOperand1 ( ) - > expressionString ( ) ) ;
2018-05-29 21:43:56 +02:00
}
2015-11-22 13:48:55 +01:00
}
}
}
void CheckFunctions : : ignoredReturnValueError ( const Token * tok , const std : : string & function )
{
reportError ( tok , Severity : : warning , " ignoredReturnValue " ,
2021-02-24 22:00:06 +01:00
" $symbol: " + function + " \n Return value of function $symbol() is not used. " , CWE252 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
}
2020-11-05 13:35:52 +01:00
void CheckFunctions : : ignoredReturnErrorCode ( const Token * tok , const std : : string & function )
{
reportError ( tok , Severity : : style , " ignoredReturnErrorCode " ,
2021-02-24 22:00:06 +01:00
" $symbol: " + function + " \n Error code from the return value of function $symbol() is not used. " , CWE252 , Certainty : : normal ) ;
2020-11-05 13:35:52 +01:00
}
2015-11-22 13:48:55 +01:00
2021-07-04 11:27:57 +02:00
//---------------------------------------------------------------------------
// Check for ignored return values.
//---------------------------------------------------------------------------
2021-07-10 08:59:01 +02:00
static const Token * checkMissingReturnScope ( const Token * tok , const Library & library ) ;
2021-07-04 11:27:57 +02:00
void CheckFunctions : : checkMissingReturn ( )
{
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
const Function * function = scope - > function ;
if ( ! function | | ! function - > hasBody ( ) )
continue ;
2021-07-16 18:50:16 +02:00
if ( function - > name ( ) = = " main " & & ! ( mSettings - > standards . c < Standards : : C99 & & mTokenizer - > isC ( ) ) )
continue ;
2021-07-04 11:27:57 +02:00
if ( function - > type ! = Function : : Type : : eFunction & & function - > type ! = Function : : Type : : eOperatorEqual )
continue ;
2021-07-08 13:50:26 +02:00
if ( Token : : Match ( function - > retDef , " %name% ( " ) & & function - > retDef - > isUpperCaseName ( ) )
continue ;
if ( Function : : returnsVoid ( function , true ) )
2021-07-04 11:27:57 +02:00
continue ;
2021-07-10 08:59:01 +02:00
const Token * errorToken = checkMissingReturnScope ( scope - > bodyEnd , mSettings - > library ) ;
2021-09-01 19:49:53 +02:00
if ( errorToken )
2021-07-04 11:27:57 +02:00
missingReturnError ( errorToken ) ;
}
}
2021-07-12 15:31:21 +02:00
static bool isForwardJump ( const Token * gotoToken )
{
if ( ! Token : : Match ( gotoToken , " goto %name% ; " ) )
return false ;
for ( const Token * prev = gotoToken ; gotoToken ; gotoToken = gotoToken - > previous ( ) ) {
if ( Token : : Match ( prev , " %name% : " ) & & prev - > str ( ) = = gotoToken - > next ( ) - > str ( ) )
return true ;
if ( prev - > str ( ) = = " { " & & prev - > scope ( ) - > type = = Scope : : eFunction )
return false ;
}
return false ;
}
2021-07-10 08:59:01 +02:00
static const Token * checkMissingReturnScope ( const Token * tok , const Library & library )
2021-07-04 11:27:57 +02:00
{
2021-07-10 08:36:54 +02:00
const Token * lastStatement = nullptr ;
while ( ( tok = tok - > previous ( ) ) ! = nullptr ) {
2021-09-06 21:20:19 +02:00
if ( tok - > str ( ) = = " ) " )
tok = tok - > link ( ) ;
2021-07-04 11:27:57 +02:00
if ( tok - > str ( ) = = " { " )
2021-07-10 08:36:54 +02:00
return lastStatement ? lastStatement : tok - > next ( ) ;
2021-07-04 11:27:57 +02:00
if ( tok - > str ( ) = = " } " ) {
2021-07-04 19:57:43 +02:00
for ( const Token * prev = tok - > link ( ) - > previous ( ) ; prev & & prev - > scope ( ) = = tok - > scope ( ) & & ! Token : : Match ( prev , " [;{}] " ) ; prev = prev - > previous ( ) ) {
if ( prev - > isKeyword ( ) & & Token : : Match ( prev , " return|throw " ) )
return nullptr ;
2021-07-12 15:31:21 +02:00
if ( prev - > str ( ) = = " goto " & & ! isForwardJump ( prev ) )
return nullptr ;
2021-07-04 19:57:43 +02:00
}
2021-07-04 11:27:57 +02:00
if ( tok - > scope ( ) - > type = = Scope : : ScopeType : : eSwitch ) {
2021-07-10 08:36:54 +02:00
// find reachable break / !default
2021-07-04 11:27:57 +02:00
bool hasDefault = false ;
2021-07-10 08:36:54 +02:00
bool reachable = false ;
2021-10-31 14:47:52 +01:00
for ( const Token * switchToken = tok - > link ( ) - > next ( ) ; switchToken ! = tok ; switchToken = switchToken - > next ( ) ) {
if ( reachable & & Token : : simpleMatch ( switchToken , " break ; " ) ) {
if ( Token : : simpleMatch ( switchToken - > previous ( ) , " } " ) & & ! checkMissingReturnScope ( switchToken - > previous ( ) , library ) )
reachable = false ;
else
return switchToken ;
}
2021-07-10 08:36:54 +02:00
if ( switchToken - > isKeyword ( ) & & Token : : Match ( switchToken , " return|throw " ) )
reachable = false ;
2021-07-10 08:59:01 +02:00
if ( Token : : Match ( switchToken , " %name% ( " ) & & library . isnoreturn ( switchToken ) )
reachable = false ;
2021-07-10 08:36:54 +02:00
if ( Token : : Match ( switchToken , " case|default " ) )
reachable = true ;
2021-07-04 11:27:57 +02:00
if ( Token : : simpleMatch ( switchToken , " default : " ) )
hasDefault = true ;
2021-10-31 14:47:52 +01:00
else if ( switchToken - > str ( ) = = " { " && (switchToken->scope()->isLoopScope() || switchToken->scope()->type == Scope::ScopeType::eSwitch))
2021-07-04 11:27:57 +02:00
switchToken = switchToken - > link ( ) ;
}
if ( ! hasDefault )
return tok - > link ( ) ;
} else if ( tok - > scope ( ) - > type = = Scope : : ScopeType : : eIf ) {
2021-07-12 17:53:32 +02:00
const Token * condition = tok - > scope ( ) - > classDef - > next ( ) - > astOperand2 ( ) ;
if ( condition & & condition - > hasKnownIntValue ( ) & & condition - > getKnownIntValue ( ) = = 1 )
return checkMissingReturnScope ( tok , library ) ;
2021-07-04 11:27:57 +02:00
return tok ;
} else if ( tok - > scope ( ) - > type = = Scope : : ScopeType : : eElse ) {
2021-07-10 08:59:01 +02:00
const Token * errorToken = checkMissingReturnScope ( tok , library ) ;
2021-07-04 11:27:57 +02:00
if ( errorToken )
return errorToken ;
tok = tok - > link ( ) ;
if ( Token : : simpleMatch ( tok - > tokAt ( - 2 ) , " } else { " ) )
2021-07-10 08:59:01 +02:00
return checkMissingReturnScope ( tok - > tokAt ( - 2 ) , library ) ;
2021-07-04 11:27:57 +02:00
return tok ;
}
// FIXME
return nullptr ;
}
if ( tok - > isKeyword ( ) & & Token : : Match ( tok , " return|throw " ) )
return nullptr ;
2021-07-12 15:31:21 +02:00
if ( tok - > str ( ) = = " goto " & & ! isForwardJump ( tok ) )
return nullptr ;
2021-07-18 16:00:42 +02:00
if ( Token : : Match ( tok , " %name% ( " ) & & ! library . isnotnoreturn ( tok ) ) {
const Token * start = tok ;
while ( Token : : Match ( start - > tokAt ( - 2 ) , " %name% :: %name% " ) )
start = start - > tokAt ( - 2 ) ;
if ( Token : : Match ( start - > previous ( ) , " [;{}] %name% ::|( " ) )
return nullptr ;
}
2021-07-10 08:49:48 +02:00
if ( Token : : Match ( tok , " [;{}] %name% : " ) )
return tok ;
2021-07-10 08:36:54 +02:00
if ( Token : : Match ( tok , " ; !!} " ) & & ! lastStatement )
lastStatement = tok - > next ( ) ;
2021-07-04 11:27:57 +02:00
}
return nullptr ;
}
2021-09-01 19:49:53 +02:00
void CheckFunctions : : missingReturnError ( const Token * tok )
2021-07-04 11:27:57 +02:00
{
reportError ( tok , Severity : : error , " missingReturn " ,
2021-09-01 19:49:53 +02:00
" Found a exit path from function with non-void return type that has missing return statement " , CWE758 , Certainty : : normal ) ;
2021-07-04 11:27:57 +02:00
}
2015-11-22 13:48:55 +01:00
//---------------------------------------------------------------------------
// Detect passing wrong values to <cmath> functions like atan(0, x);
//---------------------------------------------------------------------------
void CheckFunctions : : checkMathFunctions ( )
{
2021-02-24 22:00:06 +01:00
const bool styleC99 = mSettings - > severity . isEnabled ( Severity : : style ) & & mSettings - > standards . c ! = Standards : : C89 & & mSettings - > standards . cpp ! = Standards : : CPP03 ;
const bool printWarnings = mSettings - > severity . isEnabled ( Severity : : warning ) ;
2015-11-22 13:48:55 +01:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2015-11-22 13:48:55 +01:00
if ( tok - > varId ( ) )
continue ;
2015-12-24 11:07:47 +01:00
if ( printWarnings & & Token : : Match ( tok , " %name% ( !!) " ) ) {
2015-11-22 13:48:55 +01:00
if ( tok - > strAt ( - 1 ) ! = " . "
2018-08-03 12:14:39 +02:00
& & Token : : Match ( tok , " log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% ) " ) ) {
2015-11-22 13:48:55 +01:00
const std : : string & number = tok - > strAt ( 2 ) ;
2018-08-03 12:14:39 +02:00
if ( ( MathLib : : isInt ( number ) & & MathLib : : toLongNumber ( number ) < = 0 ) | |
( MathLib : : isFloat ( number ) & & MathLib : : toDoubleNumber ( number ) < = 0. ) )
mathfunctionCallWarning ( tok ) ;
2018-08-05 10:48:28 +02:00
} else if ( Token : : Match ( tok , " log1p|log1pf|log1pl ( %num% ) " )) {
2018-08-03 12:14:39 +02:00
const std : : string & number = tok - > strAt ( 2 ) ;
if ( ( MathLib : : isInt ( number ) & & MathLib : : toLongNumber ( number ) < = - 1 ) | |
( MathLib : : isFloat ( number ) & & MathLib : : toDoubleNumber ( number ) < = - 1. ) )
mathfunctionCallWarning ( tok ) ;
2015-11-22 13:48:55 +01:00
}
// atan2 ( x , y): x and y can not be zero, because this is mathematically not defined
else if ( Token : : Match ( tok , " atan2|atan2f|atan2l ( %num% , %num% ) " )) {
if ( MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & & MathLib : : isNullValue ( tok - > strAt ( 4 ) ) )
mathfunctionCallWarning ( tok , 2 ) ;
}
// fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined).
2015-12-24 14:40:48 +01:00
else if ( Token : : Match ( tok , " fmod|fmodf|fmodl ( " ) ) {
2015-11-22 13:48:55 +01:00
const Token * nextArg = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
2020-02-09 11:16:08 +01:00
if ( nextArg & & MathLib : : isNullValue ( nextArg - > str ( ) ) )
2015-11-22 13:48:55 +01:00
mathfunctionCallWarning ( tok , 2 ) ;
}
// pow ( x , y) If x is zero, and y is negative --> division by zero
else if ( Token : : Match ( tok , " pow|powf|powl ( %num% , %num% ) " )) {
if ( MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & & MathLib : : isNegative ( tok - > strAt ( 4 ) ) )
mathfunctionCallWarning ( tok , 2 ) ;
}
}
if ( styleC99 ) {
if ( Token : : Match ( tok , " %num% - erf ( " ) & & Tokenizer : : isOneNumber ( tok - > str ( ) ) & & tok - > next ( ) - > astOperand2 ( ) = = tok - > tokAt ( 3 ) ) {
mathfunctionCallWarning ( tok , " 1 - erf(x) " , " erfc(x) " ) ;
} else if ( Token : : simpleMatch ( tok , " exp ( " ) & & Token : : Match ( tok - > linkAt ( 1 ) , " ) - %num% " ) & & Tokenizer : : isOneNumber ( tok - > linkAt ( 1 ) - > strAt ( 2 ) ) & & tok - > linkAt ( 1 ) - > next ( ) - > astOperand1 ( ) = = tok - > next ( ) ) {
mathfunctionCallWarning ( tok , " exp(x) - 1 " , " expm1(x) " ) ;
} else if ( Token : : simpleMatch ( tok , " log ( " ) & & tok - > next ( ) - > astOperand2 ( ) ) {
const Token * plus = tok - > next ( ) - > astOperand2 ( ) ;
if ( plus - > str ( ) = = " + " & & ( ( plus - > astOperand1 ( ) & & Tokenizer : : isOneNumber ( plus - > astOperand1 ( ) - > str ( ) ) ) | | ( plus - > astOperand2 ( ) & & Tokenizer : : isOneNumber ( plus - > astOperand2 ( ) - > str ( ) ) ) ) )
mathfunctionCallWarning ( tok , " log(1 + x) " , " log1p(x) " ) ;
}
}
}
}
}
2019-07-16 08:49:02 +02:00
void CheckFunctions : : mathfunctionCallWarning ( const Token * tok , const nonneg int numParam )
2015-11-22 13:48:55 +01:00
{
if ( tok ) {
if ( numParam = = 1 )
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " $symbol: " + tok - > str ( ) + " \n Passing value " + tok - > strAt ( 2 ) + " to $symbol() leads to implementation-defined result. " , CWE758 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
else if ( numParam = = 2 )
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " $symbol: " + tok - > str ( ) + " \n Passing values " + tok - > strAt ( 2 ) + " and " + tok - > strAt ( 4 ) + " to $symbol() leads to implementation-defined result. " , CWE758 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
} else
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " Passing value '#' to #() leads to implementation-defined result. " , CWE758 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
}
void CheckFunctions : : mathfunctionCallWarning ( const Token * tok , const std : : string & oldexp , const std : : string & newexp )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : style , " unpreciseMathCall " , " Expression ' " + oldexp + " ' can be replaced by ' " + newexp + " ' to avoid loss of precision. " , CWE758 , Certainty : : normal ) ;
2015-11-22 13:48:55 +01:00
}
2017-04-23 07:53:41 +02:00
//---------------------------------------------------------------------------
// memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted
//---------------------------------------------------------------------------
void CheckFunctions : : memsetZeroBytes ( )
{
2017-04-23 15:34:45 +02:00
// FIXME:
// Replace this with library configuration.
// For instance:
// <arg nr="3">
// <warn knownIntValue="0" severity="warning" msg="..."/>
// </arg>
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2017-04-23 07:53:41 +02:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-04-23 07:53:41 +02:00
if ( Token : : Match ( tok , " memset|wmemset ( " ) & & ( numberOfArguments ( tok ) = = 3 ) ) {
2017-04-30 18:58:51 +02:00
const std : : vector < const Token * > & arguments = getArguments ( tok ) ;
2017-06-05 18:49:20 +02:00
if ( WRONG_DATA ( arguments . size ( ) ! = 3U , tok ) )
2017-04-30 18:58:51 +02:00
continue ;
const Token * lastParamTok = arguments [ 2 ] ;
2020-02-19 07:51:39 +01:00
if ( MathLib : : isNullValue ( lastParamTok - > str ( ) ) )
2017-04-23 07:53:41 +02:00
memsetZeroBytesError ( tok ) ;
}
}
}
}
void CheckFunctions : : memsetZeroBytesError ( const Token * tok )
{
const std : : string summary ( " memset() called to fill 0 bytes. " ) ;
const std : : string verbose ( summary + " The second and third arguments might be inverted. "
" The function memset ( void * ptr, int value, size_t num ) sets the "
" first num bytes of the block of memory pointed by ptr to the specified value. " ) ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " memsetZeroBytes " , summary + " \n " + verbose , CWE687 , Certainty : : normal ) ;
2017-04-23 07:53:41 +02:00
}
void CheckFunctions : : memsetInvalid2ndParam ( )
{
2017-04-23 15:34:45 +02:00
// FIXME:
// Replace this with library configuration.
// For instance:
// <arg nr="2">
// <not-float/>
// <warn possibleIntValue=":-129,256:" severity="warning" msg="..."/>
// </arg>
2021-02-24 22:00:06 +01:00
const bool printPortability = mSettings - > severity . isEnabled ( Severity : : portability ) ;
const bool printWarning = mSettings - > severity . isEnabled ( Severity : : warning ) ;
2017-04-23 07:53:41 +02:00
if ( ! printWarning & & ! printPortability )
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-23 07:49:38 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok & & ( tok ! = scope - > bodyEnd ) ; tok = tok - > next ( ) ) {
2017-04-23 07:53:41 +02:00
if ( ! Token : : simpleMatch ( tok , " memset ( " ) )
continue ;
const std : : vector < const Token * > args = getArguments ( tok ) ;
if ( args . size ( ) ! = 3 )
continue ;
// Second parameter is zero literal, i.e. 0.0f
const Token * const secondParamTok = args [ 1 ] ;
if ( Token : : Match ( secondParamTok , " %num% , " ) & & MathLib : : isNullValue ( secondParamTok - > str ( ) ) )
continue ;
// Check if second parameter is a float variable or a float literal != 0.0f
if ( printPortability & & astIsFloat ( secondParamTok , false ) ) {
memsetFloatError ( secondParamTok , secondParamTok - > expressionString ( ) ) ;
}
if ( printWarning & & secondParamTok - > isNumber ( ) ) { // Check if the second parameter is a literal and is out of range
const long long int value = MathLib : : toLongNumber ( secondParamTok - > str ( ) ) ;
2018-06-16 16:10:28 +02:00
const long long sCharMin = mSettings - > signedCharMin ( ) ;
const long long uCharMax = mSettings - > unsignedCharMax ( ) ;
2017-10-18 17:30:47 +02:00
if ( value < sCharMin | | value > uCharMax )
2017-04-23 07:53:41 +02:00
memsetValueOutOfRangeError ( secondParamTok , secondParamTok - > str ( ) ) ;
}
}
}
}
void CheckFunctions : : memsetFloatError ( const Token * tok , const std : : string & var_value )
{
const std : : string message ( " The 2nd memset() argument ' " + var_value +
" ' is a float, its representation is implementation defined. " ) ;
const std : : string verbose ( message + " memset() is used to set each byte of a block of memory to a specific value and "
" the actual representation of a floating-point value is implementation defined. " ) ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : portability , " memsetFloat " , message + " \n " + verbose , CWE688 , Certainty : : normal ) ;
2017-04-23 07:53:41 +02:00
}
void CheckFunctions : : memsetValueOutOfRangeError ( const Token * tok , const std : : string & value )
{
const std : : string message ( " The 2nd memset() argument ' " + value + " ' doesn't fit into an 'unsigned char'. " ) ;
const std : : string verbose ( message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value. " ) ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " memsetValueOutOfRange " , message + " \n " + verbose , CWE686 , Certainty : : normal ) ;
2017-04-23 07:53:41 +02:00
}
//---------------------------------------------------------------------------
// --check-library => warn for unconfigured functions
//---------------------------------------------------------------------------
2015-11-22 13:48:55 +01:00
void CheckFunctions : : checkLibraryMatchFunctions ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > checkLibrary | | ! mSettings - > severity . isEnabled ( Severity : : information ) )
2015-11-22 13:48:55 +01:00
return ;
2019-11-20 15:37:09 +01:00
bool insideNew = false ;
2018-06-16 16:10:28 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2017-10-06 11:27:01 +02:00
if ( ! tok - > scope ( ) | | ! tok - > scope ( ) - > isExecutable ( ) )
continue ;
if ( tok - > str ( ) = = " new " )
2019-11-20 15:37:09 +01:00
insideNew = true ;
2017-10-06 11:27:01 +02:00
else if ( tok - > str ( ) = = " ; " )
2019-11-20 15:37:09 +01:00
insideNew = false ;
else if ( insideNew )
2017-10-06 11:27:01 +02:00
continue ;
2022-07-13 21:08:43 +02:00
if ( tok - > isKeyword ( ) | | ! Token : : Match ( tok , " %name% ( " ) )
2017-10-06 11:27:01 +02:00
continue ;
2022-07-13 21:08:43 +02:00
if ( tok - > varId ( ) ! = 0 | | tok - > type ( ) | | tok - > isStandardType ( ) )
2017-10-06 11:27:01 +02:00
continue ;
if ( tok - > linkAt ( 1 ) - > strAt ( 1 ) = = " ( " )
continue ;
2020-06-18 14:49:19 +02:00
if ( tok - > function ( ) )
continue ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > library . isNotLibraryFunction ( tok ) )
2017-10-06 11:27:01 +02:00
continue ;
2018-06-16 16:10:28 +02:00
const std : : string & functionName = mSettings - > library . getFunctionName ( tok ) ;
2022-07-12 17:39:01 +02:00
if ( functionName . empty ( ) )
continue ;
if ( mSettings - > library . functions . find ( functionName ) ! = mSettings - > library . functions . end ( ) )
continue ;
const Token * start = tok ;
while ( Token : : Match ( start - > tokAt ( - 2 ) , " %name% :: " ) )
start = start - > tokAt ( - 2 ) ;
if ( mSettings - > library . detectContainerOrIterator ( start ) )
2017-10-10 10:18:03 +02:00
continue ;
2017-10-06 11:27:01 +02:00
reportError ( tok ,
Severity : : information ,
" checkLibraryFunction " ,
2017-10-10 10:18:03 +02:00
" --check-library: There is no matching configuration for function " + functionName + " () " ) ;
2015-11-22 13:48:55 +01:00
}
2015-11-22 16:56:44 +01:00
}
2021-07-06 08:07:46 +02:00
// Check for problems to compiler apply (Named) Return Value Optimization for local variable
// Technically we have different guarantees between standard versions
// details: https://en.cppreference.com/w/cpp/language/copy_elision
void CheckFunctions : : returnLocalStdMove ( )
{
2021-07-06 12:18:03 +02:00
if ( ! mTokenizer - > isCPP ( ) | | mSettings - > standards . cpp < Standards : : CPP11 )
2021-07-06 08:07:46 +02:00
return ;
if ( ! mSettings - > severity . isEnabled ( Severity : : performance ) )
return ;
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
// Expect return by-value
if ( Function : : returnsReference ( scope - > function , true ) )
continue ;
const auto rets = Function : : findReturns ( scope - > function ) ;
for ( const Token * ret : rets ) {
if ( ! Token : : simpleMatch ( ret - > tokAt ( - 3 ) , " std :: move ( " ) )
continue ;
const Token * retval = ret - > astOperand2 ( ) ;
// NRVO
if ( retval - > variable ( ) & & retval - > variable ( ) - > isLocal ( ) & & ! retval - > variable ( ) - > isVolatile ( ) )
copyElisionError ( retval ) ;
// RVO
2022-06-30 13:50:31 +02:00
if ( Token : : Match ( retval , " (|{ " ) & & ! retval - > isCast ( ) & & ! ( retval - > valueType ( ) & & retval - > valueType ( ) - > reference ! = Reference : : None ) )
2021-07-06 08:07:46 +02:00
copyElisionError ( retval ) ;
}
}
}
void CheckFunctions : : copyElisionError ( const Token * tok )
{
reportError ( tok ,
Severity : : performance ,
" returnStdMoveLocal " ,
" Using std::move for returning object by-value from function will affect copy elision optimization. "
" More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local " ) ;
}