diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index ec3a9521b..5934dcd85 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2633,6 +2633,7 @@ namespace { // avoid one-definition-rule violation const Variable *var; const Token *tok; + std::vector initArgs; }; } @@ -2663,25 +2664,38 @@ void CheckClass::initializerListOrder() tok = tok->next(); // find all variable initializations in list - while (tok && tok != func->functionScope->bodyStart) { + for (; tok && tok != func->functionScope->bodyStart; tok = tok->next()) { if (Token::Match(tok, "%name% (|{")) { const Variable *var = scope->getVariable(tok->str()); if (var) vars.emplace_back(var, tok); + else + continue; - if (Token::Match(tok->tokAt(2), "%name% =")) { - var = scope->getVariable(tok->strAt(2)); - - if (var) - vars.emplace_back(var, tok->tokAt(2)); + const Token* const end = tok->next()->link(); + for (; tok != end; tok = tok->next()) { + if (const Variable* argVar = scope->getVariable(tok->str())) { + if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&"))) + continue; + if (var->isReference()) + continue; + if (Token::simpleMatch(tok->astParent(), "=")) + continue; + vars.back().initArgs.emplace_back(argVar); + } } - tok = tok->next()->link()->next(); - } else - tok = tok->next(); + } } - // need at least 2 members to have out of order initialization - for (int j = 1; j < vars.size(); j++) { + for (int j = 0; j < vars.size(); j++) { + // check for use of uninitialized arguments + for (const auto& arg : vars[j].initArgs) + if (vars[j].var->index() < arg->index()) + initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), /*isArgument*/ true); + + // need at least 2 members to have out of order initialization + if (j == 0) + continue; // check for out of order initialization if (vars[j].var->index() < vars[j - 1].var->index()) initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name()); @@ -2692,15 +2706,18 @@ void CheckClass::initializerListOrder() } } -void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) +void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, bool isArgument) { std::list toks = { tok1, tok2 }; + const std::string msg = isArgument ? + "Member variable '$symbol' uses an uninitialized argument due to the order of declarations." : + "Member variable '$symbol' is in the wrong place in the initializer list."; reportError(toks, Severity::style, "initializerList", - "$symbol:" + classname + "::" + varname +"\n" - "Member variable '$symbol' is in the wrong place in the initializer list.\n" - "Member variable '$symbol' is in the wrong place in the initializer list. " + "$symbol:" + classname + "::" + varname + '\n' + + msg + '\n' + + msg + ' ' + "Members are initialized in the order they are declared, not in the " - "order they are in the initializer list. Keeping the initializer list " + "order they are in the initializer list. Keeping the initializer list " "in the same order that the members were declared prevents order dependent " "initialization errors.", CWE398, Certainty::inconclusive); } diff --git a/lib/checkclass.h b/lib/checkclass.h index bac9f6dd1..79258491b 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -199,7 +199,7 @@ private: void operatorEqToSelfError(const Token *tok); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); - void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); + void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname, bool isArgument = false); void suggestInitializationList(const Token *tok, const std::string& varname); void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); diff --git a/test/testclass.cpp b/test/testclass.cpp index 528188947..f6dddcdce 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -215,6 +215,7 @@ private: TEST_CASE(qualifiedNameMember); // #10872 TEST_CASE(initializerListOrder); + TEST_CASE(initializerListArgument); TEST_CASE(initializerListUsage); TEST_CASE(selfInitialization); @@ -7571,6 +7572,57 @@ private: "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : b(a = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + } + + void initializerListArgument() { + checkInitializerListOrder("struct A { A(); };\n" // #12322 + "struct B { explicit B(const A* a); };\n" + "struct C {\n" + " C() : b(&a) {}\n" + " B b;\n" + " const A a;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Member variable 'C::b' uses an uninitialized argument due to the order of declarations.\n", + errout.str()); + + checkInitializerListOrder("struct S {\n" + " S(const std::string& f, std::string i, int b, int c) : a(0), b(b), c(c) {}\n" + " int a, b, c;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(a) {}\n" + " int* p;\n" + " int a[1];\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(&i) {}\n" + " int* p;\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : a(b = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkInitializerListOrder("struct S {\n" + " S() : r(i) {}\n" + " int& r;\n" + " int i{};\n" + "};"); + ASSERT_EQUALS("", errout.str()); } #define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__)