Skip to content

Commit 5d55622

Browse files
authored
ValueFlow: Assume constant is nonzero when its negated (#4041)
* ValueFlow: Assume constant is nonzero when its negated * Format * Format
1 parent 4eed295 commit 5d55622

2 files changed

Lines changed: 98 additions & 71 deletions

File tree

lib/valueflow.cpp

Lines changed: 87 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,77 @@ const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, Val
275275
});
276276
}
277277

278+
static bool isInConstructorList(const Token* tok)
279+
{
280+
if (!tok)
281+
return false;
282+
if (!astIsRHS(tok))
283+
return false;
284+
const Token* parent = tok->astParent();
285+
if (!Token::Match(parent, "{|("))
286+
return false;
287+
if (!Token::Match(parent->previous(), "%var% {|("))
288+
return false;
289+
if (!parent->astOperand1() || !parent->astOperand2())
290+
return false;
291+
do {
292+
parent = parent->astParent();
293+
} while (Token::simpleMatch(parent, ","));
294+
return Token::simpleMatch(parent, ":") && !Token::simpleMatch(parent->astParent(), "?");
295+
}
296+
297+
static std::vector<ValueType> getParentValueTypes(const Token* tok,
298+
const Settings* settings = nullptr,
299+
const Token** parent = nullptr)
300+
{
301+
if (!tok)
302+
return {};
303+
if (!tok->astParent())
304+
return {};
305+
if (isInConstructorList(tok)) {
306+
if (parent)
307+
*parent = tok->astParent()->astOperand1();
308+
if (tok->astParent()->astOperand1()->valueType())
309+
return {*tok->astParent()->astOperand1()->valueType()};
310+
return {};
311+
} else if (Token::Match(tok->astParent(), "(|{|,")) {
312+
int argn = -1;
313+
const Token* ftok = getTokenArgumentFunction(tok, argn);
314+
if (ftok && ftok->function()) {
315+
std::vector<ValueType> result;
316+
std::vector<const Variable*> argsVars = getArgumentVars(ftok, argn);
317+
const Token* nameTok = nullptr;
318+
for (const Variable* var : getArgumentVars(ftok, argn)) {
319+
if (!var)
320+
continue;
321+
if (!var->valueType())
322+
continue;
323+
nameTok = var->nameToken();
324+
result.push_back(*var->valueType());
325+
}
326+
if (result.size() == 1 && nameTok && parent) {
327+
*parent = nameTok;
328+
}
329+
return result;
330+
}
331+
}
332+
if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") &&
333+
astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) {
334+
const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1();
335+
const ValueType* vtCont = contTok->valueType();
336+
if (!vtCont->containerTypeToken)
337+
return {};
338+
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings);
339+
return {std::move(vtParent)};
340+
}
341+
if (Token::Match(tok->astParent(), "return|(|{|%assign%") && parent) {
342+
*parent = tok->astParent();
343+
}
344+
if (tok->astParent()->valueType())
345+
return {*tok->astParent()->valueType()};
346+
return {};
347+
}
348+
278349
static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false)
279350
{
280351
if (!Token::simpleMatch(tok, "{"))
@@ -1547,6 +1618,17 @@ static std::vector<MathLib::bigint> minUnsignedValue(const Token* tok, int depth
15471618
return result;
15481619
}
15491620

1621+
static bool isConvertedToIntegral(const Token* tok, const Settings* settings)
1622+
{
1623+
if (!tok)
1624+
return false;
1625+
std::vector<ValueType> parentTypes = getParentValueTypes(tok, settings);
1626+
if (parentTypes.empty())
1627+
return false;
1628+
const ValueType& vt = parentTypes.front();
1629+
return vt.type != ValueType::UNKNOWN_INT && vt.isIntegral();
1630+
}
1631+
15501632
static void valueFlowImpossibleValues(TokenList* tokenList, const Settings* settings)
15511633
{
15521634
for (Token* tok = tokenList->front(); tok; tok = tok->next()) {
@@ -1602,6 +1684,11 @@ static void valueFlowImpossibleValues(TokenList* tokenList, const Settings* sett
16021684
ValueFlow::Value value{0};
16031685
value.setImpossible();
16041686
setTokenValue(tok, value, settings);
1687+
} else if (tok->isIncompleteVar() && tok->astParent() && tok->astParent()->isUnaryOp("-") &&
1688+
isConvertedToIntegral(tok->astParent(), settings)) {
1689+
ValueFlow::Value value{0};
1690+
value.setImpossible();
1691+
setTokenValue(tok, value, settings);
16051692
}
16061693
}
16071694
}
@@ -3359,77 +3446,6 @@ static bool isDifferentType(const Token* src, const Token* dst)
33593446
return false;
33603447
}
33613448

