Fixed #6560 (ValueFlow: handling ternary operator better in valueFlowSubFunction)

This commit is contained in:
Daniel Marjamäki 2015-05-24 17:02:00 +02:00
parent 7f15873759
commit eb0db322eb
5 changed files with 67 additions and 14 deletions

View File

@ -1135,18 +1135,21 @@ bool Token::isCalculation() const
return true;
}
static bool isUnaryPreOp(const Token *op)
bool Token::isUnaryPreOp() const
{
if (!op->astOperand1() || op->astOperand2())
if (!astOperand1() || astOperand2())
return false;
if (!Token::Match(op, "++|--"))
if (!Token::Match(this, "++|--"))
return true;
const Token *tok = op->astOperand1();
const Token *tokbefore = _previous;
const Token *tokafter = _next;
for (int distance = 1; distance < 10; distance++) {
if (tok == op->tokAt(-distance))
if (tokbefore == _astOperand1)
return false;
if (tok == op->tokAt(distance))
if (tokafter == _astOperand1)
return true;
tokbefore = tokbefore->_previous;
tokafter = tokafter->_previous;
}
return false; // <- guess
}
@ -1158,7 +1161,7 @@ std::string Token::expressionString() const
while (start->astOperand1() && start->astOperand2())
start = start->astOperand1();
const Token *end = top;
while (end->astOperand1() && (end->astOperand2() || isUnaryPreOp(end))) {
while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) {
if (Token::Match(end,"(|[")) {
end = end->link();
break;

View File

@ -276,6 +276,7 @@ public:
bool isBoolean() const {
return _type == eBoolean;
}
bool isUnaryPreOp() const;
unsigned int flags() const {
return _flags;

View File

@ -147,16 +147,20 @@ static std::map<unsigned int, MathLib::bigint> getProgramMemory(const Token *tok
const std::map<unsigned int, MathLib::bigint> programMemory1(programMemory);
int indentlevel = 0;
for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) {
if (Token::Match(tok2, "[;{}] %var% = %num% ;")) {
const Token *vartok = tok2->next();
const Token *numtok = tok2->tokAt(3);
if (programMemory.find(vartok->varId()) == programMemory.end())
programMemory[vartok->varId()] = MathLib::toLongNumber(numtok->str());
}
if (Token::Match(tok2, "[;{}] %varid% = %var% ;", varid)) {
const Token *vartok = tok2->tokAt(3);
programMemory[vartok->varId()] = value.intvalue;
}
if (Token::Match(tok2, "[;{}] %var% =")) {
const Token *vartok = tok2->next();
if (programMemory.find(vartok->varId()) == programMemory.end()) {
MathLib::bigint result = 0;
bool error = false;
execute(tok2->tokAt(2)->astOperand2(), &programMemory, &result, &error);
if (!error)
programMemory[vartok->varId()] = result;
}
}
if (tok2->str() == "{") {
if (indentlevel <= 0)
break;
@ -761,6 +765,16 @@ static void removeValues(std::list<ValueFlow::Value> &values, const std::list<Va
}
}
static void valueFlowAST(Token *tok, unsigned int varid, const ValueFlow::Value &value)
{
if (!tok)
return;
if (tok->varId() == varid)
setTokenValue(tok, value);
valueFlowAST(const_cast<Token*>(tok->astOperand1()), varid, value);
valueFlowAST(const_cast<Token*>(tok->astOperand2()), varid, value);
}
static bool valueFlowForward(Token * const startToken,
const Token * const endToken,
const Variable * const var,
@ -1026,6 +1040,31 @@ static bool valueFlowForward(Token * const startToken,
else if (returnStatement && tok2->str() == ";")
return false;
// If a ? is seen and it's known that the condition is true/false..
else if (tok2->str() == "?") {
const Token *condition = tok2->astOperand1();
std::list<ValueFlow::Value>::const_iterator it;
for (it = values.begin(); it != values.end(); ++it) {
const std::map<unsigned int, MathLib::bigint> programMemory(getProgramMemory(tok2, varid, *it));
if (conditionIsTrue(condition, programMemory))
valueFlowAST(const_cast<Token*>(tok2->astOperand2()->astOperand1()), varid, *it);
else if (conditionIsFalse(condition, programMemory))
valueFlowAST(const_cast<Token*>(tok2->astOperand2()->astOperand2()), varid, *it);
else
valueFlowAST(const_cast<Token*>(tok2->astOperand2()), varid, *it);
}
// Skip conditional expressions..
while (tok2->astOperand1() || tok2->astOperand2()) {
if (tok2->astOperand2())
tok2 = const_cast<Token*>(tok2->astOperand2());
else if (tok2->isUnaryPreOp())
tok2 = const_cast<Token*>(tok2->astOperand1());
else
break;
}
tok2 = tok2->next();
}
if (tok2->varId() == varid) {
// bailout: assignment
if (Token::Match(tok2->previous(), "!!* %name% %op%") && tok2->next()->isAssignmentOp()) {

View File

@ -2373,7 +2373,7 @@ private:
check("void f(int *p = 0) {\n"
" std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0;
"}");
TODO_ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", "", errout.str()); // Check the first branch of ternary
ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // Check the first branch of ternary
check("void f(char *p = 0) {\n"
" std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0;

View File

@ -1449,6 +1449,16 @@ private:
" leaveNotifyEvent(0);\n"
"}";
testValueOfX(code, 2U, 2); // No complaint about Token::Match called with varid 0. (#6443)
// #6560 - multivariables
code = "void f1(int x) {\n"
" int a = x && y;\n"
" int b = a ? x : 0;\n"
"}\n"
"void f2() {\n"
" f1(0);\n"
"}";
ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
}
void valueFlowFunctionReturn() {