Moved a few checks from CheckOther to CheckFunctions

This commit is contained in:
PKEuS 2015-11-22 13:48:55 +01:00
parent 9864f340f5
commit f516de8633
6 changed files with 819 additions and 840 deletions

View File

@ -31,7 +31,7 @@ namespace {
CheckFunctions instance;
}
void CheckFunctions::check()
void CheckFunctions::checkProhibitedFunctions()
{
const bool checkAlloca = (_settings->standards.c >= Standards::C99 && _tokenizer->isC()) || _settings->standards.cpp >= Standards::CPP11;
@ -69,3 +69,220 @@ void CheckFunctions::check()
}
}
}
//---------------------------------------------------------------------------
// strtol(str, 0, radix) <- radix must be 0 or 2-36
//---------------------------------------------------------------------------
void CheckFunctions::invalidFunctionUsage()
{
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (!tok->isName() || !Token::Match(tok, "%name% ( !!)"))
continue;
const Token * const functionToken = tok;
int argnr = 1;
const Token *argtok = tok->tokAt(2);
do {
if (Token::Match(argtok, "%num% [,)]")) {
if (MathLib::isInt(argtok->str()) &&
!_settings->library.isargvalid(functionToken, argnr, MathLib::toLongNumber(argtok->str())))
invalidFunctionArgError(argtok, functionToken->str(), argnr, _settings->library.validarg(functionToken, argnr));
} else {
const Token *top = argtok;
while (top->astParent() && top->astParent()->str() != "," && top->astParent() != tok->next())
top = top->astParent();
if (top->isComparisonOp() || Token::Match(top, "%oror%|&&")) {
if (_settings->library.isboolargbad(functionToken, argnr))
invalidFunctionArgBoolError(top, functionToken->str(), argnr);
// Are the values 0 and 1 valid?
else if (!_settings->library.isargvalid(functionToken, argnr, 0))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken, argnr));
else if (!_settings->library.isargvalid(functionToken, argnr, 1))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken, argnr));
}
}
argnr++;
argtok = argtok->nextArgument();
} while (argtok && argtok->str() != ")");
}
}
}
void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr;
if (!tok)
;
else if (tok->isNumber())
errmsg << ". The value is " << tok->str() << " but the valid values are '" << validstr << "'.";
else if (tok->isComparisonOp())
errmsg << ". The value is 0 or 1 (comparison result) but the valid values are '" << validstr << "'.";
reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str());
}
void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr << ". A non-boolean value is required.";
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str());
}
//---------------------------------------------------------------------------
// Check for ignored return values.
//---------------------------------------------------------------------------
void CheckFunctions::checkIgnoredReturnValue()
{
if (!_settings->isEnabled("warning"))
return;
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->varId() || !Token::Match(tok, "%name% (") || tok->strAt(-1) == ".")
continue;
if (!tok->scope()->isExecutable()) {
tok = tok->scope()->classEnd;
continue;
}
const Token* parent = tok;
while (parent->astParent() && parent->astParent()->str() == "::")
parent = parent->astParent();
if (tok->next()->astOperand1() != parent)
continue;
if (!tok->next()->astParent() && (!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && _settings->library.isUseRetVal(tok))
ignoredReturnValueError(tok, tok->str());
}
}
}
void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function)
{
reportError(tok, Severity::warning, "ignoredReturnValue",
"Return value of function " + function + "() is not used.", 0U, false);
}
//---------------------------------------------------------------------------
// Detect passing wrong values to <cmath> functions like atan(0, x);
//---------------------------------------------------------------------------
void CheckFunctions::checkMathFunctions()
{
const bool styleC99 = _settings->isEnabled("style") && _settings->standards.c != Standards::C89 && _settings->standards.cpp != Standards::CPP03;
const bool printWarnings = _settings->isEnabled("warning");
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->varId())
continue;
if (printWarnings) {
if (tok->strAt(-1) != "."
&& Token::Match(tok, "log|logf|logl|log10|log10f|log10l ( %num% )")) {
const std::string& number = tok->strAt(2);
bool isNegative = MathLib::isNegative(number);
bool isInt = MathLib::isInt(number);
bool isFloat = MathLib::isFloat(number);
if (isNegative && isInt && MathLib::toLongNumber(number) <= 0) {
mathfunctionCallWarning(tok); // case log(-2)
} else if (isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) {
mathfunctionCallWarning(tok); // case log(-2.0)
} else if (!isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) {
mathfunctionCallWarning(tok); // case log(0.0)
} else if (!isNegative && isInt && MathLib::toLongNumber(number) <= 0) {
mathfunctionCallWarning(tok); // case log(0)
}
}
// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
else if (Token::Match(tok, "acos|acosl|acosf|asin|asinf|asinl ( %num% )")) {
if (std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0)
mathfunctionCallWarning(tok);
}
// sqrt( x ): if x is negative the result is undefined
else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )")) {
if (MathLib::isNegative(tok->strAt(2)))
mathfunctionCallWarning(tok);
}
// 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).
else if (Token::Match(tok, "fmod|fmodf|fmodl ( %any%")) {
const Token* nextArg = tok->tokAt(2)->nextArgument();
if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg->str()))
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)");
}
}
}
}
}
void CheckFunctions::mathfunctionCallWarning(const Token *tok, const unsigned int numParam)
{
if (tok) {
if (numParam == 1)
reportError(tok, Severity::warning, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to implementation-defined result.");
else if (numParam == 2)
reportError(tok, Severity::warning, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to implementation-defined result.");
} else
reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.");
}
void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp)
{
reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.");
}
void CheckFunctions::checkLibraryMatchFunctions()
{
if (!_settings->checkLibrary || !_settings->isEnabled("information"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "%name% (") &&
!Token::Match(tok, "for|if|while|switch|sizeof|catch|asm|return") &&
!tok->function() &&
!tok->varId() &&
!tok->type() &&
!tok->isStandardType() &&
tok->linkAt(1)->strAt(1) != "(" &&
tok->astParent() == tok->next() &&
_settings->library.isNotLibraryFunction(tok)) {
reportError(tok,
Severity::information,
"checkLibraryFunction",
"--check-library: There is no matching configuration for function " + tok->str() + "()");
}
}
}

View File

