Fixed #5845 (new check: same condition after noreturn conditional code => second condition is always false)
This commit is contained in:
parent
8aa568f085
commit
f2ec5f24ce
|
@ -441,10 +441,12 @@ void CheckCondition::multiConditionError(const Token *tok, unsigned int line1)
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Detect oppositing inner and outer conditions
|
// - Opposite inner conditions => always false
|
||||||
|
// - (TODO) Same/Overlapping inner condition => always true
|
||||||
|
// - same condition after early exit => always false
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void CheckCondition::oppositeInnerCondition()
|
void CheckCondition::multiCondition2()
|
||||||
{
|
{
|
||||||
if (!_settings->isEnabled(Settings::WARNING))
|
if (!_settings->isEnabled(Settings::WARNING))
|
||||||
return;
|
return;
|
||||||
|
@ -496,9 +498,19 @@ void CheckCondition::oppositeInnerCondition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse until inner condition is reached..
|
// parse until second condition is reached..
|
||||||
|
enum MULTICONDITIONTYPE { INNER, AFTER } type;
|
||||||
|
const Token *tok;
|
||||||
|
if (Token::Match(scope->classStart, "{ return|throw|continue|break")) {
|
||||||
|
tok = scope->classEnd->next();
|
||||||
|
type = MULTICONDITIONTYPE::AFTER;
|
||||||
|
} else {
|
||||||
|
tok = scope->classStart;
|
||||||
|
type = MULTICONDITIONTYPE::INNER;
|
||||||
|
}
|
||||||
|
const Token * const endToken = tok->scope()->classEnd;
|
||||||
const Token *ifToken = nullptr;
|
const Token *ifToken = nullptr;
|
||||||
for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
|
for (; tok && tok != endToken; tok = tok->next()) {
|
||||||
if (Token::simpleMatch(tok, "if (")) {
|
if (Token::simpleMatch(tok, "if (")) {
|
||||||
ifToken = tok;
|
ifToken = tok;
|
||||||
break;
|
break;
|
||||||
|
@ -539,8 +551,13 @@ void CheckCondition::oppositeInnerCondition()
|
||||||
// Condition..
|
// Condition..
|
||||||
const Token *cond2 = ifToken->next()->astOperand2();
|
const Token *cond2 = ifToken->next()->astOperand2();
|
||||||
|
|
||||||
|
if (type == MULTICONDITIONTYPE::INNER) {
|
||||||
if (isOppositeCond(false, _tokenizer->isCPP(), cond1, cond2, _settings->library, true))
|
if (isOppositeCond(false, _tokenizer->isCPP(), cond1, cond2, _settings->library, true))
|
||||||
oppositeInnerConditionError(cond1, cond2);
|
oppositeInnerConditionError(cond1, cond2);
|
||||||
|
} else {
|
||||||
|
if (isSameExpression(_tokenizer->isCPP(), true, cond1, cond2, _settings->library, true))
|
||||||
|
sameConditionAfterEarlyExitError(cond1, cond2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,6 +569,14 @@ void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token*
|
||||||
reportError(errorPath, Severity::warning, "oppositeInnerCondition", "Opposite inner 'if' condition leads to a dead code block.", CWE398, false);
|
reportError(errorPath, Severity::warning, "oppositeInnerCondition", "Opposite inner 'if' condition leads to a dead code block.", CWE398, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckCondition::sameConditionAfterEarlyExitError(const Token *cond1, const Token* cond2)
|
||||||
|
{
|
||||||
|
ErrorPath errorPath;
|
||||||
|
errorPath.push_back(ErrorPathItem(cond1, "first condition"));
|
||||||
|
errorPath.push_back(ErrorPathItem(cond2, "second condition"));
|
||||||
|
reportError(errorPath, Severity::warning, "sameConditionAfterEarlyExit", "Same condition, second condition is always false", CWE398, false);
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// if ((x != 1) || (x != 3)) // expression always true
|
// if ((x != 1) || (x != 3)) // expression always true
|
||||||
// if ((x == 1) && (x == 3)) // expression always false
|
// if ((x == 1) && (x == 3)) // expression always false
|
||||||
|
|
|
@ -55,7 +55,7 @@ public:
|
||||||
CheckCondition checkCondition(tokenizer, settings, errorLogger);
|
CheckCondition checkCondition(tokenizer, settings, errorLogger);
|
||||||
checkCondition.multiCondition();
|
checkCondition.multiCondition();
|
||||||
checkCondition.clarifyCondition(); // not simplified because ifAssign
|
checkCondition.clarifyCondition(); // not simplified because ifAssign
|
||||||
checkCondition.oppositeInnerCondition();
|
checkCondition.multiCondition2();
|
||||||
checkCondition.checkIncorrectLogicOperator();
|
checkCondition.checkIncorrectLogicOperator();
|
||||||
checkCondition.checkInvalidTestForOverflow();
|
checkCondition.checkInvalidTestForOverflow();
|
||||||
checkCondition.alwaysTrueFalse();
|
checkCondition.alwaysTrueFalse();
|
||||||
|
@ -90,8 +90,13 @@ public:
|
||||||
/** match 'if' and 'else if' conditions */
|
/** match 'if' and 'else if' conditions */
|
||||||
void multiCondition();
|
void multiCondition();
|
||||||
|
|
||||||
/** To check the dead code in a program, which is inaccessible due to the counter-conditions check in nested-if statements **/
|
/**
|
||||||
void oppositeInnerCondition();
|
* multiconditions #2
|
||||||
|
* - Opposite inner conditions => always false
|
||||||
|
* - (TODO) Same/Overlapping inner condition => always true
|
||||||
|
* - same condition after early exit => always false
|
||||||
|
**/
|
||||||
|
void multiCondition2();
|
||||||
|
|
||||||
/** @brief %Check for testing for mutual exclusion over ||*/
|
/** @brief %Check for testing for mutual exclusion over ||*/
|
||||||
void checkIncorrectLogicOperator();
|
void checkIncorrectLogicOperator();
|
||||||
|
@ -124,6 +129,8 @@ private:
|
||||||
|
|
||||||
void oppositeInnerConditionError(const Token *tok1, const Token* tok2);
|
void oppositeInnerConditionError(const Token *tok1, const Token* tok2);
|
||||||
|
|
||||||
|
void sameConditionAfterEarlyExitError(const Token *cond1, const Token *cond2);
|
||||||
|
|
||||||
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive);
|
void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive);
|
||||||
void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive);
|
void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive);
|
||||||
|
|
||||||
|
@ -144,6 +151,7 @@ private:
|
||||||
c.multiConditionError(nullptr,1);
|
c.multiConditionError(nullptr,1);
|
||||||
c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1);
|
c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1);
|
||||||
c.oppositeInnerConditionError(nullptr, nullptr);
|
c.oppositeInnerConditionError(nullptr, nullptr);
|
||||||
|
c.sameConditionAfterEarlyExitError(nullptr, nullptr);
|
||||||
c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false);
|
c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false);
|
||||||
c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false);
|
c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false);
|
||||||
c.moduloAlwaysTrueFalseError(nullptr, "1");
|
c.moduloAlwaysTrueFalseError(nullptr, "1");
|
||||||
|
@ -163,7 +171,8 @@ private:
|
||||||
"- Detect usage of | where & should be used\n"
|
"- Detect usage of | where & should be used\n"
|
||||||
"- Detect matching 'if' and 'else if' conditions\n"
|
"- Detect matching 'if' and 'else if' conditions\n"
|
||||||
"- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n"
|
"- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n"
|
||||||
"- Find dead code which is inaccessible due to the counter-conditions check in nested if statements\n"
|
"- Opposite inner condition is always false\n"
|
||||||
|
"- Same condition after early exit is always false\n"
|
||||||
"- Condition that is always true/false\n"
|
"- Condition that is always true/false\n"
|
||||||
"- Mutual exclusion over || always evaluating to true\n"
|
"- Mutual exclusion over || always evaluating to true\n"
|
||||||
"- Comparisons of modulo results that are always true/false.\n"
|
"- Comparisons of modulo results that are always true/false.\n"
|
||||||
|
|
|
@ -79,6 +79,8 @@ private:
|
||||||
TEST_CASE(oppositeInnerCondition2);
|
TEST_CASE(oppositeInnerCondition2);
|
||||||
TEST_CASE(oppositeInnerConditionAnd);
|
TEST_CASE(oppositeInnerConditionAnd);
|
||||||
|
|
||||||
|
TEST_CASE(sameConditionAfterEarlyExit);
|
||||||
|
|
||||||
TEST_CASE(clarifyCondition1); // if (a = b() < 0)
|
TEST_CASE(clarifyCondition1); // if (a = b() < 0)
|
||||||
TEST_CASE(clarifyCondition2); // if (a & b == c)
|
TEST_CASE(clarifyCondition2); // if (a & b == c)
|
||||||
TEST_CASE(clarifyCondition3); // if (! a & b)
|
TEST_CASE(clarifyCondition3); // if (! a & b)
|
||||||
|
@ -1709,6 +1711,14 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sameConditionAfterEarlyExit() {
|
||||||
|
check("void f(int x) {\n"
|
||||||
|
" if (x > 100) { return; }\n"
|
||||||
|
" if (x > 100) {}\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Same condition, second condition is always false\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
// clarify conditions with = and comparison
|
// clarify conditions with = and comparison
|
||||||
void clarifyCondition1() {
|
void clarifyCondition1() {
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
|
|
Loading…
Reference in New Issue