From 4b9f3c68fb1e2a1bfd440d6be9cefc2c3070ff5f Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:11:12 +0100 Subject: [PATCH] Fix #12188 FN uninitvar with increment of struct member (#5665) --- lib/astutils.cpp | 19 +++++++++++++++++++ lib/astutils.h | 2 ++ lib/checkuninitvar.cpp | 12 ------------ test/testuninitvar.cpp | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 7af2b427f..c1025da34 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -3265,6 +3265,18 @@ static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Settings return ExprUsage::Inconclusive; } +bool isLeafDot(const Token* tok) +{ + if (!tok) + return false; + const Token * parent = tok->astParent(); + if (!Token::simpleMatch(parent, ".")) + return false; + if (parent->astOperand2() == tok && !Token::simpleMatch(parent->astParent(), ".")) + return true; + return isLeafDot(parent); +} + ExprUsage getExprUsage(const Token* tok, int indirect, const Settings* settings, bool cpp) { const Token* parent = tok->astParent(); @@ -3285,6 +3297,13 @@ ExprUsage getExprUsage(const Token* tok, int indirect, const Settings* settings, !parent->isUnaryOp("&") && !(astIsRHS(tok) && isLikelyStreamRead(cpp, parent))) return ExprUsage::Used; + if (isLeafDot(tok)) { + const Token* op = parent->astParent(); + while (Token::simpleMatch(op, ".")) + op = op->astParent(); + if (Token::Match(op, "%assign%|++|--") && op->str() != "=") + return ExprUsage::Used; + } if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) { const Token* const lhs = parent->astOperand1(); if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs == lhs->variable()->nameToken()) diff --git a/lib/astutils.h b/lib/astutils.h index c5fe9e095..b4a7be82c 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -418,6 +418,8 @@ bool isCPPCast(const Token* tok); bool isConstVarExpression(const Token* tok, std::function skipPredicate = nullptr); +bool isLeafDot(const Token* tok); + enum class ExprUsage { None, NotUsed, PassedByReference, Used, Inconclusive }; ExprUsage getExprUsage(const Token* tok, int indirect, const Settings* settings, bool cpp); diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index e0c8b7db6..94d64d275 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1596,18 +1596,6 @@ void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } -static bool isLeafDot(const Token* tok) -{ - if (!tok) - return false; - const Token * parent = tok->astParent(); - if (!Token::simpleMatch(parent, ".")) - return false; - if (parent->astOperand2() == tok && !Token::simpleMatch(parent->astParent(), ".")) - return true; - return isLeafDot(parent); -} - void CheckUninitVar::valueFlowUninit() { logChecker("CheckUninitVar::valueFlowUninit"); diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 0bd66b5cb..37b2100f5 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -7215,6 +7215,42 @@ private: " foo(&my_st);\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + valueFlowUninit("struct S {\n" // #12188 + " int i;\n" + " struct T { int j; } t;\n" + "};\n" + "void f() {\n" + " S s;\n" + " ++s.i;\n" + "}\n" + "void g() {\n" + " S s;\n" + " s.i--;\n" + "}\n" + "void h() {\n" + " S s;\n" + " s.i &= 3;\n" + "}\n" + "void k() {\n" + " S s;\n" + " if (++s.i < 3) {}\n" + "}\n" + "void m() {\n" + " S s;\n" + " ++s.t.j;\n" + "}\n" + "void n() {\n" + " S s;\n" + " if (s.t.j-- < 3) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: s.i\n" + "[test.cpp:11]: (error) Uninitialized variable: s.i\n" + "[test.cpp:15]: (error) Uninitialized variable: s.i\n" + "[test.cpp:19]: (error) Uninitialized variable: s.i\n" + "[test.cpp:23]: (error) Uninitialized variable: s.t.j\n" + "[test.cpp:27]: (error) Uninitialized variable: s.t.j\n", + errout.str()); } void uninitvar_memberfunction() {