@ -31,7 +31,7 @@
/// @{
/**
* @brief Check for functions which should not be used
* @brief Check for bad function usage
*/
class CPPCHECKLIB CheckFunctions : public Check {
@ -45,21 +45,67 @@ public:
: Check(myName(), tokenizer, settings, errorLogger) {
}
/** @brief Run checks against the normal token list */
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
CheckFunctions checkFunctions(tokenizer, settings, errorLogger);
// Checks
checkFunctions.checkIgnoredReturnValue();
// --check-library : functions with nonmatching configuration
checkFunctions.checkLibraryMatchFunctions();
}
/** @brief Run checks against the simplified token list */
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
CheckFunctions checkFunctions(tokenizer, settings, errorLogger);
checkFunctions.check();
checkFunctions.checkProhibitedFunctions();
checkFunctions.invalidFunctionUsage();
checkFunctions.checkMathFunctions();
}
/** Check for functions that should not be used */
void check();
void checkProhibitedFunctions();
/**
* @brief Invalid function usage (invalid input value / overlapping data)
*
* %Check that given function parameters are valid according to the standard
* - wrong radix given for strtol/strtoul
* - overlapping data when using sprintf/snprintf
* - wrong input value according to library
*/
void invalidFunctionUsage();
/** @brief %Check for ignored return values. */
void checkIgnoredReturnValue();
/** @brief %Check for parameters given to math function that do not make sense*/
void checkMathFunctions();
/** @brief --check-library: warn for unconfigured function calls */
void checkLibraryMatchFunctions();
private:
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr);
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr);
void ignoredReturnValueError(const Token* tok, const std::string& function);
void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1);
void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp);
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
CheckFunctions c(0, settings, errorLogger);
for (std::map<std::string, Library::WarnInfo>::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) {
c.reportError(0, Severity::style, i->first+"Called", i->second.message);
}
c.invalidFunctionArgError(0, "func_name", 1, "1-4");
c.invalidFunctionArgBoolError(0, "func_name", 1);
c.ignoredReturnValueError(0, "malloc");
c.mathfunctionCallWarning(0);
c.mathfunctionCallWarning(0, "1 - erf(x)", "erfc(x)");
}
static std::string myName() {
@ -67,7 +113,10 @@ private:
}
std::string classInfo() const {
return "Warn if a function is called whose usage is discouraged\n";
return "Check function usage:\n"
"- return value of certain functions not used\n"
"- invalid input values for functions\n"
"- Warn if a function is called whose usage is discouraged\n";
}
};
/// @}

View File

