diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index b1f50440f..fe7bcf38d 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -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(), "(|{"); @@ -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); @@ -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; @@ -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]; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 64e89d8f8..6472602e8 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -447,6 +447,7 @@ private: 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 @@ -7372,6 +7373,67 @@ private: } } + 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 v);\n"