Skip to content
Merged
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
25 changes: 25 additions & 0 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5442,6 +5442,23 @@ static std::string getTypeString(const Token *typeToken)
return "";
}

static bool hasMatchingConstructor(const Scope* classScope, const ValueType* argType) {
if (!classScope || !argType)
return false;
return std::any_of(classScope->functionList.cbegin(),
classScope->functionList.cend(),
[&](const Function& f) {
if (!f.isConstructor() || f.argCount() != 1 || !f.getArgumentVar(0))
return false;
const ValueType* vt = f.getArgumentVar(0)->valueType();
return vt &&
vt->type == argType->type &&
(argType->sign == ValueType::Sign::UNKNOWN_SIGN || vt->sign == argType->sign) &&
vt->pointer == argType->pointer &&
(vt->constness & 1) >= (argType->constness & 1);
});
}

const Function* Scope::findFunction(const Token *tok, bool requireConst) const
{
const bool isCall = Token::Match(tok->next(), "(|{");
Expand Down Expand Up @@ -5473,6 +5490,8 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const
addMatchingFunctions(nestedScope);
}

const std::size_t numberOfMatchesNonBase = matches.size();

// check in base classes
findFunctionInBase(tok->str(), args, matches);

Expand All @@ -5485,6 +5504,9 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const

// check each function against the arguments in the function call for a match
for (std::size_t i = 0; i < matches.size();) {
if (i > 0 && i == numberOfMatchesNonBase && fallback1Func.empty() && !fallback2Func.empty())
break;

bool constFallback = false;
const Function * func = matches[i];
size_t same = 0;
Expand Down Expand Up @@ -5592,6 +5614,9 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const
else if (funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str()))
fallback1++;

else if (!funcarg->isPointer() && funcarg->type() && hasMatchingConstructor(funcarg->type()->classScope, arguments[j]->valueType()))
fallback2++;

// Try to evaluate the apparently more complex expression
else if (check->isCPP()) {
const Token *vartok = arguments[j];
Expand Down
62 changes: 62 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ class TestSymbolDatabase : public TestFixture {
TEST_CASE(findFunction48);
TEST_CASE(findFunction49); // #11888
TEST_CASE(findFunction50); // #11904 - method with same name and arguments in derived class
TEST_CASE(findFunction51); // #11975 - method with same name in derived class
TEST_CASE(findFunctionContainer);
TEST_CASE(findFunctionExternC);
TEST_CASE(findFunctionGlobalScope); // ::foo
Expand Down Expand Up @@ -7372,6 +7373,67 @@ class TestSymbolDatabase : public TestFixture {
}
}

void findFunction51() {
// Both A and B defines the method test but with different arguments.
// The call to test in B should match the method in B. The match is close enough.
{
GET_SYMBOL_DB("class A {\n"
"public:\n"
" void test(bool a = true);\n"
"};\n"
"\n"
"class B : public A {\n"
"public:\n"
" B(): b_obj(this) { b_obj->test(\"1\"); }\n"
" void test(const std::string& str_obj);\n"
" B* b_obj;\n"
"};");
const Token* call = Token::findsimplematch(tokenizer.tokens(), "test ( \"1\" ) ;");
ASSERT(call);
ASSERT(call->function());
ASSERT(call->function()->tokenDef);
ASSERT_EQUALS(9, call->function()->tokenDef->linenr());
}
{
GET_SYMBOL_DB("struct STR { STR(const char * p); };\n"
"class A {\n"
"public:\n"
" void test(bool a = true);\n"
"};\n"
"\n"
"class B : public A {\n"
"public:\n"
" B(): b_obj(this) { b_obj->test(\"1\"); }\n"
" void test(const STR& str_obj);\n"
" B* b_obj;\n"
"};");
const Token* call = Token::findsimplematch(tokenizer.tokens(), "test ( \"1\" ) ;");
ASSERT(call);
ASSERT(call->function());
ASSERT(call->function()->tokenDef);
ASSERT_EQUALS(10, call->function()->tokenDef->linenr());
}
{
GET_SYMBOL_DB("struct STR { STR(const char * p); };\n"
"class A {\n"
"public:\n"
" void test(bool a = true, int b = 0);\n"
"};\n"
"\n"
"class B : public A {\n"
"public:\n"
" B(): b_obj(this) { b_obj->test(\"1\"); }\n"
" void test(const STR& str_obj);\n"
" B* b_obj;\n"
"};");
const Token* call = Token::findsimplematch(tokenizer.tokens(), "test ( \"1\" ) ;");
ASSERT(call);
ASSERT(call->function());
ASSERT(call->function()->tokenDef);
ASSERT_EQUALS(10, call->function()->tokenDef->linenr());
}
}

void findFunctionContainer() {
{
GET_SYMBOL_DB("void dostuff(std::vector<int> v);\n"
Expand Down