diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 8ac931b1b..84954050e 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2935,61 +2935,175 @@ const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *ty //--------------------------------------------------------------------------- -/** @todo This function only counts the number of arguments in the function call. - It does not take into account function constantness. - It does not take into account argument types. This can be difficult because of promotion and conversion operators and casts and because the argument can also be a function call. - */ -const Function* Scope::findFunction(const Token *tok) const +void Scope::findFunctionInBase(const Token * tok, size_t args, std::vector & matches) const { - std::list::const_iterator it; - - // this is a function call so try to find it based on name and arguments - for (it = functionList.begin(); it != functionList.end(); ++it) { - if (it->tokenDef->str() == tok->str()) { - const Function *func = &*it; - const Token *end = tok->linkAt(1); - if (end) { - // check the arguments - unsigned int args = 0; - const Token *arg = tok->tokAt(2); - while (arg && arg != end) { - /** @todo check argument type for match */ - - // mismatch parameter: passing parameter by address to function, argument is reference - if (arg->str() == "&") { - // check that function argument type is not mismatching - const Variable *funcarg = func->getArgumentVar(args); - if (funcarg && funcarg->isReference()) { - args = ~0U; - break; - } - } - - args++; - arg = arg->nextArgument(); - } - - // check for argument count match or default arguments - if (args == func->argCount() || - (args < func->argCount() && args >= func->minArgCount())) - return func; - } - } - } - - // check in base classes if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { for (std::size_t i = 0; i < definedType->derivedFrom.size(); ++i) { const Type *base = definedType->derivedFrom[i].type; if (base && base->classScope) { if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; - const Function * func = base->classScope->findFunction(tok); - if (func) - return func; + + for (std::list::const_iterator it = base->classScope->functionList.begin(); it != base->classScope->functionList.end(); ++it) { + if (it->tokenDef->str() == tok->str()) { + const Function *func = &*it; + if (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount())) { + matches.push_back(func); + } + } + } + + base->classScope->findFunctionInBase(tok, args, matches); } } } +} + +//--------------------------------------------------------------------------- + +/** @todo This function only counts the number of arguments in the function call. + It does not take into account function constantness. + It does not take into account argument types. This can be difficult because of promotion and conversion operators and casts and because the argument can also be a function call. + */ +const Function* Scope::findFunction(const Token *tok) const +{ + // make sure this is a function call + const Token *end = tok->linkAt(1); + if (!end) + return nullptr; + + std::vector arguments; + + // find all the arguments for this function call + const Token *arg = tok->tokAt(2); + while (arg && arg != end) { + arguments.push_back(arg); + arg = arg->nextArgument(); + } + + std::vector matches; + + // find all the possible functions that could match + const std::size_t args = arguments.size(); + for (std::list::const_iterator it = functionList.begin(); it != functionList.end(); ++it) { + if (it->tokenDef->str() == tok->str()) { + const Function *func = &*it; + if (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount())) { + matches.push_back(func); + } + } + } + + // check in base classes + findFunctionInBase(tok, args, matches); + + // check each function against the arguments in the function call for a match + for (size_t i = 0; i < matches.size(); ++i) { + const Function * func = matches[i]; + size_t same = 0; + for (std::size_t j = 0; j < args; ++j) { + const Variable *funcarg = func->getArgumentVar(j); + // check for a match with a variable + if (Token::Match(arguments[j], "%var% ,|)") && arguments[j]->varId()) { + const Variable * callarg = check->getVariableFromVarId(arguments[j]->varId()); + if (callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && + callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && + callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) { + same++; + } + } + + // check for a match with a numeric literal + else if (Token::Match(arguments[j], "%num% ,|)")) { + if (MathLib::isInt(arguments[j]->str())) { + if (arguments[j]->str().find("ll") != std::string::npos || + arguments[j]->str().find("LL") != std::string::npos) { + if (arguments[j]->str().find("u") != std::string::npos || + arguments[j]->str().find("U") != std::string::npos) { + if (funcarg->typeStartToken()->str() == "long" && + funcarg->typeStartToken()->isLong() && + funcarg->typeStartToken()->isUnsigned()) { + same++; + } + } else { + if (funcarg->typeStartToken()->str() == "long" && + funcarg->typeStartToken()->isLong() && + !funcarg->typeStartToken()->isUnsigned()) { + same++; + } + } + } else if (arguments[j]->str().find("l") != std::string::npos || + arguments[j]->str().find("L") != std::string::npos) { + if (arguments[j]->str().find("u") != std::string::npos || + arguments[j]->str().find("U") != std::string::npos) { + if (funcarg->typeStartToken()->str() == "long" && + !funcarg->typeStartToken()->isLong() && + funcarg->typeStartToken()->isUnsigned()) { + same++; + } + } else { + if (funcarg->typeStartToken()->str() == "long" && + !funcarg->typeStartToken()->isLong() && + !funcarg->typeStartToken()->isUnsigned()) { + same++; + } + } + } else if (arguments[j]->str().find("u") != std::string::npos || + arguments[j]->str().find("U") != std::string::npos) { + if (funcarg->typeStartToken()->str() == "int" && + funcarg->typeStartToken()->isUnsigned()) { + same++; + } else if (Token::Match(funcarg->typeStartToken(), "char|short")) { + same++; + } + } else { + if (funcarg->typeStartToken()->str() == "int" && + !funcarg->typeStartToken()->isUnsigned()) { + same++; + } else if (Token::Match(funcarg->typeStartToken(), "char|short|int")) { + same++; + } + } + } else { + if (arguments[j]->str().find("f") != std::string::npos || + arguments[j]->str().find("F") != std::string::npos) { + if (funcarg->typeStartToken()->str() == "float") { + same++; + } + } else if (arguments[j]->str().find("l") != std::string::npos || + arguments[j]->str().find("L") != std::string::npos) { + if (funcarg->typeStartToken()->str() == "double" && + funcarg->typeStartToken()->isLong()) { + same++; + } + } else { + if (funcarg->typeStartToken()->str() == "double" && + !funcarg->typeStartToken()->isLong()) { + same++; + } + } + } + } + + // check that function argument type is not mismatching + else if (arguments[j]->str() == "&" && funcarg && funcarg->isReference()) { + // can't match so remove this function from possible matches + matches.erase(matches.begin() + i--); + break; + } + } + + // check if all arguments matched + if (same == args) { + // found a match + return func; + } + } + + // no exact match so just return first function found + if (!matches.empty()) { + return matches[0]; + } return nullptr; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index c9b1fcfd1..c04e66946 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -796,6 +796,8 @@ private: * @return true if tok points to a variable declaration, false otherwise */ bool isVariableDeclaration(const Token* tok, const Token*& vartok, const Token*& typetok) const; + + void findFunctionInBase(const Token * tok, size_t args, std::vector & matches) const; }; class CPPCHECKLIB SymbolDatabase { diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index fc63b4fb0..33fc8bcc7 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -135,6 +135,7 @@ private: TEST_CASE(uninitVar25); // ticket #4789 TEST_CASE(uninitVar26); TEST_CASE(uninitVar27); // ticket #5170 - rtl::math::setNan(&d) + TEST_CASE(uninitVar28); // ticket #6258 TEST_CASE(uninitVarEnum); TEST_CASE(uninitVarStream); TEST_CASE(uninitVarTypedef); @@ -2110,6 +2111,21 @@ private: ASSERT_EQUALS("", errout.str()); } + void uninitVar28() { + check("class Fred {\n" + " int i;\n" + " float f;\n" + "public:\n" + " Fred() {\n" + " foo(1);\n" + " foo(1.0f);\n" + " }\n" + " void foo(int a) { i = a; }\n" + " void foo(float a) { f = a; }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + } + void uninitVarArray1() { check("class John\n" "{\n" diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 05eba1336..e14c52f78 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -228,6 +228,7 @@ private: TEST_CASE(findFunction2); // mismatch: parameter passed by address => reference argument TEST_CASE(findFunction3); TEST_CASE(findFunction4); + TEST_CASE(findFunction5); // #6230 TEST_CASE(noexceptFunction1); TEST_CASE(noexceptFunction2); @@ -2248,88 +2249,107 @@ private: ASSERT_EQUALS("", errout.str()); const Token *f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1 ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1U ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1UL ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1ULL ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0F ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0 ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( 1.0L ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( i ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ui ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ul ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ull ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( f ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( d ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ld ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( ri ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rui ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rul ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rull ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rf ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rd ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( rld ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); f = Token::findsimplematch(tokenizer.tokens(), "foo ( cri ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crui ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crul ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crull ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crf ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crd ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7); f = Token::findsimplematch(tokenizer.tokens(), "foo ( crld ) ;"); - TODO_ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8, false); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8); + } + + void findFunction5() { + GET_SYMBOL_DB("struct Fred {\n" + " void Sync(dsmp_t& type, int& len, int limit = 123);\n" + " void Sync(int& syncpos, dsmp_t& type, int& len, int limit = 123);\n" + " void FindSyncPoint();\n" + "};\n" + "void Fred::FindSyncPoint() {\n" + " dsmp_t type;\n" + " int syncpos, len;\n" + " Sync(syncpos, type, len);\n" + " Sync(type, len);\n" + "}"); + const Token *f = Token::findsimplematch(tokenizer.tokens(), "Sync ( syncpos"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3); + + f = Token::findsimplematch(tokenizer.tokens(), "Sync ( type"); + ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 2); } #define FUNC(x) const Function *x = findFunctionByName(#x, &db->scopeList.front()); \ diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 4c18b716c..a17917e8b 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -68,6 +68,7 @@ private: TEST_CASE(uninitvar2_4494); // #4494 TEST_CASE(uninitvar2_malloc); // malloc returns uninitialized data TEST_CASE(uninitvar7); // ticket #5971 + TEST_CASE(uninitvar8); // ticket #6230 TEST_CASE(syntax_error); // Ticket #5073 @@ -2649,6 +2650,21 @@ private: checkUninitVar2(code, "test.cpp"); } + void uninitvar8() { + const char code[] = "struct Fred {\n" + " void Sync(dsmp_t& type, int& len, int limit = 123);\n" + " void Sync(int& syncpos, dsmp_t& type, int& len, int limit = 123);\n" + " void FindSyncPoint();\n" + "};\n" + "void Fred::FindSyncPoint() {\n" + " dsmp_t type;\n" + " int syncpos, len;\n" + " Sync(syncpos, type, len);\n" + "}"; + checkUninitVar2(code, "test.cpp"); + ASSERT_EQUALS("", errout.str()); + } + // Handling of function calls void uninitvar2_func() { // non-pointer variable