Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5861,7 +5861,10 @@
});
}

void Scope::findFunctionInBase(const Token* tok, size_t args, std::vector<const Function *> & matches) const
void Scope::findFunctionInBase(const std::string& name,
const Token* tok,
size_t args,
std::vector<const Function*>& matches) const
{
if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) {
const std::vector<Type::BaseInfo> &derivedFrom = definedType->derivedFrom;
Expand All @@ -5871,7 +5874,7 @@
if (base->classScope == 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), "~"))
Expand All @@ -5882,7 +5885,7 @@
}
}

base->classScope->findFunctionInBase(tok, args, matches);
base->classScope->findFunctionInBase(name, tok, args, matches);
}
}
}
Expand Down Expand Up @@ -6021,8 +6024,10 @@
});
}

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;

Check warning

Code scanning / Cppcheck Premium

Do not dereference null pointers Warning

Do not dereference null pointers

const bool isCall = Token::Match(tok->next(), "(|{");

const std::vector<const Token *> arguments = getArguments(tok);
Expand All @@ -6032,8 +6037,8 @@
// 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())
Expand Down Expand Up @@ -6068,7 +6073,7 @@
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) {
Expand Down Expand Up @@ -6298,8 +6303,8 @@
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];

Expand Down Expand Up @@ -7792,6 +7797,13 @@
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;
}
Expand Down
12 changes: 10 additions & 2 deletions lib/symboldatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<const Function *> & matches) const;
void findFunctionInBase(const std::string& name,
const Token* tok,
size_t args,
std::vector<const Function*>& matches) const;

/** @brief initialize varlist */
void getVariableList(const Token *start, const Token *end);
Expand Down
19 changes: 19 additions & 0 deletions test/testnullpointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<class T>\n"
"struct Thunk {\n"
" T& operator()() const;\n"
"};\n"
"void f(Thunk<A> 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"
Expand Down
86 changes: 86 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<class T>\n"
"struct Thunk {\n"
" T& operator()() const;\n"
"};\n"
"void f(Thunk<A> 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<int>(Reference::LValue), static_cast<int>(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<class T>\n"
"struct C {\n"
" A& operator()();\n"
" B& operator()(int);\n"
"};\n"
"void f(C<int> 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<class T>\n"
"struct C {\n"
" A& operator()();\n"
" B& operator()() const;\n"
"};\n"
"void f(C<int> c, const C<int>& 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<class T>\n"
"struct C {\n"
" A& operator()(int);\n"
" B& operator()(double);\n"
"};\n"
"void f(C<int> 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<int> getInts() const & { return mInts; }\n"
Expand Down
Loading