From 03a6282ab337f9a449407e745f85374bba270184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 4 Sep 2016 14:14:21 +0200 Subject: [PATCH] Fixed #7338 (Library: method in base class) --- lib/library.cpp | 66 ++++++++++++++++++++++++++++---------------- lib/library.h | 6 +++- test/testlibrary.cpp | 29 +++++++++++++++++++ 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/lib/library.cpp b/lib/library.cpp index faa87a783..e12462d17 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -530,6 +530,8 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co if (name.empty()) return Error(OK); + _functions.insert(name); + for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { const std::string functionnodename = functionnode->Name(); if (functionnodename == "noreturn") @@ -716,18 +718,29 @@ bool Library::isargvalid(const Token *ftok, int argnr, const MathLib::bigint arg return false; } -static std::string functionName(const Token *ftok, bool *error) +std::string Library::getFunctionName(const Token *ftok, bool *error) const { if (!ftok) { *error = true; return ""; } - if (ftok->isName()) + if (ftok->isName()) { + for (const Scope *scope = ftok->scope(); scope; scope = scope->nestedIn) { + if (!scope->isClassOrStruct()) + continue; + for (unsigned int i = 0; i < scope->definedType->derivedFrom.size(); ++i) { + const Type::BaseInfo &baseInfo = scope->definedType->derivedFrom[i]; + const std::string name(baseInfo.name + "::" + ftok->str()); + if (_functions.find(name) != _functions.end() && matchArguments(ftok, name)) + return name; + } + } return ftok->str(); + } if (ftok->str() == "::") { if (!ftok->astOperand2()) - return functionName(ftok->astOperand1(), error); - return functionName(ftok->astOperand1(),error) + "::" + functionName(ftok->astOperand2(),error); + return getFunctionName(ftok->astOperand1(), error); + return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); } if (ftok->str() == "." && ftok->astOperand1()) { const std::string type = astCanonicalType(ftok->astOperand1()); @@ -736,13 +749,13 @@ static std::string functionName(const Token *ftok, bool *error) return ""; } - return type + "::" + functionName(ftok->astOperand2(),error); + return type + "::" + getFunctionName(ftok->astOperand2(),error); } *error = true; return ""; } -static std::string functionName(const Token *ftok) +std::string Library::getFunctionName(const Token *ftok) const { if (!Token::Match(ftok, "%name% (")) return ""; @@ -750,7 +763,7 @@ static std::string functionName(const Token *ftok) // Lookup function name using AST.. if (ftok->astParent()) { bool error = false; - std::string ret = functionName(ftok->next()->astOperand1(), &error); + std::string ret = getFunctionName(ftok->next()->astOperand1(), &error); return error ? std::string() : ret; } @@ -773,7 +786,7 @@ bool Library::isnullargbad(const Token *ftok, int argnr) const const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // scan format string argument should not be null - const std::string funcname = functionName(ftok); + const std::string funcname = getFunctionName(ftok); std::map >::const_iterator it = _formatstr.find(funcname); if (it != _formatstr.end() && it->second.first) return true; @@ -786,7 +799,7 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // non-scan format string argument should not be uninitialized - const std::string funcname = functionName(ftok); + const std::string funcname = getFunctionName(ftok); std::map >::const_iterator it = _formatstr.find(funcname); if (it != _formatstr.end() && !it->second.first) return true; @@ -798,14 +811,14 @@ bool Library::isuninitargbad(const Token *ftok, int argnr) const /** get allocation info for function */ const Library::AllocFunc* Library::alloc(const Token *tok) const { - const std::string funcname = functionName(tok); + const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_alloc, funcname); } /** get deallocation info for function */ const Library::AllocFunc* Library::dealloc(const Token *tok) const { - const std::string funcname = functionName(tok); + const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && argumentChecks.find(funcname) != argumentChecks.end() ? 0 : getAllocDealloc(_dealloc, funcname); } @@ -829,7 +842,7 @@ const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) co if (isNotLibraryFunction(ftok)) return nullptr; std::map >::const_iterator it1; - it1 = argumentChecks.find(functionName(ftok)); + it1 = argumentChecks.find(getFunctionName(ftok)); if (it1 == argumentChecks.end()) return nullptr; const std::map::const_iterator it2 = it1->second.find(argnr); @@ -906,10 +919,15 @@ bool Library::isNotLibraryFunction(const Token *ftok) const if (ftok->varId()) return true; + return !matchArguments(ftok, getFunctionName(ftok)); +} + +bool Library::matchArguments(const Token *ftok, const std::string &functionName) const +{ int callargs = numberOfArguments(ftok); - const std::map >::const_iterator it = argumentChecks.find(functionName(ftok)); + const std::map >::const_iterator it = argumentChecks.find(functionName); if (it == argumentChecks.end()) - return (callargs != 0); + return (callargs == 0); int args = 0; int firstOptionalArg = -1; for (std::map::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { @@ -919,16 +937,16 @@ bool Library::isNotLibraryFunction(const Token *ftok) const firstOptionalArg = it2->first; if (it2->second.formatstr) - return args > callargs; + return args <= callargs; } - return (firstOptionalArg < 0) ? args != callargs : !(callargs >= firstOptionalArg-1 && callargs <= args); + return (firstOptionalArg < 0) ? args == callargs : (callargs >= firstOptionalArg-1 && callargs <= args); } const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return nullptr; - std::map::const_iterator i = functionwarn.find(functionName(ftok)); + std::map::const_iterator i = functionwarn.find(getFunctionName(ftok)); if (i == functionwarn.cend()) return nullptr; return &i->second; @@ -937,12 +955,12 @@ const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const bool Library::formatstr_function(const Token* ftok) const { return (!isNotLibraryFunction(ftok) && - _formatstr.find(functionName(ftok)) != _formatstr.cend()); + _formatstr.find(getFunctionName(ftok)) != _formatstr.cend()); } int Library::formatstr_argno(const Token* ftok) const { - const std::map& argumentChecksFunc = argumentChecks.at(functionName(ftok)); + const std::map& argumentChecksFunc = argumentChecks.at(getFunctionName(ftok)); for (std::map::const_iterator i = argumentChecksFunc.cbegin(); i != argumentChecksFunc.cend(); ++i) { if (i->second.formatstr) { return i->first - 1; @@ -953,18 +971,18 @@ int Library::formatstr_argno(const Token* ftok) const bool Library::formatstr_scan(const Token* ftok) const { - return _formatstr.at(functionName(ftok)).first; + return _formatstr.at(getFunctionName(ftok)).first; } bool Library::formatstr_secure(const Token* ftok) const { - return _formatstr.at(functionName(ftok)).second; + return _formatstr.at(getFunctionName(ftok)).second; } bool Library::isUseRetVal(const Token* ftok) const { return (!isNotLibraryFunction(ftok) && - _useretval.find(functionName(ftok)) != _useretval.end()); + _useretval.find(getFunctionName(ftok)) != _useretval.end()); } bool Library::isnoreturn(const Token *ftok) const @@ -973,7 +991,7 @@ bool Library::isnoreturn(const Token *ftok) const return true; if (isNotLibraryFunction(ftok)) return false; - std::map::const_iterator it = _noreturn.find(functionName(ftok)); + std::map::const_iterator it = _noreturn.find(getFunctionName(ftok)); return (it != _noreturn.end() && it->second); } @@ -983,7 +1001,7 @@ bool Library::isnotnoreturn(const Token *ftok) const return false; if (isNotLibraryFunction(ftok)) return false; - std::map::const_iterator it = _noreturn.find(functionName(ftok)); + std::map::const_iterator it = _noreturn.find(getFunctionName(ftok)); return (it != _noreturn.end() && !it->second); } diff --git a/lib/library.h b/lib/library.h index 8607978e7..2ce5f89a3 100644 --- a/lib/library.h +++ b/lib/library.h @@ -161,7 +161,7 @@ public: // returns true if ftok is not a library function bool isNotLibraryFunction(const Token *ftok) const; - + bool matchArguments(const Token *ftok, const std::string &functionName) const; bool isUseRetVal(const Token* ftok) const; @@ -476,6 +476,7 @@ private: std::set _useretval; std::map _alloc; // allocation functions std::map _dealloc; // deallocation functions + std::set _functions; std::map _noreturn; // is function noreturn? std::set _ignorefunction; // ignore functions/macros from a library (gtk, qt etc) std::map _reporterrors; @@ -493,6 +494,9 @@ private: const ArgumentChecks * getarg(const Token *ftok, int argnr) const; + std::string getFunctionName(const Token *ftok, bool *error) const; + std::string getFunctionName(const Token *ftok) const; + static const AllocFunc* getAllocDealloc(const std::map &data, const std::string &name) { const std::map::const_iterator it = data.find(name); return (it == data.end()) ? nullptr : &it->second; diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp index 5c0ecf2a4..93188f601 100644 --- a/test/testlibrary.cpp +++ b/test/testlibrary.cpp @@ -44,6 +44,7 @@ private: TEST_CASE(function_arg_minsize); TEST_CASE(function_namespace); TEST_CASE(function_method); + TEST_CASE(function_baseClassMethod); // calling method in base class TEST_CASE(function_warn); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions @@ -395,6 +396,34 @@ private: } } + void function_baseClassMethod() const { + const char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + ""; + + Library library; + readLibrary(library, xmldata); + + { + Settings settings; + Tokenizer tokenizer(&settings, nullptr); + std::istringstream istr("struct X : public Base { void dostuff() { f(0); } };"); + tokenizer.tokenize(istr, "test.cpp"); + ASSERT(library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); + } + + { + Settings settings; + Tokenizer tokenizer(&settings, nullptr); + std::istringstream istr("struct X : public Base { void dostuff() { f(1,2); } };"); + tokenizer.tokenize(istr, "test.cpp"); + ASSERT(!library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); + } + } + void function_warn() const { const char xmldata[] = "\n" "\n"