From 141ce7cd63ea8e61de682f7c81a0822f820ce131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 31 Dec 2018 17:05:46 +0100 Subject: [PATCH] ValueFlow: Use FwdAnalysisAllPaths in ValueFlow to track complex expressions --- lib/astutils.cpp | 14 ++++++++++++-- lib/astutils.h | 5 ++++- lib/valueflow.cpp | 28 ++++++++++++++++++++++++++++ lib/valueflow.h | 2 +- test/testvalueflow.cpp | 18 ++++++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index c28de7159..e74c43005 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1165,11 +1165,14 @@ struct FwdAnalysisAllPaths::Result FwdAnalysisAllPaths::checkRecursive(const Tok if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { const Token *parent = tok; bool other = false; - bool same = false; + bool same = tok->astParent() && isSameExpression(mCpp, false, expr, tok, mLibrary, false, false, nullptr); while (Token::Match(parent->astParent(), "*|.|::|[")) { parent = parent->astParent(); - if (parent && isSameExpression(mCpp, false, expr, parent->astOperand1(), mLibrary, false, false, nullptr)) + if (parent && isSameExpression(mCpp, false, expr, parent, mLibrary, false, false, nullptr)) { same = true; + if (mWhat == What::ValueFlow) + mValueFlow.push_back(parent); + } if (!same && Token::Match(parent, ". %var%") && parent->next()->varId() && exprVarIds.find(parent->next()->varId()) == exprVarIds.end()) { other = true; break; @@ -1356,6 +1359,13 @@ bool FwdAnalysisAllPaths::unusedValue(const Token *expr, const Token *startToken return (result.type == FwdAnalysisAllPaths::Result::Type::NONE || result.type == FwdAnalysisAllPaths::Result::Type::RETURN) && !possiblyAliased(expr, startToken); } +std::vector FwdAnalysisAllPaths::valueFlow(const Token *expr, const Token *startToken, const Token *endToken) +{ + mWhat = What::ValueFlow; + check(expr, startToken, endToken); + return mValueFlow; +} + bool FwdAnalysisAllPaths::possiblyAliased(const Token *expr, const Token *startToken) const { if (expr->isUnaryOp("*")) diff --git a/lib/astutils.h b/lib/astutils.h index 21c789efd..ad6897de3 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -191,6 +191,8 @@ public: */ bool unusedValue(const Token *expr, const Token *startToken, const Token *endToken); + std::vector valueFlow(const Token *expr, const Token *startToken, const Token *endToken); + /** Is there some possible alias for given expression */ bool possiblyAliased(const Token *expr, const Token *startToken) const; @@ -213,7 +215,8 @@ private: const bool mCpp; const Library &mLibrary; - enum class What { Reassign, UnusedValue } mWhat; + enum class What { Reassign, UnusedValue, ValueFlow } mWhat; + std::vector mValueFlow; }; #endif // astutilsH diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index a430940d2..fb387c847 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -4654,6 +4654,33 @@ static void valueFlowContainerAfterCondition(TokenList *tokenlist, handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } +static void valueFlowFwdAnalysis(const TokenList *tokenlist, const Settings *settings) +{ + for (const Token *tok = tokenlist->front(); tok; tok = tok->next()) { + if (tok->str() != "=" || !tok->astOperand1() || !tok->astOperand2()) + continue; + if (!tok->scope()->isExecutable()) + continue; + if (!tok->astOperand2()->hasKnownIntValue()) + continue; + ValueFlow::Value v(tok->astOperand2()->values().front()); + v.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned value " + MathLib::toString(v.intvalue)); + FwdAnalysisAllPaths fwdAnalysis(tokenlist->isCPP(), settings->library); + const Token *startToken = tok->findExpressionStartEndTokens().second->next(); + const Scope *functionScope = tok->scope(); + while (functionScope->nestedIn && functionScope->nestedIn->isExecutable()) + functionScope = functionScope->nestedIn; + const Token *endToken = functionScope->bodyEnd; + for (const Token *tok2 : fwdAnalysis.valueFlow(tok->astOperand1(), startToken, endToken)) { + const Scope *s = tok2->scope(); + while (s && s != tok->scope()) + s = s->nestedIn; + v.valueKind = s ? ValueFlow::Value::ValueKind::Known : ValueFlow::Value::ValueKind::Possible; + setTokenValue(const_cast(tok2), v, settings); + } + } +} + ValueFlow::Value::Value(const Token *c, long long val) : valueType(INT), intvalue(val), @@ -4724,6 +4751,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowBitAnd(tokenlist); valueFlowSameExpressions(tokenlist); + valueFlowFwdAnalysis(tokenlist, settings); // Temporary hack.. run valueflow until there is nothing to update or timeout expires const std::time_t timeout = std::time(0) + TIMEOUT; diff --git a/lib/valueflow.h b/lib/valueflow.h index ae036b5bb..083377a7c 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -151,7 +151,7 @@ namespace ValueFlow { } /** How known is this value */ - enum ValueKind { + enum class ValueKind { /** This value is possible, other unlisted values may also be possible */ Possible, /** Only listed values are possible */ diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 0118fb5c2..ef6308a98 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -87,6 +87,8 @@ private: TEST_CASE(valueFlowForwardTernary); TEST_CASE(valueFlowForwardLambda); + TEST_CASE(valueFlowFwdAnalysis); + TEST_CASE(valueFlowSwitchVariable); TEST_CASE(valueFlowForLoop); @@ -2353,6 +2355,22 @@ private: ASSERT_EQUALS(true, testValueOfX(code,3U,0)); } + void valueFlowFwdAnalysis() { + const char *code; + std::list values; + + code = "void f() {\n" + " struct Foo foo;\n" + " foo.x = 1;\n" + " x = 0 + foo.x;\n" // <- foo.x is 1 + "}"; + values = tokenValues(code, "+"); + ASSERT_EQUALS(1U, values.size()); + ASSERT_EQUALS(true, values.front().isKnown()); + ASSERT_EQUALS(true, values.front().isIntValue()); + ASSERT_EQUALS(1, values.front().intvalue); + } + void valueFlowSwitchVariable() { const char *code; code = "void f(int x) {\n"