diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index f8c18a6f6..e3777fdd4 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -2229,8 +2229,8 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& return false; } - // function call.. - else if (Token::Match(tok1, "%name% (") && !tok1->isStandardType() && + // function/constructor call, return init list + else if ((Token::Match(tok1, "%name% (|{") || Token::simpleMatch(tok1->astParent(), "return {")) && !tok1->isStandardType() && !Token::Match(tok1, "return|if|string|switch|while|catch|for")) { if (isMemberFunc(scope, tok1) && tok1->strAt(-1) != ".") { if (!isConstMemberFunc(scope, tok1)) @@ -2244,7 +2244,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& for (const Token* tok2 = lpar->next(); tok2 && tok2 != tok1->next()->link(); tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); - else if (tok2->isName() && isMemberVar(scope, tok2)) { + else if ((tok2->isName() && isMemberVar(scope, tok2)) || (tok2->isUnaryOp("&") && (tok2 = tok2->astOperand1()))) { const Variable* var = tok2->variable(); if (!var || !var->isMutable()) return false; // TODO: Only bailout if function takes argument as non-const reference diff --git a/test/testclass.cpp b/test/testclass.cpp index b81c9a444..475bd5f17 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -174,6 +174,7 @@ private: TEST_CASE(const69); // ticket #9806 TEST_CASE(const70); // variadic template can receive more arguments than in its definition TEST_CASE(const71); // ticket #10146 + TEST_CASE(const72); // ticket #10520 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); @@ -5807,6 +5808,84 @@ private: ASSERT_EQUALS("", errout.str()); } + void const72() { // #10520 + checkConst("struct S {\n" + " explicit S(int* p) : mp(p) {}\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkConst("struct S {\n" + " explicit S(int* p) : mp(p) {}\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S(&i); }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkConst("struct S {\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkConst("struct S {\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return { &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkConst("struct S {\n" + " explicit S(const int* p) : mp(p) {}\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); + + checkConst("struct S {\n" + " explicit S(const int* p) : mp(p) {}\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S(&i); }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); + + checkConst("struct S {\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); + + checkConst("struct S {\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return { &i }; }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n"