From 8adfcc848c335b170af95d22888d052b659f504e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 21 May 2021 14:30:47 +0200 Subject: [PATCH] Uninitialized variables; check RHS expression --- lib/checkuninitvar.cpp | 64 +++++++++++++++++++++++++++++++++++++----- lib/checkuninitvar.h | 1 + test/testuninitvar.cpp | 6 ++-- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 51cb60853..59f66acf9 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -775,8 +775,25 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var } else { - if (tok->strAt(1) == "=") - checkRhs(tok, var, *alloc, number_of_if, emptyString); + const Token *parent = tok; + while (parent->astParent() && ((astIsLHS(parent) && parent->astParent()->str() == "[") || parent->astParent()->isUnaryOp("*"))) { + parent = parent->astParent(); + if (parent->str() == "[") { + if (const Token *errorToken = checkExpr(parent->astOperand2(), var, *alloc, number_of_if==0)) { + if (!suppressErrors) + uninitvarError(errorToken, errorToken->expressionString(), *alloc); + return true; + } + } + } + if (Token::simpleMatch(parent->astParent(), "=") && astIsLHS(parent)) { + const Token *eq = parent->astParent(); + if (const Token *errorToken = checkExpr(eq->astOperand2(), var, *alloc, number_of_if==0)) { + if (!suppressErrors) + uninitvarError(errorToken, errorToken->expressionString(), *alloc); + return true; + } + } // assume that variable is assigned return true; @@ -788,6 +805,32 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var return false; } +const Token *CheckUninitVar::checkExpr(const Token *tok, const Variable& var, const Alloc alloc, bool known, bool *bailout) +{ + if (!tok) + return nullptr; + if (tok->astOperand1()) { + bool bailout1 = false; + const Token *errorToken = checkExpr(tok->astOperand1(), var, alloc, known, &bailout1); + if (bailout && bailout1) + *bailout = true; + if (errorToken) + return errorToken; + if ((bailout1 || !known) && Token::Match(tok, "%oror%|&&|?")) + return nullptr; + } + if (tok->astOperand2()) + return checkExpr(tok->astOperand2(), var, alloc, known, bailout); + if (tok->varId() == var.declarationId()) { + const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc); + if (errorToken) + return errorToken; + else if (bailout) + *bailout = true; + } + return nullptr; +} + bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar) { const Token * const endpar = startparentheses->link(); @@ -1059,16 +1102,23 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, int deref = 0; derefValue = valueExpr; while (Token::Match(derefValue->astParent(), "+|-|*|[|.") || (derefValue->astParent() && derefValue->astParent()->isCast())) { - if (derefValue->astParent()->isUnaryOp("*")) - ++deref; - else if (derefValue->astParent()->str() == "[") { + const Token * const derefValueParent = derefValue->astParent(); + if (derefValueParent->str() == "*") { + if (derefValueParent->isUnaryOp("*")) + ++deref; + else + break; + } else if (derefValueParent->str() == "[") { if (astIsLhs(derefValue)) ++deref; else break; - } else if (derefValue->astParent()->str() == ".") + } else if (Token::Match(derefValueParent, "[+-]")) { + if (derefValueParent->valueType() && derefValueParent->valueType()->pointer == 0) + break; + } else if (derefValueParent->str() == ".") ++deref; - derefValue = derefValue->astParent(); + derefValue = derefValueParent; if (deref < arrayDim) valueExpr = derefValue; } diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index 42232549f..bdc684631 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -79,6 +79,7 @@ public: void checkStruct(const Token *tok, const Variable &structvar); enum Alloc { NO_ALLOC, NO_CTOR_CALL, CTOR_CALL, ARRAY }; bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, Alloc* const alloc, const std::string &membervar, std::map variableValue); + const Token *checkExpr(const Token *tok, const Variable& var, const Alloc alloc, bool known, bool *bailout=nullptr); bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar); bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors); const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const; diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index e08439fbb..470b708cc 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -624,7 +624,7 @@ private: " int a[10];\n" " a[0] = 10 - a[1];\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a[1]\n", errout.str()); // goto/setjmp/longjmp.. checkUninitVar("void foo(int x)\n" @@ -1495,7 +1495,7 @@ private: " char a[10];\n" " a[a[0]] = 0;\n" "}"); - TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: a[0]\n", errout.str()); checkUninitVar("int f()\n" "{\n" @@ -4944,7 +4944,7 @@ private: " if (c->x() == 4) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Uninitialized variable: c\n", errout.str()); - + valueFlowUninit("struct A { \n" " int i; \n" " void f();\n"