From 37d373999fe38c0bebe130712a17cd94a3f73ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 12 May 2021 21:26:15 +0200 Subject: [PATCH] switch in forwardanalyzer --- lib/analyzer.h | 2 ++ lib/forwardanalyzer.cpp | 25 +++++++++++++++++++++++++ lib/valueflow.cpp | 12 ++++++++++++ test/testvalueflow.cpp | 19 ++++++++++++++++++- 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/analyzer.h b/lib/analyzer.h index 8d0f5be7d..566ea1c31 100644 --- a/lib/analyzer.h +++ b/lib/analyzer.h @@ -147,6 +147,8 @@ struct Analyzer { virtual void forkScope(const Token* /*endBlock*/) {} /// If the value is conditional virtual bool isConditional() const = 0; + /// If the value is known + virtual bool isKnown() const = 0; /// The condition that will be assumed during analysis virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0; /// Return analyzer for expression at token diff --git a/lib/forwardanalyzer.cpp b/lib/forwardanalyzer.cpp index b24069b55..ad7aaa710 100644 --- a/lib/forwardanalyzer.cpp +++ b/lib/forwardanalyzer.cpp @@ -652,6 +652,31 @@ struct ForwardTraversal { } else if (Token::simpleMatch(tok, "switch (")) { if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) return Break(); + if (!analyzer->isKnown()) + return Break(); + Token *bodyStart = tok->linkAt(1)->next(); + const Token *bodyEnd = bodyStart->link(); + bool known = true; + for (Token *bodyTok = bodyStart->next(); bodyTok != bodyEnd; bodyTok = bodyTok->next()) { + if (bodyTok->str() == "{") + bodyTok = bodyTok->link(); + known |= Token::Match(bodyTok, "return|throw|break"); + if (!Token::Match(bodyTok, "case %any% :") && !Token::simpleMatch(bodyTok, "default :")) + continue; + while (Token::Match(bodyTok, "case %any% :") || Token::simpleMatch(bodyTok, "default :")) { + bodyTok = bodyTok->tokAt(2); + while (Token::Match(bodyTok, "[;:]")) + bodyTok = bodyTok->next(); + } + + ForwardTraversal ft = *this; + ft.analyzer->forkScope(bodyEnd); + if (!known) + ft.analyzer->lowerToPossible(); + ft.updateRange(bodyTok, bodyEnd, depth - 1); + known = false; + bodyTok = bodyTok->previous(); + } return Break(); } else { if (updateTok(tok, &next) == Progress::Break) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index cadf8a6bb..5d86fa500 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -2319,6 +2319,10 @@ struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { return false; } + virtual bool isKnown() const OVERRIDE { + return value.isKnown(); + } + virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE { const Scope* scope = endBlock->scope(); if (!scope) @@ -5305,6 +5309,14 @@ struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { return false; } + virtual bool isKnown() const OVERRIDE { + for (auto&& p:values) { + if (!p.second.isKnown()) + return false; + } + return !values.empty(); + } + virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE { const Scope* scope = endBlock->scope(); if (!scope) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 52b351282..5f1fc52ba 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -92,6 +92,7 @@ private: TEST_CASE(valueFlowForwardFunction); TEST_CASE(valueFlowForwardTernary); TEST_CASE(valueFlowForwardLambda); + TEST_CASE(valueFlowForwardSwitch); TEST_CASE(valueFlowForwardTryCatch); TEST_CASE(valueFlowForwardInconclusiveImpossible); @@ -3028,6 +3029,22 @@ private: TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 3)); } + + void valueFlowForwardSwitch() { + const char *code; + code = "void f(int var) {\n" + " int x = 123;\n" + " switch (var) {\n" + " case 1:\n" + " return x;\n" + " case 2:\n" + " return x;\n" + " }\n" + "}"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 123)); + ASSERT_EQUALS(true, testValueOfXKnown(code, 7U, 123)); + } + void valueFlowForwardTryCatch() { const char *code; @@ -4378,7 +4395,7 @@ private: " return x;\n" "}\n"; values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT); - ASSERT_EQUALS(0, values.size()); + ASSERT(values.size()); } void valueFlowConditionExpressions() {