New check: Recommend expm1, log1p, erfc (#5392)
This commit is contained in:
parent
7142edf03e
commit
ff4fc6a234
|
@ -1892,6 +1892,8 @@ void CheckOther::nanInArithmeticExpressionError(const Token *tok)
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
void CheckOther::checkMathFunctions()
|
void CheckOther::checkMathFunctions()
|
||||||
{
|
{
|
||||||
|
bool styleC99 = _settings->isEnabled("style") && _settings->standards.c != Standards::C89 && _settings->standards.cpp != Standards::CPP03;
|
||||||
|
|
||||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||||
const std::size_t functions = symbolDatabase->functionScopes.size();
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
||||||
for (std::size_t i = 0; i < functions; ++i) {
|
for (std::size_t i = 0; i < functions; ++i) {
|
||||||
|
@ -1943,6 +1945,18 @@ void CheckOther::checkMathFunctions()
|
||||||
MathLib::isNegative(tok->strAt(4))) {
|
MathLib::isNegative(tok->strAt(4))) {
|
||||||
mathfunctionCallWarning(tok, 2);
|
mathfunctionCallWarning(tok, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (styleC99) {
|
||||||
|
if (Token::Match(tok, "%num% - erf (") && MathLib::toDoubleNumber(tok->str()) == 1.0 && 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%") && MathLib::toDoubleNumber(tok->linkAt(1)->strAt(2)) == 1.0 && tok->linkAt(1)->next()->astOperand1() == tok->next()) {
|
||||||
|
mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)");
|
||||||
|
} else if (Token::Match(tok, "log (") && tok->next()->astOperand2()) {
|
||||||
|
const Token* plus = tok->next()->astOperand2();
|
||||||
|
if (plus->str() == "+" && ((plus->astOperand1() && MathLib::toDoubleNumber(plus->astOperand1()->str()) == 1.0) || (plus->astOperand2() && MathLib::toDoubleNumber(plus->astOperand2()->str()) == 1.0)))
|
||||||
|
mathfunctionCallWarning(tok, "log(1 + x)", "log10(x)");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1958,6 +1972,11 @@ void CheckOther::mathfunctionCallWarning(const Token *tok, const unsigned int nu
|
||||||
reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.");
|
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
|
// Creating instance of clases which are destroyed immediately
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -258,6 +258,7 @@ private:
|
||||||
void zerodivcondError(const Token *tokcond, const Token *tokdiv, bool inconclusive);
|
void zerodivcondError(const Token *tokcond, const Token *tokdiv, bool inconclusive);
|
||||||
void nanInArithmeticExpressionError(const Token *tok);
|
void nanInArithmeticExpressionError(const Token *tok);
|
||||||
void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1);
|
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 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 redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var);
|
||||||
void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var);
|
void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var);
|
||||||
|
@ -326,6 +327,7 @@ private:
|
||||||
c.suspiciousEqualityComparisonError(0);
|
c.suspiciousEqualityComparisonError(0);
|
||||||
c.selfAssignmentError(0, "varname");
|
c.selfAssignmentError(0, "varname");
|
||||||
c.mathfunctionCallWarning(0);
|
c.mathfunctionCallWarning(0);
|
||||||
|
c.mathfunctionCallWarning(0, "1 - erf(x)", "erfc(x)");
|
||||||
c.memsetZeroBytesError(0, "varname");
|
c.memsetZeroBytesError(0, "varname");
|
||||||
c.memsetFloatError(0, "varname");
|
c.memsetFloatError(0, "varname");
|
||||||
c.memsetValueOutOfRangeError(0, "varname");
|
c.memsetValueOutOfRangeError(0, "varname");
|
||||||
|
@ -405,7 +407,8 @@ private:
|
||||||
"* redundant get and set function of user id (--std=posix).\n"
|
"* redundant get and set function of user id (--std=posix).\n"
|
||||||
"* Passing NULL pointer to function with variable number of arguments leads to UB on some platforms.\n"
|
"* Passing NULL pointer to function with variable number of arguments leads to UB on some platforms.\n"
|
||||||
"* NaN (not a number) value used in arithmetic expression.\n"
|
"* NaN (not a number) value used in arithmetic expression.\n"
|
||||||
"* comma in return statement (the comma can easily be misread as a semicolon).\n";
|
"* comma in return statement (the comma can easily be misread as a semicolon).\n"
|
||||||
|
"* prefer erfc, expm1 or log1p to avoid loss of precision.\n";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
|
|
|
@ -88,6 +88,7 @@ private:
|
||||||
TEST_CASE(mathfunctionCall_asin);
|
TEST_CASE(mathfunctionCall_asin);
|
||||||
TEST_CASE(mathfunctionCall_pow);
|
TEST_CASE(mathfunctionCall_pow);
|
||||||
TEST_CASE(mathfunctionCall_atan2);
|
TEST_CASE(mathfunctionCall_atan2);
|
||||||
|
TEST_CASE(mathfunctionCall_precision);
|
||||||
|
|
||||||
TEST_CASE(switchRedundantAssignmentTest);
|
TEST_CASE(switchRedundantAssignmentTest);
|
||||||
TEST_CASE(switchRedundantOperationTest);
|
TEST_CASE(switchRedundantOperationTest);
|
||||||
|
@ -1689,6 +1690,41 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
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 'log10(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 'log10(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 'log10(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() {
|
void switchRedundantAssignmentTest() {
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
|
|
Loading…
Reference in New Issue