diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index d98c66770e1..e6f676e7a20 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -5861,7 +5861,10 @@ bool Scope::hasInlineOrLambdaFunction(const Token** tokStart, bool onlyInline) c }); } -void Scope::findFunctionInBase(const Token* tok, size_t args, std::vector & matches) const +void Scope::findFunctionInBase(const std::string& name, + const Token* tok, + size_t args, + std::vector& matches) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; @@ -5871,7 +5874,7 @@ void Scope::findFunctionInBase(const Token* tok, size_t args, std::vectorclassScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; - auto range = base->classScope->functionMap.equal_range(tok->str()); + auto range = base->classScope->functionMap.equal_range(name); for (auto it = range.first; it != range.second; ++it) { const Function *func = it->second; if (func->isDestructor() && !Token::simpleMatch(tok->tokAt(-1), "~")) @@ -5882,7 +5885,7 @@ void Scope::findFunctionInBase(const Token* tok, size_t args, std::vectorclassScope->findFunctionInBase(tok, args, matches); + base->classScope->findFunctionInBase(name, tok, args, matches); } } } @@ -6021,8 +6024,10 @@ static bool hasMatchingConstructor(const Scope* classScope, const ValueType* arg }); } -const Function* Scope::findFunction(const Token *tok, bool requireConst, Reference ref) const +const Function* Scope::findFunction(const Token* tok, bool requireConst, Reference ref, const std::string& funcName) const { + const std::string& name = funcName.empty() ? tok->str() : funcName; + const bool isCall = Token::Match(tok->next(), "(|{"); const std::vector arguments = getArguments(tok); @@ -6032,8 +6037,8 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst, Referen // find all the possible functions that could match const std::size_t args = arguments.size(); - auto addMatchingFunctions = [&](const Scope *scope) { - auto range = scope->functionMap.equal_range(tok->str()); + auto addMatchingFunctions = [&](const Scope* scope) { + auto range = scope->functionMap.equal_range(name); for (auto it = range.first; it != range.second; ++it) { const Function *func = it->second; if (ref == Reference::LValue && func->hasRvalRefQualifier()) @@ -6068,7 +6073,7 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst, Referen const std::size_t numberOfMatchesNonBase = matches.size(); // check in base classes - findFunctionInBase(tok, args, matches); + findFunctionInBase(name, tok, args, matches); // Non-call => Do not match parameters if (!isCall) { @@ -6298,8 +6303,8 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst, Referen matches.erase(itPure); // Only one candidate left - if (matches.size() == 1 && std::none_of(functionList.begin(), functionList.end(), [tok](const Function& f) { - return startsWith(f.name(), tok->str() + " <"); + if (matches.size() == 1 && std::none_of(functionList.begin(), functionList.end(), [&name](const Function& f) { + return startsWith(f.name(), name + " <"); })) return matches[0]; @@ -7792,6 +7797,13 @@ static const Function* getFunction(const Token* tok) { lambda = lvar->nameToken()->tokAt(2)->function(); if (lambda && lambda->retDef) return lambda; + // calling an object of a class that overloads operator() + if (tok != lvar->nameToken() && !lvar->isPointer() && !lvar->isArray() && lvar->typeScope()) { + const Function* callOp = + lvar->typeScope()->findFunction(tok, lvar->isConst(), Reference::LValue, "operator()"); + if (callOp && callOp->retDef) + return callOp; + } } return nullptr; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 7d52d0bfb32..86ce9419999 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1144,9 +1144,14 @@ class CPPCHECKLIB Scope { * @brief find a function * @param tok token of function call * @param requireConst if const refers to a const variable only const methods should be matched + * @param ref reference qualification of the object the function is called on + * @param funcName name to look up instead of tok->str(), e.g. "operator()" when tok is a variable that is called * @return pointer to function if found or NULL if not found */ - const Function *findFunction(const Token *tok, bool requireConst=false, Reference ref=Reference::None) const; + const Function* findFunction(const Token* tok, + bool requireConst = false, + Reference ref = Reference::None, + const std::string& funcName = "") const; const Scope *findRecordInNestedList(const std::string & name, bool isC = false) const; Scope *findRecordInNestedList(const std::string & name, bool isC = false); @@ -1210,7 +1215,10 @@ class CPPCHECKLIB Scope { */ bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const; - void findFunctionInBase(const Token* tok, size_t args, std::vector & matches) const; + void findFunctionInBase(const std::string& name, + const Token* tok, + size_t args, + std::vector& matches) const; /** @brief initialize varlist */ void getVariableList(const Token *start, const Token *end); diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 698946b8efa..c1bf766b8bb 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -147,6 +147,7 @@ class TestNullPointer : public TestFixture { TEST_CASE(nullpointer107); // #13682 (FP/FN cases around guards that depend on the pointer indirectly) TEST_CASE(nullpointer108); TEST_CASE(nullpointer109); + TEST_CASE(nullpointer110); // #14937 TEST_CASE(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 @@ -3128,6 +3129,24 @@ class TestNullPointer : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void nullpointer110() + { // #14937 - noreturn member function called on operator() result + check("struct A {\n" + " [[noreturn]] void g(int);\n" + "};\n" + "template\n" + "struct Thunk {\n" + " T& operator()() const;\n" + "};\n" + "void f(Thunk thunk, int* p) {\n" + " if (!p)\n" + " thunk().g(0);\n" + " *p = 1;\n" + "}", + dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("", errout_str()); + } + void nullpointer_addressOf() { // address of check("void f() {\n" " struct X *x = 0;\n" diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 69d345a1cc5..1ce349cf969 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -542,6 +542,8 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(findFunction60); TEST_CASE(findFunction61); TEST_CASE(findFunction62); // #14272 - pointer passed to function is const + TEST_CASE(findFunction63); // #14937 - member function of type returned by operator() + TEST_CASE(findFunction64); // overloaded operator() TEST_CASE(findFunctionRef1); TEST_CASE(findFunctionRef2); // #13328 TEST_CASE(findFunctionContainer); @@ -8875,6 +8877,90 @@ class TestSymbolDatabase : public TestFixture { ASSERT_EQUALS(2, functionCall->function()->token->linenr()); } + void findFunction63() + { // #14937 + GET_SYMBOL_DB("struct A {\n" + " void g(int);\n" + "};\n" + "template\n" + "struct Thunk {\n" + " T& operator()() const;\n" + "};\n" + "void f(Thunk thunk) {\n" + " thunk().g(0);\n" + "}\n"); + const Token* g = Token::findsimplematch(tokenizer.tokens(), "g ( 0 )"); + ASSERT(g); + ASSERT(g->function()); + ASSERT(g->function()->tokenDef); + ASSERT_EQUALS(2, g->function()->tokenDef->linenr()); + const Token* call = Token::findsimplematch(tokenizer.tokens(), "( ) . g"); + ASSERT(call && call->valueType()); + ASSERT(call->valueType()->typeScope && call->valueType()->typeScope->className == "A"); + ASSERT_EQUALS(static_cast(Reference::LValue), static_cast(call->valueType()->reference)); + } + + void findFunction64() + { // overloaded operator() + { + GET_SYMBOL_DB("struct A { void g(int); };\n" // overloads distinguished by argument count + "struct B { void h(int); };\n" + "template\n" + "struct C {\n" + " A& operator()();\n" + " B& operator()(int);\n" + "};\n" + "void f(C c) {\n" + " c().g(1);\n" + " c(1).h(1);\n" + "}\n"); + const Token* g = Token::findsimplematch(tokenizer.tokens(), "g ( 1 )"); + ASSERT(g && g->function()); + ASSERT_EQUALS(1, g->function()->tokenDef->linenr()); + const Token* h = Token::findsimplematch(tokenizer.tokens(), "h ( 1 )"); + ASSERT(h && h->function()); + ASSERT_EQUALS(2, h->function()->tokenDef->linenr()); + } + { + GET_SYMBOL_DB("struct A { void g(int); };\n" // overloads distinguished by constness of the object + "struct B { void h(int); };\n" + "template\n" + "struct C {\n" + " A& operator()();\n" + " B& operator()() const;\n" + "};\n" + "void f(C c, const C& k) {\n" + " c().g(1);\n" + " k().h(1);\n" + "}\n"); + const Token* g = Token::findsimplematch(tokenizer.tokens(), "g ( 1 )"); + ASSERT(g && g->function()); + ASSERT_EQUALS(1, g->function()->tokenDef->linenr()); + const Token* h = Token::findsimplematch(tokenizer.tokens(), "h ( 1 )"); + ASSERT(h && h->function()); + ASSERT_EQUALS(2, h->function()->tokenDef->linenr()); + } + { + GET_SYMBOL_DB("struct A { void g(int); };\n" // overloads distinguished by argument type + "struct B { void h(int); };\n" + "template\n" + "struct C {\n" + " A& operator()(int);\n" + " B& operator()(double);\n" + "};\n" + "void f(C c, int i, double d) {\n" + " c(i).g(1);\n" + " c(d).h(1);\n" + "}\n"); + const Token* g = Token::findsimplematch(tokenizer.tokens(), "g ( 1 )"); + ASSERT(g && g->function()); + ASSERT_EQUALS(1, g->function()->tokenDef->linenr()); + const Token* h = Token::findsimplematch(tokenizer.tokens(), "h ( 1 )"); + ASSERT(h && h->function()); + ASSERT_EQUALS(2, h->function()->tokenDef->linenr()); + } + } + void findFunctionRef1() { GET_SYMBOL_DB("struct X {\n" " const std::vector getInts() const & { return mInts; }\n"