--------------------------------------------------------------------------- Detect passing wrong values to functions like atan(0, x); ---------------------------------------------------------------------------
| 440 | // Detect passing wrong values to <cmath> functions like atan(0, x); |
| 441 | //--------------------------------------------------------------------------- |
| 442 | void CheckFunctionsImpl::checkMathFunctions() |
| 443 | { |
| 444 | const bool styleC99 = mSettings.severity.isEnabled(Severity::style) && ((mTokenizer->isC() && mSettings.standards.c != Standards::C89) || (mTokenizer->isCPP() && mSettings.standards.cpp != Standards::CPP03)); |
| 445 | const bool printWarnings = mSettings.severity.isEnabled(Severity::warning); |
| 446 | |
| 447 | if (!styleC99 && !printWarnings && !mSettings.isPremiumEnabled("wrongmathcall")) |
| 448 | return; |
| 449 | |
| 450 | logChecker("CheckFunctions::checkMathFunctions"); // style,warning,c99,c++11 |
| 451 | |
| 452 | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
| 453 | for (const Scope *scope : symbolDatabase->functionScopes) { |
| 454 | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
| 455 | if (tok->varId()) |
| 456 | continue; |
| 457 | if (printWarnings && Token::Match(tok, "%name% ( !!)")) { |
| 458 | if (tok->strAt(-1) != "." |
| 459 | && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) { |
| 460 | const std::string& number = tok->strAt(2); |
| 461 | if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= 0) || |
| 462 | (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.)) |
| 463 | mathfunctionCallWarning(tok); |
| 464 | } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) { |
| 465 | const std::string& number = tok->strAt(2); |
| 466 | if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= -1) || |
| 467 | (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.)) |
| 468 | mathfunctionCallWarning(tok); |
| 469 | } |
| 470 | // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined |
| 471 | else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) { |
| 472 | if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) |
| 473 | mathfunctionCallWarning(tok, 2); |
| 474 | } |
| 475 | // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). |
| 476 | else if (Token::Match(tok, "fmod|fmodf|fmodl (")) { |
| 477 | const Token* nextArg = tok->tokAt(2)->nextArgument(); |
| 478 | if (nextArg && MathLib::isNullValue(nextArg->str())) |
| 479 | mathfunctionCallWarning(tok, 2); |
| 480 | } |
| 481 | // pow ( x , y) If x is zero, and y is negative --> division by zero |
| 482 | else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) { |
| 483 | if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) |
| 484 | mathfunctionCallWarning(tok, 2); |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | if (styleC99) { |
| 489 | if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) { |
| 490 | mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)"); |
| 491 | } 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()) { |
| 492 | mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)"); |
| 493 | } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) { |
| 494 | const Token* plus = tok->next()->astOperand2(); |
| 495 | if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str())))) |
| 496 | mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)"); |
| 497 | } |
| 498 | } |
| 499 | } |
no test coverage detected