3362-
static bool isInConstructorList(const Token* tok)
3363-
{
3364-
if (!tok)
3365-
return false;
3366-
if (!astIsRHS(tok))
3367-
return false;
3368-
const Token* parent = tok->astParent();
3369-
if (!Token::Match(parent, "{|("))
3370-
return false;
3371-
if (!Token::Match(parent->previous(), "%var% {|("))
3372-
return false;
3373-
if (!parent->astOperand1() || !parent->astOperand2())
3374-
return false;
3375-
do {
3376-
parent = parent->astParent();
3377-
} while (Token::simpleMatch(parent, ","));
3378-
return Token::simpleMatch(parent, ":") && !Token::simpleMatch(parent->astParent(), "?");
3379-
}
3380-
3381-
static std::vector<ValueType> getParentValueTypes(const Token* tok,
3382-
const Settings* settings = nullptr,
3383-
const Token** parent = nullptr)
3384-
{
3385-
if (!tok)
3386-
return {};
3387-
if (!tok->astParent())
3388-
return {};
3389-
if (isInConstructorList(tok)) {
3390-
if (parent)
3391-
*parent = tok->astParent()->astOperand1();
3392-
if (tok->astParent()->astOperand1()->valueType())
3393-
return {*tok->astParent()->astOperand1()->valueType()};
3394-
return {};
3395-
} else if (Token::Match(tok->astParent(), "(|{|,")) {
3396-
int argn = -1;
3397-
const Token* ftok = getTokenArgumentFunction(tok, argn);
3398-
if (ftok && ftok->function()) {
3399-
std::vector<ValueType> result;
3400-
std::vector<const Variable*> argsVars = getArgumentVars(ftok, argn);
3401-
const Token* nameTok = nullptr;
3402-
for (const Variable* var : getArgumentVars(ftok, argn)) {
3403-
if (!var)
3404-
continue;
3405-
if (!var->valueType())
3406-
continue;
3407-
nameTok = var->nameToken();
3408-
result.push_back(*var->valueType());
3409-
}
3410-
if (result.size() == 1 && nameTok && parent) {
3411-
*parent = nameTok;
3412-
}
3413-
return result;
3414-
}
3415-
}
3416-
if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") &&
3417-
astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) {
3418-
const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1();
3419-
const ValueType* vtCont = contTok->valueType();
3420-
if (!vtCont->containerTypeToken)
3421-
return {};
3422-
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings);
3423-
return {std::move(vtParent)};
3424-
}
3425-
if (Token::Match(tok->astParent(), "return|(|{|%assign%") && parent) {
3426-
*parent = tok->astParent();
3427-
}
3428-
if (tok->astParent()->valueType())
3429-
return {*tok->astParent()->valueType()};
3430-
return {};
3431-
}
3432-
34333449
bool isLifetimeBorrowed(const Token *tok, const Settings *settings)
34343450
{
34353451
if (!tok)

test/testvalueflow.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5065,6 +5065,17 @@ class TestValueFlow : public TestFixture {
50655065
"}\n";
50665066
values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT);
50675067
ASSERT_EQUALS(0, values.size());
5068+
5069+
code = "void f() {\n"
5070+
" int i;\n"
5071+
" if (x) {\n"
5072+
" int y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated
5073+
" if (y != 0) return;\n"
5074+
" i++;\n"
5075+
" }\n"
5076+
"}\n";
5077+
values = tokenValues(code, "i ++", ValueFlow::Value::ValueType::UNINIT);
5078+
ASSERT_EQUALS(0, values.size());
50685079
}
50695080

50705081
void valueFlowConditionExpressions() {

0 commit comments

Comments
 (0)