diff --git a/lib/astutils.cpp b/lib/astutils.cpp index c0ab6c08f..de2b15b05 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1226,34 +1226,78 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn) return nullptr; } - // goto start of function call and get argn - argn = 0; - while (tok && !Token::simpleMatch(tok, ";") && !isScopeBracket(tok)) { - if (tok->str() == ",") - ++argn; - else if (Token::Match(tok, ")|}")) - tok = tok->link(); - else if (Token::Match(tok->previous(), "%name% (|{")) - break; - else if (Token::Match(tok->previous(), "> (|{") && tok->previous()->link()) - break; - tok = tok->previous(); + const Token* argtok = tok; + while(argtok && argtok->astParent() && (!Token::Match(argtok->astParent(), ",|(|{") || argtok->astParent()->isCast())) { + argtok = argtok->astParent(); } + if (!argtok) + return nullptr; + if (Token::simpleMatch(argtok, ",")) + argtok = argtok->astOperand1(); + if (Token::simpleMatch(argtok, "(") && argtok->astOperand2()) + argtok = argtok->astOperand2(); + tok = argtok; + while(Token::Match(tok->astParent(), ",|(|{")) { + tok = tok->astParent(); + if (Token::Match(tok, "(|{")) + break; + } + std::vector args = getArguments(tok); + auto it = std::find(args.begin(), args.end(), argtok); + if (it != args.end()) + argn = std::distance(args.begin(), it); + if (argn == -1) + return nullptr; if (!Token::Match(tok, "{|(")) return nullptr; - tok = tok->previous(); + tok = tok->astOperand1(); + while(Token::simpleMatch(tok, ".")) + tok = tok->astOperand2(); + while(Token::simpleMatch(tok, "::")) { + tok = tok->astOperand2(); + if(Token::simpleMatch(tok, "<") && tok->link()) + tok = tok->astOperand1(); + } if (tok && tok->link() && tok->str() == ">") tok = tok->link()->previous(); - if (!Token::Match(tok, "%name% [({<]")) + if (!Token::Match(tok, "%name%|(|{")) return nullptr; return tok; } +const Variable* getArgumentVar(const Token* tok, int argnr) +{ + if (!tok) + return nullptr; + if (tok->function()) + return tok->function()->getArgumentVar(argnr); + if (Token::Match(tok->previous(), "%type% (|{") || tok->variable()) { + const Type* type = Token::typeOf(tok); + if (!type) + return nullptr; + const Scope* typeScope = type->classScope; + const int argCount = numberOfArguments(tok); + for (const Function &function : typeScope->functionList) { + if (function.isConstructor()) + continue; + if (function.argCount() < argCount) + continue; + if (!Token::simpleMatch(function.token, "operator()")) + continue; + return function.getArgumentVar(argnr); + } + } + return nullptr; +} + bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive) { if (!tok) return false; + if(Token::simpleMatch(tok, ",")) + return false; + const Token * const tok1 = tok; // address of variable @@ -1288,7 +1332,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti return false; } - if (!tok->function()) { + if (!tok->function() && !tok->variable() && Token::Match(tok, "%name%")) { // Check if direction (in, out, inout) is specified in the library configuration and use that if (!addressOf && settings) { const Library::ArgumentChecks::Direction argDirection = settings->library.getArgDirection(tok, 1 + argnr); @@ -1318,17 +1362,22 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti return true; } - const Variable *arg = tok->function()->getArgumentVar(argnr); + const Variable *arg = getArgumentVar(tok, argnr); + if (!arg) { + if (inconclusive) + *inconclusive = true; + return false; + } - if (addressOf || (indirect > 0 && arg && arg->isPointer())) { - if (!(arg && arg->isConst())) + if (addressOf || (indirect > 0 && arg->isPointer())) { + if (!arg->isConst()) return true; // If const is applied to the pointer, then the value can still be modified - if (arg && Token::simpleMatch(arg->typeEndToken(), "* const")) + if (Token::simpleMatch(arg->typeEndToken(), "* const")) return true; } - return arg && !arg->isConst() && arg->isReference(); + return !arg->isConst() && arg->isReference(); } bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth) diff --git a/lib/token.cpp b/lib/token.cpp index 59e6dd57e..000cfd15d 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -2054,6 +2054,8 @@ void Token::type(const ::Type *t) const ::Type *Token::typeOf(const Token *tok) { + if (!tok) + return nullptr; if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) @@ -2074,6 +2076,8 @@ const ::Type *Token::typeOf(const Token *tok) if (!function) return nullptr; return function->retType; + } else if (Token::Match(tok->previous(), "%type% (|{")) { + return typeOf(tok->previous()); } else if (Token::simpleMatch(tok, "=")) { return Token::typeOf(tok->astOperand1()); } else if (Token::simpleMatch(tok, ".")) { diff --git a/test/testother.cpp b/test/testother.cpp index 69e759fd7..58be8aa99 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1983,6 +1983,63 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); + // #9710 + check("class a {\n" + " void operator()(int& i) const {\n" + " i++;\n" + " }\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("class a {\n" + " void operator()(int& i) const {\n" + " i++;\n" + " }\n" + "};\n" + "void f(int& i) {\n" + " a x;\n" + " x(i);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("class a {\n" + " void operator()(const int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a x;\n" + " x(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); + + check("class a {\n" + " void foo(const int& i) const;\n" + " void operator()(int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("class a {\n" + " void operator()(const int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); + + // #9767 + check("void fct1(MyClass& object) {\n" + " fct2([&](void){}, object);\n" + "}\n" + "bool fct2(std::function lambdaExpression, MyClass& object) {\n" + " object.modify();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + check("void e();\n" "void g(void);\n" "void h(void);\n"