diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index d38c1661d..3cf986c69 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -1903,7 +1903,7 @@ bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const const Type *derivedFrom = i.type; // find the function in the base class - if (derivedFrom && derivedFrom->classScope) { + if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { if (isMemberVar(derivedFrom->classScope, tok)) return true; } @@ -1944,7 +1944,7 @@ bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) const const Type *derivedFrom = i.type; // find the function in the base class - if (derivedFrom && derivedFrom->classScope) { + if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { if (isMemberFunc(derivedFrom->classScope, tok)) return true; } @@ -2460,7 +2460,8 @@ void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, con } } } - checkDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type); + if (typeCurrent != parentClassIt.type) + checkDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type); } } diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index 5231ef777..ac44441a8 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -603,7 +603,7 @@ double MathLib::toDoubleNumber(const std::string &str) return ret; } -template<> std::string MathLib::toString(double value) +template<> std::string MathLib::toString(double value) { std::ostringstream result; result.precision(12); diff --git a/lib/mathlib.h b/lib/mathlib.h index a08348235..6b9a52561 100644 --- a/lib/mathlib.h +++ b/lib/mathlib.h @@ -159,7 +159,7 @@ MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2); -template<> CPPCHECKLIB std::string MathLib::toString(double value); // Declare specialization to avoid linker problems +template<> CPPCHECKLIB std::string MathLib::toString(double value); // Declare specialization to avoid linker problems /// @} //--------------------------------------------------------------------------- diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index fcd0732a3..25262dfe7 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -633,7 +633,7 @@ void SymbolDatabase::createSymbolDatabaseFindAllScopes() bool newFunc = true; // Is this function already in the database? for (std::multimap::const_iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { - if (Function::argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) { + if (i->second->argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) { newFunc = false; break; } @@ -2348,7 +2348,39 @@ static bool usingNamespace(const Scope *scope, const Token *first, const Token * return false; } -bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) +static bool typesMatch( + const Scope *first_scope, + const Token *first_token, + const Scope *second_scope, + const Token *second_token, + const Token **new_first, + const Token **new_second) +{ + // get first type + const Type * first_type = first_scope->check->findType(first_token, first_scope); + if (first_type) { + // get second type + const Type * second_type = second_scope->check->findType(second_token, second_scope); + // check if types match + if (first_type == second_type) { + const Token* tok1 = first_token; + while (tok1 && tok1->str() != first_type->name()) + tok1 = tok1->next(); + const Token *tok2 = second_token; + while (tok2 && tok2->str() != second_type->name()) + tok2 = tok2->next(); + // update parser token positions + if (tok1 && tok2) { + *new_first = tok1; + *new_second = tok2; + return true; + } + } + } + return false; +} + +bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const { const bool isCPP = scope->check->isCPP(); if (!isCPP) // C does not support overloads @@ -2485,6 +2517,10 @@ bool Function::argsMatch(const Scope *scope, const Token *first, const Token *se else if (usingNamespace(scope, first->next(), second->next(), offset)) first = first->tokAt(offset); + // same type with different qualification + else if (typesMatch(scope, first->next(), nestedIn, second->next(), &first, &second)) + ; + // variable with class path else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") { std::string param = path; @@ -2641,7 +2677,7 @@ Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, co const Function *f = i->second; if (f->hasBody()) continue; - if (Function::argsMatch(scope, f->argDef, argStart, emptyString, 0)) { + if (f->argsMatch(scope, f->argDef, argStart, emptyString, 0)) { function = const_cast(i->second); break; } @@ -2793,7 +2829,7 @@ void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const To for (std::multimap::iterator it = scope1->functionMap.find((*tok)->str()); it != scope1->functionMap.end() && it->first == (*tok)->str(); ++it) { Function * func = const_cast(it->second); if (!func->hasBody()) { - if (Function::argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { + if (func->argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { if (func->type == Function::eDestructor && destructor) { func->hasBody(true); } else if (func->type != Function::eDestructor && !destructor) { @@ -2975,6 +3011,8 @@ const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) } } + base.type = classScope->check->findType(base.nameTok, classScope); + // save pattern for base class name derivedFrom.push_back(base); } else @@ -4482,6 +4520,8 @@ const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Tok for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { + if (base->classScope == scope) + return nullptr; const Type * type = base->classScope->findType(typeTok->str()); if (type) return type; @@ -4650,6 +4690,30 @@ void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::v } } +const Scope *Scope::findRecordInBase(const std::string & name) const +{ + if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { + const std::vector &derivedFrom = definedType->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Type *base = i.type; + if (base && base->classScope) { + if (base->classScope == this) // Recursive class; tok should have been found already + continue; + + if (base->name() == name) { + return base->classScope; + } + + const ::Type * type = base->classScope->findType(name); + if (type) + return type->classScope; + } + } + } + + return nullptr; +} + //--------------------------------------------------------------------------- static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2) @@ -5219,9 +5283,14 @@ const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startSc } } else { const Type * type = scope->findType(tok->str()); + const Scope *scope1; if (type) return type; - else + else if ((scope1 = scope->findRecordInBase(tok->str()))) { + type = scope1->definedType; + if (type) + return type; + } else break; } } @@ -5251,9 +5320,14 @@ const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startSc } } else { const Type * type = scope->findType(tok->str()); + const Scope *scope1; if (type) return type; - else + else if ((scope1 = scope->findRecordInBase(tok->str()))) { + type = scope1->definedType; + if (type) + return type; + } else break; } } @@ -5345,7 +5419,7 @@ Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *n for (std::multimap::const_iterator it = ns->functionMap.find(func->str()); it != ns->functionMap.end() && it->first == func->str(); ++it) { - if (Function::argsMatch(ns, it->second->argDef, func->next(), path, path_length) && + if (it->second->argsMatch(ns, it->second->argDef, func->next(), path, path_length) && it->second->isDestructor() == destructor) { function = it->second; break; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index da0c7c5b6..604eed176 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -895,7 +895,7 @@ public: const Token *templateDef; ///< points to 'template <' before function const Token *functionPointerUsage; ///< function pointer usage - static bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length); + bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const; static bool returnsReference(const Function* function, bool unknown = false); @@ -1150,6 +1150,8 @@ public: const Token * addEnum(const Token * tok, bool isCpp); + const Scope *findRecordInBase(const std::string &name) const; + private: /** * @brief helper function for getVariableList() diff --git a/test/testclass.cpp b/test/testclass.cpp index 16f45009e..87b0de185 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -5391,14 +5391,14 @@ private: "int MixerParticipant::GetAudioFrame() {\n" " return 0;\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (performance, inconclusive) Technically the member function 'MixerParticipant::GetAudioFrame' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class MixerParticipant : public MixerParticipant {\n" " bool InitializeFileReader() {\n" " printf(\"music\");\n" " }\n" "};"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MixerParticipant::InitializeFileReader' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // Based on an example from SVN source code causing an endless recursion within CheckClass::isConstMemberFunc() // A more complete example including a template declaration like diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 7fec0bca2..2a4f00399 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -398,6 +398,7 @@ private: TEST_CASE(findFunction31); TEST_CASE(findFunction32); // C: relax type matching TEST_CASE(findFunction33); // #9885 variadic function + TEST_CASE(findFunction34); // #10061 TEST_CASE(findFunctionContainer); TEST_CASE(findFunctionExternC); TEST_CASE(findFunctionGlobalScope); // ::foo @@ -6217,6 +6218,26 @@ private: } } + void findFunction34() { + GET_SYMBOL_DB("namespace cppcheck {\n" + " class Platform {\n" + " public:\n" + " enum PlatformType { Unspecified };\n" + " };\n" + "}\n" + "class ImportProject {\n" + " void selectOneVsConfig(cppcheck::Platform::PlatformType);\n" + "};\n" + "class Settings : public cppcheck::Platform { };\n" + "void ImportProject::selectOneVsConfig(Settings::PlatformType) { }"); + (void)db; + const Token *foo = Token::findsimplematch(tokenizer.tokens(), "selectOneVsConfig ( Settings :: PlatformType ) { }"); + ASSERT(foo); + ASSERT(foo->function()); + ASSERT(foo->function()->tokenDef); + ASSERT_EQUALS(8, foo->function()->tokenDef->linenr()); + } + void findFunctionContainer() { { GET_SYMBOL_DB("void dostuff(std::vector v);\n"