From 1de6af8ad5b307ee09e6b715c3a9a6235ede3727 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Thu, 2 May 2024 10:19:22 +0200 Subject: [PATCH] Fix #12685 FN uninitvar for constructor argument (#6368) --- lib/astutils.cpp | 32 ++++++++++++++++++++++++-------- test/testuninitvar.cpp | 22 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 486eb730e4a..afc5ef81ac1 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -3295,15 +3295,36 @@ static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Settings const Token* ftok = getTokenArgumentFunction(tok, argnr); if (!ftok) return ExprUsage::None; - if (ftok->function()) { + const Function* func = ftok->function(); + // variable init/constructor call? + if (!func && ftok->variable() && ftok == ftok->variable()->nameToken()) { + // STL types or containers don't initialize external variables + if (ftok->variable()->isStlType() || (ftok->variable()->valueType() && ftok->variable()->valueType()->container)) + return ExprUsage::Used; + // TODO: resolve multiple constructors + if (ftok->variable()->type() && ftok->variable()->type()->classScope) { + const int nCtor = ftok->variable()->type()->classScope->numConstructors; + if (nCtor == 0) + return ExprUsage::Used; + if (nCtor == 1) { + const Scope* scope = ftok->variable()->type()->classScope; + auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [](const Function& f) { + return f.isConstructor(); + }); + if (it != scope->functionList.end()) + func = &*it; + } + } + } + if (func) { std::vector args = getArgumentVars(ftok, argnr); for (const Variable* arg : args) { if (!arg) continue; if (arg->isReference() || (arg->isPointer() && indirect == 1)) { - if (!ftok->function()->hasBody()) + if (!func->hasBody()) return ExprUsage::PassedByReference; - for (const Token* bodytok = ftok->function()->functionScope->bodyStart; bodytok != ftok->function()->functionScope->bodyEnd; bodytok = bodytok->next()) { + for (const Token* bodytok = func->functionScope->bodyStart; bodytok != func->functionScope->bodyEnd; bodytok = bodytok->next()) { if (bodytok->variable() == arg) { if (arg->isReference()) return ExprUsage::PassedByReference; @@ -3321,11 +3342,6 @@ static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Settings return ExprUsage::Used; } else if (ftok->str() == "{") { return indirect == 0 ? ExprUsage::Used : ExprUsage::Inconclusive; - } else if (ftok->variable() && ftok == ftok->variable()->nameToken()) { // variable init/constructor call - if (ftok->variable()->type() && ftok->variable()->type()->classScope && ftok->variable()->type()->classScope->numConstructors == 0) - return ExprUsage::Used; - if (ftok->variable()->isStlType() || (ftok->variable()->valueType() && ftok->variable()->valueType()->container)) // STL types or containers don't initialize external variables - return ExprUsage::Used; } else { const bool isnullbad = settings.library.isnullargbad(ftok, argnr + 1); if (indirect == 0 && astIsPointer(tok) && !addressOf && isnullbad) diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 98359185b0d..08335e8cbeb 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -7438,7 +7438,7 @@ class TestUninitVar : public TestFixture { "[test.cpp:27]: (error) Uninitialized variable: s.t.j\n", errout_str()); - valueFlowUninit("struct S { int x; };\n" + valueFlowUninit("struct S { int x; };\n" // #6933 "void f() {\n" " int i;\n" " S s(i);\n" @@ -7525,6 +7525,26 @@ class TestUninitVar : public TestFixture { " return s2;\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + valueFlowUninit("struct S {\n" // #12685 + " explicit S(double v);\n" + " double m;\n" + "};\n" + "void f() {\n" + " double d;\n" + " S s(d);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: d\n", errout_str()); + + valueFlowUninit("struct S {\n" + " explicit S(double v) : m(v) {}\n" + " double m;\n" + "};\n" + "void f() {\n" + " double d;\n" + " S s{ d };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Uninitialized variable: d\n", errout_str()); } void uninitvar_memberfunction() {