@ -945,68 +945,6 @@ void CheckOther::suspiciousEqualityComparisonError(const Token* tok)
}
//---------------------------------------------------------------------------
// strtol(str, 0, radix) <- radix must be 0 or 2-36
//---------------------------------------------------------------------------
void CheckOther::invalidFunctionUsage()
{
const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (!tok->isName() || !Token::Match(tok, "%name% ( !!)"))
continue;
const Token * const functionToken = tok;
int argnr = 1;
const Token *argtok = tok->tokAt(2);
do {
if (Token::Match(argtok,"%num% [,)]")) {
if (MathLib::isInt(argtok->str()) &&
!_settings->library.isargvalid(functionToken, argnr, MathLib::toLongNumber(argtok->str())))
invalidFunctionArgError(argtok,functionToken->str(),argnr,_settings->library.validarg(functionToken,argnr));
} else {
const Token *top = argtok;
while (top->astParent() && top->astParent()->str() != "," && top->astParent() != tok->next())
top = top->astParent();
if (top->isComparisonOp() || Token::Match(top, "%oror%|&&")) {
if (_settings->library.isboolargbad(functionToken, argnr))
invalidFunctionArgBoolError(top, functionToken->str(), argnr);
// Are the values 0 and 1 valid?
else if (!_settings->library.isargvalid(functionToken, argnr, 0))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken,argnr));
else if (!_settings->library.isargvalid(functionToken, argnr, 1))
invalidFunctionArgError(top, functionToken->str(), argnr, _settings->library.validarg(functionToken,argnr));
}
}
argnr++;
argtok = argtok->nextArgument();
} while (argtok && argtok->str() != ")");
}
}
}
void CheckOther::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr;
if (!tok)
;
else if (tok->isNumber())
errmsg << ". The value is " << tok->str() << " but the valid values are '" << validstr << "'.";
else if (tok->isComparisonOp())
errmsg << ". The value is 0 or 1 (comparison result) but the valid values are '" << validstr << "'.";
reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str());
}
void CheckOther::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr)
{
std::ostringstream errmsg;
errmsg << "Invalid " << functionName << "() argument nr " << argnr << ". A non-boolean value is required.";
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str());
}
//---------------------------------------------------------------------------
// Find consecutive return, break, continue, goto or throw statements. e.g.:
// break; break;
@ -1709,98 +1647,6 @@ void CheckOther::nanInArithmeticExpressionError(const Token *tok)
"Although nothing bad really happens, it is suspicious.");
}
//---------------------------------------------------------------------------
// Detect passing wrong values to <cmath> functions like atan(0, x);
//---------------------------------------------------------------------------
void CheckOther::checkMathFunctions()
{
const bool styleC99 = _settings->isEnabled("style") && _settings->standards.c != Standards::C89 && _settings->standards.cpp != Standards::CPP03;
const bool printWarnings = _settings->isEnabled("warning");
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->varId())
continue;
if (printWarnings) {
if (tok->strAt(-1) != "."
&& Token::Match(tok, "log|logf|logl|log10|log10f|log10l ( %num% )")) {
const std::string& number = tok->strAt(2);
bool isNegative = MathLib::isNegative(number);
bool isInt = MathLib::isInt(number);
bool isFloat = MathLib::isFloat(number);
if (isNegative && isInt && MathLib::toLongNumber(number) <= 0) {
mathfunctionCallWarning(tok); // case log(-2)
} else if (isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) {
mathfunctionCallWarning(tok); // case log(-2.0)
} else if (!isNegative && isFloat && MathLib::toDoubleNumber(number) <= 0.) {
mathfunctionCallWarning(tok); // case log(0.0)
} else if (!isNegative && isInt && MathLib::toLongNumber(number) <= 0) {
mathfunctionCallWarning(tok); // case log(0)
}
}
// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
else if (Token::Match(tok, "acos|acosl|acosf|asin|asinf|asinl ( %num% )")) {
if (std::fabs(MathLib::toDoubleNumber(tok->strAt(2))) > 1.0)
mathfunctionCallWarning(tok);
}
// sqrt( x ): if x is negative the result is undefined
else if (Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )")) {
if (MathLib::isNegative(tok->strAt(2)))
mathfunctionCallWarning(tok);
}
// 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).
else if (Token::Match(tok, "fmod|fmodf|fmodl ( %any%")) {
const Token* nextArg = tok->tokAt(2)->nextArgument();
if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg->str()))
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)");
}
}
}
}
}
void CheckOther::mathfunctionCallWarning(const Token *tok, const unsigned int numParam)
{
if (tok) {
if (numParam == 1)
reportError(tok, Severity::warning, "wrongmathcall", "Passing value " + tok->strAt(2) + " to " + tok->str() + "() leads to implementation-defined result.");
else if (numParam == 2)
reportError(tok, Severity::warning, "wrongmathcall", "Passing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to " + tok->str() + "() leads to implementation-defined result.");
} else
reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.");
}
void CheckOther::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp)
{
reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.");
}
//---------------------------------------------------------------------------
// Creating instance of clases which are destroyed immediately
//---------------------------------------------------------------------------
@ -2520,45 +2366,6 @@ void CheckOther::varFuncNullUBError(const Token *tok)
"}");
}
//---------------------------------------------------------------------------
// Check for ignored return values.
//---------------------------------------------------------------------------
void CheckOther::checkIgnoredReturnValue()
{
if (!_settings->isEnabled("warning"))
return;
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t functions = symbolDatabase->functionScopes.size();
for (std::size_t i = 0; i < functions; ++i) {
const Scope * scope = symbolDatabase->functionScopes[i];
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (tok->varId() || !Token::Match(tok, "%name% (") || tok->strAt(-1) == ".")
continue;
if (!tok->scope()->isExecutable()) {
tok = tok->scope()->classEnd;
continue;
}
const Token* parent = tok;
while (parent->astParent() && parent->astParent()->str() == "::")
parent = parent->astParent();
if (tok->next()->astOperand1() != parent)
continue;
if (!tok->next()->astParent() && (!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && _settings->library.isUseRetVal(tok))
ignoredReturnValueError(tok, tok->str());
}
}
}
void CheckOther::ignoredReturnValueError(const Token* tok, const std::string& function)
{
reportError(tok, Severity::warning, "ignoredReturnValue",
"Return value of function " + function + "() is not used.", 0U, false);
}
void CheckOther::checkRedundantPointerOp()
{
if (!_settings->isEnabled("style"))
@ -2596,29 +2403,6 @@ void CheckOther::redundantPointerOpError(const Token* tok, const std::string &va
"Redundant pointer operation on " + varname + " - it's already a pointer.", 0U, inconclusive);
}
void CheckOther::checkLibraryMatchFunctions()
{
if (!_settings->checkLibrary || !_settings->isEnabled("information"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "%name% (") &&
!Token::Match(tok, "for|if|while|switch|sizeof|catch|asm|return") &&
!tok->function() &&
!tok->varId() &&
!tok->type() &&
!tok->isStandardType() &&
tok->linkAt(1)->strAt(1) != "(" &&
tok->astParent() == tok->next() &&
_settings->library.isNotLibraryFunction(tok)) {
reportError(tok,
Severity::information,
"checkLibraryFunction",
"--check-library: There is no matching configuration for function " + tok->str() + "()");
}
}
}
void CheckOther::checkInterlockedDecrement()
{
if (!_settings->isWindowsPlatform()) {

View File

@ -66,14 +66,10 @@ public:
checkOther.checkVarFuncNullUB();
checkOther.checkNanInArithmeticExpression();
checkOther.checkCommaSeparatedReturn();
checkOther.checkIgnoredReturnValue();
checkOther.checkRedundantPointerOp();
checkOther.checkZeroDivision();
checkOther.checkInterlockedDecrement();
checkOther.checkUnusedLabel();
// --check-library : functions with nonmatching configuration
checkOther.checkLibraryMatchFunctions();
}
/** @brief Run checks against the simplified token list */
@ -87,9 +83,6 @@ public:
checkOther.checkIncompleteStatement();
checkOther.checkCastIntToCharAndBack();
checkOther.invalidFunctionUsage();
checkOther.checkMathFunctions();
checkOther.checkMisusedScopedObject();
checkOther.checkMemsetZeroBytes();
checkOther.checkMemsetInvalid2ndParam();
@ -115,16 +108,6 @@ public:
/** @brief Check for pointer casts to a type with an incompatible binary data representation */
void invalidPointerCast();
/**
* @brief Invalid function usage (invalid input value / overlapping data)
*
* %Check that given function parameters are valid according to the standard
* - wrong radix given for strtol/strtoul
* - overlapping data when using sprintf/snprintf
* - wrong input value according to library
*/
void invalidFunctionUsage();
/** @brief %Check scope of variables */
void checkVariableScope();
static bool checkInnerScope(const Token *tok, const Variable* var, bool& used);
@ -147,9 +130,6 @@ public:
/** @brief Check for NaN (not-a-number) in an arithmetic expression */
void checkNanInArithmeticExpression();
/** @brief %Check for parameters given to math function that do not make sense*/
void checkMathFunctions();
/** @brief copying to memory or assigning to a variable twice */
void checkRedundantAssignment();
@ -214,15 +194,9 @@ public:
/** @brief %Check for using of comparison functions evaluating always to true or false. */
void checkComparisonFunctionIsAlwaysTrueOrFalse();
/** @brief %Check for ignored return values. */
void checkIgnoredReturnValue();
/** @brief %Check for redundant pointer operations */
void checkRedundantPointerOp();
/** @brief --check-library: warn for unconfigured function calls */
void checkLibraryMatchFunctions();
/** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */
void checkInterlockedDecrement();
@ -238,8 +212,6 @@ private:
void clarifyStatementError(const Token* tok);
void cstyleCastError(const Token *tok);
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const std::string &validstr);
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr);
void passedByValueError(const Token *tok, const std::string &parname);
void constStatementError(const Token *tok, const std::string &type);
void charArrayIndexError(const Token *tok);
@ -248,8 +220,6 @@ private:
void zerodivError(const Token *tok, bool inconclusive);
void zerodivcondError(const Token *tokcond, const Token *tokdiv, bool inconclusive);
void nanInArithmeticExpressionError(const Token *tok);
void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1);
void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp);
void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive);
void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var);
void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var);
@ -278,7 +248,6 @@ private:
void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean);
void varFuncNullUBError(const Token *tok);
void commaSeparatedReturnError(const Token *tok);
void ignoredReturnValueError(const Token* tok, const std::string& function);
void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive);
void raceAfterInterlockedDecrementError(const Token* tok);
void unusedLabelError(const Token* tok);
@ -287,8 +256,6 @@ private:
CheckOther c(0, settings, errorLogger);
// error
c.invalidFunctionArgError(0, "func_name", 1, "1-4");
c.invalidFunctionArgBoolError(0, "func_name", 1);
c.zerodivError(0, false);
c.zerodivcondError(0,0,false);
c.misusedScopeObjectError(NULL, "varname");
@ -317,8 +284,6 @@ private:
c.suspiciousCaseInSwitchError(0, "||");
c.suspiciousEqualityComparisonError(0);
c.selfAssignmentError(0, "varname");
c.mathfunctionCallWarning(0);
c.mathfunctionCallWarning(0, "1 - erf(x)", "erfc(x)");
c.memsetZeroBytesError(0, "varname");
c.memsetFloatError(0, "varname");
c.memsetValueOutOfRangeError(0, "varname");
@ -338,7 +303,6 @@ private:
c.varFuncNullUBError(0);
c.nanInArithmeticExpressionError(0);
c.commaSeparatedReturnError(0);
c.ignoredReturnValueError(0, "malloc");
c.redundantPointerOpError(0, "varname", false);
c.unusedLabelError(0);
}
@ -358,13 +322,11 @@ private:
"- bitwise operation with negative right operand\n"
"- provide wrong dimensioned array to pipe() system command (--std=posix)\n"
"- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n"
"- invalid input values for functions\n"
"- race condition with non-interlocked access after InterlockedDecrement() call\n"
// warning
"- either division by zero or useless condition\n"
"- memset() with a value out of range as the 2nd parameter\n"
"- return value of certain functions not used\n"
// performance
"- redundant data copying for const variable\n"

View File

@ -19,6 +19,7 @@
#include "tokenize.h"
#include "checkfunctions.h"
#include "testsuite.h"
#include <tinyxml2.h>
class TestFunctions : public TestFixture {
@ -39,58 +40,66 @@ private:
LOAD_LIB_2(settings.library, "std.cfg");
LOAD_LIB_2(settings.library, "posix.cfg");
TEST_CASE(testbsd_signal);
TEST_CASE(testgethostbyname);
TEST_CASE(testgethostbyaddr);
TEST_CASE(testusleep);
TEST_CASE(testindex);
TEST_CASE(test_qt_index); // FP when using the Qt function 'index'?
TEST_CASE(testrindex);
// Prohibited functions
TEST_CASE(prohibitedFunctions_posix);
TEST_CASE(prohibitedFunctions_index);
TEST_CASE(prohibitedFunctions_qt_index); // FP when using the Qt function 'index'?
TEST_CASE(prohibitedFunctions_rindex);
TEST_CASE(prohibitedFunctions_var); // no false positives for variables
TEST_CASE(prohibitedFunctions_gets); // dangerous function
TEST_CASE(prohibitedFunctions_alloca);
TEST_CASE(prohibitedFunctions_declaredFunction); // declared function ticket #3121
TEST_CASE(prohibitedFunctions_std_gets); // test std::gets
TEST_CASE(prohibitedFunctions_multiple); // multiple use of obsolete functions
TEST_CASE(prohibitedFunctions_c_declaration); // c declared function
TEST_CASE(prohibitedFunctions_functionWithBody); // function with body
TEST_CASE(prohibitedFunctions_crypt); // Non-reentrant function
TEST_CASE(prohibitedFunctions_namespaceHandling);
// no false positives for variables
TEST_CASE(testvar);
// Invalid function usage
TEST_CASE(invalidFunctionUsage1);
// dangerous function
TEST_CASE(testgets);
// Math function usage
TEST_CASE(mathfunctionCall_fmod);
TEST_CASE(mathfunctionCall_sqrt);
TEST_CASE(mathfunctionCall_log);
TEST_CASE(mathfunctionCall_acos);
TEST_CASE(mathfunctionCall_asin);
TEST_CASE(mathfunctionCall_pow);
TEST_CASE(mathfunctionCall_atan2);
TEST_CASE(mathfunctionCall_precision);
TEST_CASE(testalloca);
// declared function ticket #3121
TEST_CASE(test_declared_function);
// test std::gets
TEST_CASE(test_std_gets);
// multiple use of obsolete functions
TEST_CASE(test_multiple);
// c declared function
TEST_CASE(test_c_declaration);
// function with body
TEST_CASE(test_function_with_body);
// Non-reentrant functions
TEST_CASE(test_crypt);
TEST_CASE(test_namespace_handling);
// Ignored return value
TEST_CASE(checkIgnoredReturnValue);
}
void check(const char code[], const char filename[]="test.cpp") {
void check(const char code[], const char filename[]="test.cpp", const Settings* settings_=nullptr) {
// Clear the error buffer..
errout.str("");
if (!settings_)
settings_ = &settings;
// Tokenize..
Tokenizer tokenizer(&settings, this);
Tokenizer tokenizer(settings_, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, filename);
CheckFunctions checkFunctions(&tokenizer, settings_, this);
// Check...
checkFunctions.checkIgnoredReturnValue();
// Simplify...
tokenizer.simplifyTokenList2();
// Check...
CheckFunctions checkFunctions(&tokenizer, &settings, this);
checkFunctions.check();
checkFunctions.checkProhibitedFunctions();
checkFunctions.checkMathFunctions();
checkFunctions.invalidFunctionUsage();
}
void testbsd_signal() {
void prohibitedFunctions_posix() {
check("void f()\n"
"{\n"
" bsd_signal(SIGABRT, SIG_IGN);\n"
@ -103,10 +112,7 @@ private:
" return bsd_signal;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void testgethostbyname() {
check("void f()\n"
"{\n"
" struct hostent *hp;\n"
@ -115,9 +121,7 @@ private:
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'gethostbyname' called. It is recommended to use 'getaddrinfo' instead.\n", errout.str());
}
void testgethostbyaddr() {
check("void f()\n"
"{\n"
" long addr;\n"
@ -127,9 +131,7 @@ private:
" }\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (style) Obsolescent function 'gethostbyaddr' called. It is recommended to use 'getnameinfo' instead.\n", errout.str());
}
void testusleep() {
check("void f()\n"
"{\n"
" usleep( 1000 );\n"
@ -137,8 +139,7 @@ private:
ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str());
}
void testindex() {
void prohibitedFunctions_index() {
check("namespace n1 {\n"
" int index(){};\n"
"}\n"
@ -178,14 +179,14 @@ private:
errout.str());
}
void test_qt_index() {
void prohibitedFunctions_qt_index() {
check("void TDataModel::forceRowRefresh(int row) {\n"
" emit dataChanged(index(row, 0), index(row, columnCount() - 1));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", errout.str());
}
void testrindex() {
void prohibitedFunctions_rindex() {
check("void f()\n"
"{\n"
" int rindex( 0 );\n"
@ -201,7 +202,7 @@ private:
}
void testvar() {
void prohibitedFunctions_var() {
check("class Fred {\n"
"public:\n"
" Fred() : index(0) { }\n"
@ -210,7 +211,7 @@ private:
ASSERT_EQUALS("", errout.str());
}
void testgets() {
void prohibitedFunctions_gets() {
check("void f()\n"
"{\n"
" char *x = gets(a);\n"
@ -224,7 +225,7 @@ private:
ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' instead.\n", errout.str());
}
void testalloca() {
void prohibitedFunctions_alloca() {
check("void f()\n"
"{\n"
" char *x = alloca(10);\n"
@ -255,7 +256,7 @@ private:
}
// ticket #3121
void test_declared_function() {
void prohibitedFunctions_declaredFunction() {
check("int ftime ( int a )\n"
"{\n"
" return a;\n"
@ -269,7 +270,7 @@ private:
}
// test std::gets
void test_std_gets() {
void prohibitedFunctions_std_gets() {
check("void f(char * str)\n"
"{\n"
" char *x = std::gets(str);\n"
@ -280,7 +281,7 @@ private:
}
// multiple use
void test_multiple() {
void prohibitedFunctions_multiple() {
check("void f(char * str)\n"
"{\n"
" char *x = std::gets(str);\n"
@ -290,7 +291,7 @@ private:
"[test.cpp:4]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str());
}
void test_c_declaration() {
void prohibitedFunctions_c_declaration() {
check("char * gets ( char * c ) ;\n"
"int main ()\n"
"{\n"
@ -307,7 +308,7 @@ private:
ASSERT_EQUALS("[test.cpp:4]: (portability) Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads.\n", errout.str());
}
void test_function_with_body() {
void prohibitedFunctions_functionWithBody() {
check("char * gets ( char * c ) { return c; }\n"
"int main ()\n"
"{\n"
@ -317,13 +318,14 @@ private:
ASSERT_EQUALS("", errout.str());
}
void test_crypt() {
void prohibitedFunctions_crypt() {
check("void f(char *pwd)\n"
"{\n"
" char *cpwd;"
" crypt(pwd, cpwd);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n"
"[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str());
check("void f()\n"
"{\n"
@ -331,7 +333,8 @@ private:
" char *cpwd;"
" crypt(pwd, cpwd);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n"
"[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str());
check("int f()\n"
"{\n"
@ -341,7 +344,7 @@ private:
ASSERT_EQUALS("", errout.str());
}
void test_namespace_handling() {
void prohibitedFunctions_namespaceHandling() {
check("int f()\n"
"{\n"
" time_t t = 0;"
@ -369,7 +372,8 @@ private:
"{\n"
" ::getpwent();\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function getpwent() is not used.\n"
"[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str());
// Be quiet on function definitions
check("int getpwent()\n"
@ -392,6 +396,490 @@ private:
"}");
ASSERT_EQUALS("", errout.str());
}
void invalidFunctionUsage1() {
check("int f() { memset(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
check("int f() { memset(a,b,sizeof(a)!=0); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
// Ticket #6588 (c mode)
check("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}", "test.c");
ASSERT_EQUALS("[test.c:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
// Ticket #6588 (c++ mode)
check("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}");
ASSERT_EQUALS("[test.cpp:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
check("int f() { strtol(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (comparison result) but the valid values are '0,2:36'.\n", errout.str());
check("int f() { strtol(a,b,1); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'.\n", errout.str());
check("int f() { strtol(a,b,10); }");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_sqrt() {
// sqrt, sqrtf, sqrtl
check("void foo()\n"
"{\n"
" std::cout << sqrt(-1) << std::endl;\n"
" std::cout << sqrtf(-1) << std::endl;\n"
" std::cout << sqrtl(-1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1 to sqrt() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1 to sqrtf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1 to sqrtl() leads to implementation-defined result.\n", errout.str());
// implementation-defined behaviour for "finite values of x<0" only:
check("void foo()\n"
"{\n"
" std::cout << sqrt(-0.) << std::endl;\n"
" std::cout << sqrtf(-0.) << std::endl;\n"
" std::cout << sqrtl(-0.) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << sqrt(1) << std::endl;\n"
" std::cout << sqrtf(1) << std::endl;\n"
" std::cout << sqrtl(1) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_log() {
// log,log10,logf,logl,log10f,log10l
check("void foo()\n"
"{\n"
" std::cout << log(-2) << std::endl;\n"
" std::cout << logf(-2) << std::endl;\n"
" std::cout << logl(-2) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -2 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -2 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -2 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-1) << std::endl;\n"
" std::cout << logf(-1) << std::endl;\n"
" std::cout << logl(-1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-1.0) << std::endl;\n"
" std::cout << logf(-1.0) << std::endl;\n"
" std::cout << logl(-1.0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.0 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.0 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.0 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-0.1) << std::endl;\n"
" std::cout << logf(-0.1) << std::endl;\n"
" std::cout << logl(-0.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -0.1 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -0.1 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -0.1 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(0) << std::endl;\n"
" std::cout << logf(0.) << std::endl;\n"
" std::cout << logl(0.0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 0 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 0. to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 0.0 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(1E-3) << std::endl;\n"
" std::cout << logf(1E-3) << std::endl;\n"
" std::cout << logl(1E-3) << std::endl;\n"
" std::cout << log(1.0E-3) << std::endl;\n"
" std::cout << logf(1.0E-3) << std::endl;\n"
" std::cout << logl(1.0E-3) << std::endl;\n"
" std::cout << log(1.0E+3) << std::endl;\n"
" std::cout << logf(1.0E+3) << std::endl;\n"
" std::cout << logl(1.0E+3) << std::endl;\n"
" std::cout << log(2.0) << std::endl;\n"
" std::cout << logf(2.0) << std::endl;\n"
" std::cout << logf(2.0f) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::string *log(0);\n"
"}");
ASSERT_EQUALS("", errout.str());
// #3473 - no warning if "log" is a variable
check("Fred::Fred() : log(0) { }");
ASSERT_EQUALS("", errout.str());
// #5748
check("void f() { foo.log(0); }");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_acos() {
// acos, acosf, acosl
check("void foo()\n"
"{\n"
" return acos(-1) \n"
" + acos(0.1) \n"
" + acos(0.0001) \n"
" + acos(0.01) \n"
" + acos(1.0E-1) \n"
" + acos(-1.0E-1) \n"
" + acos(+1.0E-1) \n"
" + acos(0.1E-1) \n"
" + acos(+0.1E-1) \n"
" + acos(-0.1E-1) \n"
" + acosf(-1) \n"
" + acosf(0.1) \n"
" + acosf(0.0001) \n"
" + acosf(0.01) \n"
" + acosf(1.0E-1) \n"
" + acosf(-1.0E-1) \n"
" + acosf(+1.0E-1) \n"
" + acosf(0.1E-1) \n"
" + acosf(+0.1E-1) \n"
" + acosf(-0.1E-1) \n"
" + acosl(-1) \n"
" + acosl(0.1) \n"
" + acosl(0.0001) \n"
" + acosl(0.01) \n"
" + acosl(1.0E-1) \n"
" + acosl(-1.0E-1) \n"
" + acosl(+1.0E-1) \n"
" + acosl(0.1E-1) \n"
" + acosl(+0.1E-1) \n"
" + acosl(-0.1E-1); \n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << acos(1.1) << std::endl;\n"
" std::cout << acosf(1.1) << std::endl;\n"
" std::cout << acosl(1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to acos() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to acosf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to acosl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << acos(-1.1) << std::endl;\n"
" std::cout << acosf(-1.1) << std::endl;\n"
" std::cout << acosl(-1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to acos() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to acosf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to acosl() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_asin() {
// asin, asinf, asinl
check("void foo()\n"
"{\n"
" return asin(1) \n"
" + asin(-1) \n"
" + asin(0.1) \n"
" + asin(0.0001) \n"
" + asin(0.01) \n"
" + asin(1.0E-1) \n"
" + asin(-1.0E-1) \n"
" + asin(+1.0E-1) \n"
" + asin(0.1E-1) \n"
" + asin(+0.1E-1) \n"
" + asin(-0.1E-1) \n"
" + asinf(1) \n"
" + asinf(-1) \n"
" + asinf(0.1) \n"
" + asinf(0.0001) \n"
" + asinf(0.01) \n"
" + asinf(1.0E-1) \n"
" + asinf(-1.0E-1) \n"
" + asinf(+1.0E-1) \n"
" + asinf(0.1E-1) \n"
" + asinf(+0.1E-1) \n"
" + asinf(-0.1E-1) \n"
" + asinl(1) \n"
" + asinl(-1) \n"
" + asinl(0.1) \n"
" + asinl(0.0001) \n"
" + asinl(0.01) \n"
" + asinl(1.0E-1) \n"
" + asinl(-1.0E-1) \n"
" + asinl(+1.0E-1) \n"
" + asinl(0.1E-1) \n"
" + asinl(+0.1E-1) \n"
" + asinl(-0.1E-1); \n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << asin(1.1) << std::endl;\n"
" std::cout << asinf(1.1) << std::endl;\n"
" std::cout << asinl(1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to asin() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to asinf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to asinl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << asin(-1.1) << std::endl;\n"
" std::cout << asinf(-1.1) << std::endl;\n"
" std::cout << asinl(-1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to asin() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to asinf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to asinl() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_pow() {
// pow, powf, powl
check("void foo()\n"
"{\n"
" std::cout << pow(0,-10) << std::endl;\n"
" std::cout << powf(0,-10) << std::endl;\n"
" std::cout << powl(0,-10) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and -10 to pow() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 0 and -10 to powf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 0 and -10 to powl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << pow(0,10) << std::endl;\n"
" std::cout << powf(0,10) << std::endl;\n"
" std::cout << powl(0,10) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_atan2() {
// atan2
check("void foo()\n"
"{\n"
" std::cout << atan2(1,1) ;\n"
" std::cout << atan2(-1,-1) ;\n"
" std::cout << atan2(0.1,1) ;\n"
" std::cout << atan2(0.0001,100) ;\n"
" std::cout << atan2(0.0,1e-1) ;\n"
" std::cout << atan2(1.0E-1,-3) ;\n"
" std::cout << atan2(-1.0E-1,+2) ;\n"
" std::cout << atan2(+1.0E-1,0) ;\n"
" std::cout << atan2(0.1E-1,3) ;\n"
" std::cout << atan2(+0.1E-1,1) ;\n"
" std::cout << atan2(-0.1E-1,8) ;\n"
" std::cout << atan2f(1,1) ;\n"
" std::cout << atan2f(-1,-1) ;\n"
" std::cout << atan2f(0.1,1) ;\n"
" std::cout << atan2f(0.0001,100) ;\n"
" std::cout << atan2f(0.0,1e-1) ;\n"
" std::cout << atan2f(1.0E-1,-3) ;\n"
" std::cout << atan2f(-1.0E-1,+2) ;\n"
" std::cout << atan2f(+1.0E-1,0) ;\n"
" std::cout << atan2f(0.1E-1,3) ;\n"
" std::cout << atan2f(+0.1E-1,1) ;\n"
" std::cout << atan2f(-0.1E-1,8) ;\n"
" std::cout << atan2l(1,1) ;\n"
" std::cout << atan2l(-1,-1) ;\n"
" std::cout << atan2l(0.1,1) ;\n"
" std::cout << atan2l(0.0001,100) ;\n"
" std::cout << atan2l(0.0,1e-1) ;\n"
" std::cout << atan2l(1.0E-1,-3) ;\n"
" std::cout << atan2l(-1.0E-1,+2) ;\n"
" std::cout << atan2l(+1.0E-1,0) ;\n"
" std::cout << atan2l(0.1E-1,3) ;\n"
" std::cout << atan2l(+0.1E-1,1) ;\n"
" std::cout << atan2l(-0.1E-1,8) ;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << atan2(0,0) << std::endl;\n"
" std::cout << atan2f(0,0) << std::endl;\n"
" std::cout << atan2l(0,0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and 0 to atan2() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 0 and 0 to atan2f() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 0 and 0 to atan2l() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_fmod() {
// fmod, fmodl, fmodf
check("void foo()\n"
"{\n"
" std::cout << fmod(1.0,0) << std::endl;\n"
" std::cout << fmodf(1.0,0) << std::endl;\n"
" std::cout << fmodl(1.0,0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 1.0 and 0 to fmod() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 1.0 and 0 to fmodf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 1.0 and 0 to fmodl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << fmod(1.0,1) << std::endl;\n"
" std::cout << fmodf(1.0,1) << std::endl;\n"
" std::cout << fmodl(1.0,1) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_precision() {
check("void foo() {\n"
" print(exp(x) - 1);\n"
" print(log(1 + x));\n"
" print(1 - erf(x));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(exp(x) - 1.0);\n"
" print(log(1.0 + x));\n"
" print(1.0 - erf(x));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(exp(3 + x*f(a)) - 1);\n"
" print(log(x*4 + 1));\n"
" print(1 - erf(34*x + f(x) - c));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(2*exp(x) - 1);\n"
" print(1 - erf(x)/2.0);\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void checkIgnoredReturnValue() {
Settings settings2;
settings2.addEnabled("warning");
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def version=\"2\">\n"
" <function name=\"mystrcmp,foo::mystrcmp\">\n"
" <use-retval/>\n"
" <arg nr=\"1\"/>\n"
" <arg nr=\"2\"/>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
settings2.library.load(doc);
check("void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("void foo() {\n"
" foo::mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("bool mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition, but it returns a value. Assume it is the one specified in the library.
"void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("void mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition which returns void!
"void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" class mystrcmp { mystrcmp() {} };\n" // strcmp is a constructor definition here
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" return mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" return foo::mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" if(mystrcmp(a, b));\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" bool b = mystrcmp(a, b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
// #6194
check("void foo() {\n"
" MyStrCmp mystrcmp(x, y);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
// #6197
check("void foo() {\n"
" abc::def.mystrcmp(a,b);\n"
"}", "test.cpp", &settings2);
ASSERT_EQUALS("", errout.str());
// #6233
check("int main() {\n"
" auto lambda = [](double value) {\n"
" double rounded = floor(value + 0.5);\n"
" printf(\"Rounded value = %f\\n\", rounded);\n"
" };\n"
" lambda(13.3);\n"
" return 0;\n"
"}");
ASSERT_EQUALS("", errout.str());
// #6669
check("void foo(size_t size) {\n"
" void * res{malloc(size)};\n"
"}");
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestFunctions)

View File

@ -49,8 +49,6 @@ private:
TEST_CASE(nanInArithmeticExpression);
TEST_CASE(invalidFunctionUsage1);
TEST_CASE(varScope1);
TEST_CASE(varScope2);
TEST_CASE(varScope3);
@ -79,15 +77,6 @@ private:
TEST_CASE(passedByValue);
TEST_CASE(mathfunctionCall_fmod);
TEST_CASE(mathfunctionCall_sqrt);
TEST_CASE(mathfunctionCall_log);
TEST_CASE(mathfunctionCall_acos);
TEST_CASE(mathfunctionCall_asin);
TEST_CASE(mathfunctionCall_pow);
TEST_CASE(mathfunctionCall_atan2);
TEST_CASE(mathfunctionCall_precision);
TEST_CASE(switchRedundantAssignmentTest);
TEST_CASE(switchRedundantOperationTest);
TEST_CASE(switchRedundantBitwiseOperationTest);
@ -163,8 +152,6 @@ private:
TEST_CASE(integerOverflow); // #5895
TEST_CASE(checkIgnoredReturnValue);
TEST_CASE(redundantPointerOp);
TEST_CASE(test_isSameExpression);
TEST_CASE(raceAfterInterlockedDecrement);
@ -600,62 +587,6 @@ private:
}
void invalidFunctionUsage(const char code[], bool isCPP = true) {
// Clear the error buffer..
errout.str("");
const char cfg[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"memset\"> <arg nr=\"3\"><not-bool/></arg> </function>\n"
" <function name=\"strtol\"> <arg nr=\"3\"><valid>0,2:36</valid></arg> </function>\n"
"</def>";
tinyxml2::XMLDocument xmldoc;
xmldoc.Parse(cfg, sizeof(cfg));
Settings settings;
settings.library.load(xmldoc);
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, isCPP ? "test.cpp" : "test.c");
// Check for redundant code..
CheckOther checkOther(&tokenizer, &settings, this);
checkOther.invalidFunctionUsage();
}
void invalidFunctionUsage1() {
invalidFunctionUsage("int f() { memset(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
invalidFunctionUsage("int f() { memset(a,b,sizeof(a)!=0); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
// Ticket #6588 (c mode)
invalidFunctionUsage("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}", /*isCPP=*/false);
ASSERT_EQUALS("[test.c:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
// Ticket #6588 (c++ mode)
invalidFunctionUsage("void record(char* buf, int n) {\n"
" memset(buf, 0, n < 255);\n" /* KO */
" memset(buf, 0, n < 255 ? n : 255);\n" /* OK */
"}");
ASSERT_EQUALS("[test.cpp:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,sizeof(a)!=12); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (comparison result) but the valid values are '0,2:36'.\n", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,1); }");
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'.\n", errout.str());
invalidFunctionUsage("int f() { strtol(a,b,10); }");
ASSERT_EQUALS("", errout.str());
}
void varScope1() {
check("unsigned short foo()\n"
"{\n"
@ -1408,367 +1339,6 @@ private:
}
void mathfunctionCall_sqrt() {
// sqrt, sqrtf, sqrtl
check("void foo()\n"
"{\n"
" std::cout << sqrt(-1) << std::endl;\n"
" std::cout << sqrtf(-1) << std::endl;\n"
" std::cout << sqrtl(-1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1 to sqrt() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1 to sqrtf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1 to sqrtl() leads to implementation-defined result.\n", errout.str());
// implementation-defined behaviour for "finite values of x<0" only:
check("void foo()\n"
"{\n"
" std::cout << sqrt(-0.) << std::endl;\n"
" std::cout << sqrtf(-0.) << std::endl;\n"
" std::cout << sqrtl(-0.) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << sqrt(1) << std::endl;\n"
" std::cout << sqrtf(1) << std::endl;\n"
" std::cout << sqrtl(1) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_log() {
// log,log10,logf,logl,log10f,log10l
check("void foo()\n"
"{\n"
" std::cout << log(-2) << std::endl;\n"
" std::cout << logf(-2) << std::endl;\n"
" std::cout << logl(-2) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -2 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -2 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -2 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-1) << std::endl;\n"
" std::cout << logf(-1) << std::endl;\n"
" std::cout << logl(-1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-1.0) << std::endl;\n"
" std::cout << logf(-1.0) << std::endl;\n"
" std::cout << logl(-1.0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.0 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.0 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.0 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(-0.1) << std::endl;\n"
" std::cout << logf(-0.1) << std::endl;\n"
" std::cout << logl(-0.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -0.1 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -0.1 to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -0.1 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(0) << std::endl;\n"
" std::cout << logf(0.) << std::endl;\n"
" std::cout << logl(0.0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 0 to log() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 0. to logf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 0.0 to logl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << log(1E-3) << std::endl;\n"
" std::cout << logf(1E-3) << std::endl;\n"
" std::cout << logl(1E-3) << std::endl;\n"
" std::cout << log(1.0E-3) << std::endl;\n"
" std::cout << logf(1.0E-3) << std::endl;\n"
" std::cout << logl(1.0E-3) << std::endl;\n"
" std::cout << log(1.0E+3) << std::endl;\n"
" std::cout << logf(1.0E+3) << std::endl;\n"
" std::cout << logl(1.0E+3) << std::endl;\n"
" std::cout << log(2.0) << std::endl;\n"
" std::cout << logf(2.0) << std::endl;\n"
" std::cout << logf(2.0f) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::string *log(0);\n"
"}");
ASSERT_EQUALS("", errout.str());
// #3473 - no warning if "log" is a variable
check("Fred::Fred() : log(0) { }");
ASSERT_EQUALS("", errout.str());
// #5748
check("void f() { foo.log(0); }");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_acos() {
// acos, acosf, acosl
check("void foo()\n"
"{\n"
" return acos(-1) \n"
" + acos(0.1) \n"
" + acos(0.0001) \n"
" + acos(0.01) \n"
" + acos(1.0E-1) \n"
" + acos(-1.0E-1) \n"
" + acos(+1.0E-1) \n"
" + acos(0.1E-1) \n"
" + acos(+0.1E-1) \n"
" + acos(-0.1E-1) \n"
" + acosf(-1) \n"
" + acosf(0.1) \n"
" + acosf(0.0001) \n"
" + acosf(0.01) \n"
" + acosf(1.0E-1) \n"
" + acosf(-1.0E-1) \n"
" + acosf(+1.0E-1) \n"
" + acosf(0.1E-1) \n"
" + acosf(+0.1E-1) \n"
" + acosf(-0.1E-1) \n"
" + acosl(-1) \n"
" + acosl(0.1) \n"
" + acosl(0.0001) \n"
" + acosl(0.01) \n"
" + acosl(1.0E-1) \n"
" + acosl(-1.0E-1) \n"
" + acosl(+1.0E-1) \n"
" + acosl(0.1E-1) \n"
" + acosl(+0.1E-1) \n"
" + acosl(-0.1E-1); \n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << acos(1.1) << std::endl;\n"
" std::cout << acosf(1.1) << std::endl;\n"
" std::cout << acosl(1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to acos() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to acosf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to acosl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << acos(-1.1) << std::endl;\n"
" std::cout << acosf(-1.1) << std::endl;\n"
" std::cout << acosl(-1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to acos() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to acosf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to acosl() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_asin() {
// asin, asinf, asinl
check("void foo()\n"
"{\n"
" return asin(1) \n"
" + asin(-1) \n"
" + asin(0.1) \n"
" + asin(0.0001) \n"
" + asin(0.01) \n"
" + asin(1.0E-1) \n"
" + asin(-1.0E-1) \n"
" + asin(+1.0E-1) \n"
" + asin(0.1E-1) \n"
" + asin(+0.1E-1) \n"
" + asin(-0.1E-1) \n"
" + asinf(1) \n"
" + asinf(-1) \n"
" + asinf(0.1) \n"
" + asinf(0.0001) \n"
" + asinf(0.01) \n"
" + asinf(1.0E-1) \n"
" + asinf(-1.0E-1) \n"
" + asinf(+1.0E-1) \n"
" + asinf(0.1E-1) \n"
" + asinf(+0.1E-1) \n"
" + asinf(-0.1E-1) \n"
" + asinl(1) \n"
" + asinl(-1) \n"
" + asinl(0.1) \n"
" + asinl(0.0001) \n"
" + asinl(0.01) \n"
" + asinl(1.0E-1) \n"
" + asinl(-1.0E-1) \n"
" + asinl(+1.0E-1) \n"
" + asinl(0.1E-1) \n"
" + asinl(+0.1E-1) \n"
" + asinl(-0.1E-1); \n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << asin(1.1) << std::endl;\n"
" std::cout << asinf(1.1) << std::endl;\n"
" std::cout << asinl(1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 1.1 to asin() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value 1.1 to asinf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value 1.1 to asinl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << asin(-1.1) << std::endl;\n"
" std::cout << asinf(-1.1) << std::endl;\n"
" std::cout << asinl(-1.1) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.1 to asin() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing value -1.1 to asinf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing value -1.1 to asinl() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_pow() {
// pow, powf, powl
check("void foo()\n"
"{\n"
" std::cout << pow(0,-10) << std::endl;\n"
" std::cout << powf(0,-10) << std::endl;\n"
" std::cout << powl(0,-10) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and -10 to pow() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 0 and -10 to powf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 0 and -10 to powl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << pow(0,10) << std::endl;\n"
" std::cout << powf(0,10) << std::endl;\n"
" std::cout << powl(0,10) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_atan2() {
// atan2
check("void foo()\n"
"{\n"
" std::cout << atan2(1,1) ;\n"
" std::cout << atan2(-1,-1) ;\n"
" std::cout << atan2(0.1,1) ;\n"
" std::cout << atan2(0.0001,100) ;\n"
" std::cout << atan2(0.0,1e-1) ;\n"
" std::cout << atan2(1.0E-1,-3) ;\n"
" std::cout << atan2(-1.0E-1,+2) ;\n"
" std::cout << atan2(+1.0E-1,0) ;\n"
" std::cout << atan2(0.1E-1,3) ;\n"
" std::cout << atan2(+0.1E-1,1) ;\n"
" std::cout << atan2(-0.1E-1,8) ;\n"
" std::cout << atan2f(1,1) ;\n"
" std::cout << atan2f(-1,-1) ;\n"
" std::cout << atan2f(0.1,1) ;\n"
" std::cout << atan2f(0.0001,100) ;\n"
" std::cout << atan2f(0.0,1e-1) ;\n"
" std::cout << atan2f(1.0E-1,-3) ;\n"
" std::cout << atan2f(-1.0E-1,+2) ;\n"
" std::cout << atan2f(+1.0E-1,0) ;\n"
" std::cout << atan2f(0.1E-1,3) ;\n"
" std::cout << atan2f(+0.1E-1,1) ;\n"
" std::cout << atan2f(-0.1E-1,8) ;\n"
" std::cout << atan2l(1,1) ;\n"
" std::cout << atan2l(-1,-1) ;\n"
" std::cout << atan2l(0.1,1) ;\n"
" std::cout << atan2l(0.0001,100) ;\n"
" std::cout << atan2l(0.0,1e-1) ;\n"
" std::cout << atan2l(1.0E-1,-3) ;\n"
" std::cout << atan2l(-1.0E-1,+2) ;\n"
" std::cout << atan2l(+1.0E-1,0) ;\n"
" std::cout << atan2l(0.1E-1,3) ;\n"
" std::cout << atan2l(+0.1E-1,1) ;\n"
" std::cout << atan2l(-0.1E-1,8) ;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo()\n"
"{\n"
" std::cout << atan2(0,0) << std::endl;\n"
" std::cout << atan2f(0,0) << std::endl;\n"
" std::cout << atan2l(0,0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and 0 to atan2() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 0 and 0 to atan2f() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 0 and 0 to atan2l() leads to implementation-defined result.\n", errout.str());
}
void mathfunctionCall_fmod() {
// fmod, fmodl, fmodf
check("void foo()\n"
"{\n"
" std::cout << fmod(1.0,0) << std::endl;\n"
" std::cout << fmodf(1.0,0) << std::endl;\n"
" std::cout << fmodl(1.0,0) << std::endl;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 1.0 and 0 to fmod() leads to implementation-defined result.\n"
"[test.cpp:4]: (warning) Passing values 1.0 and 0 to fmodf() leads to implementation-defined result.\n"
"[test.cpp:5]: (warning) Passing values 1.0 and 0 to fmodl() leads to implementation-defined result.\n", errout.str());
check("void foo()\n"
"{\n"
" std::cout << fmod(1.0,1) << std::endl;\n"
" std::cout << fmodf(1.0,1) << std::endl;\n"
" std::cout << fmodl(1.0,1) << std::endl;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void mathfunctionCall_precision() {
check("void foo() {\n"
" print(exp(x) - 1);\n"
" print(log(1 + x));\n"
" print(1 - erf(x));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(exp(x) - 1.0);\n"
" print(log(1.0 + x));\n"
" print(1.0 - erf(x));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(exp(3 + x*f(a)) - 1);\n"
" print(log(x*4 + 1));\n"
" print(1 - erf(34*x + f(x) - c));\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n"
"[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n"
"[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str());
check("void foo() {\n"
" print(2*exp(x) - 1);\n"
" print(1 - erf(x)/2.0);\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void switchRedundantAssignmentTest() {
check("void foo()\n"
@ -6135,97 +5705,6 @@ private:
#endif
}
void checkIgnoredReturnValue() {
Settings settings;
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"mystrcmp,foo::mystrcmp\">\n"
" <use-retval/>\n"
" <arg nr=\"1\"/>\n"
" <arg nr=\"2\"/>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
settings.library.load(doc);
check("void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("void foo() {\n"
" foo::mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("bool mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition, but it returns a value. Assume it is the one specified in the library.
"void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function mystrcmp() is not used.\n", errout.str());
check("void mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition which returns void!
"void foo() {\n"
" mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" class mystrcmp { mystrcmp() {} };\n" // strcmp is a constructor definition here
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" return mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" return foo::mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" if(mystrcmp(a, b));\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" bool b = mystrcmp(a, b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
// #6194
check("void foo() {\n"
" MyStrCmp mystrcmp(x, y);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
// #6197
check("void foo() {\n"
" abc::def.mystrcmp(a,b);\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
// #6233
check("int main() {\n"
" auto lambda = [](double value) {\n"
" double rounded = floor(value + 0.5);\n"
" printf(\"Rounded value = %f\\n\", rounded);\n"
" };\n"
" lambda(13.3);\n"
" return 0;\n"
"}");
ASSERT_EQUALS("", errout.str());
// #6669
check("void foo(size_t size) {\n"
" void * res{malloc(size)};\n"
"}", "test.cpp", false, false, true, &settings);
ASSERT_EQUALS("", errout.str());
}
void redundantPointerOp() {
check("int *f(int *x) {\n"
" return &*x;\n"