Fixed #6230 (SymbolDatabase: Wrong function() is set for token)

This commit is contained in:
Robert Reif 2014-11-17 16:04:44 +01:00 committed by Daniel Marjamäki
parent 7c4a137e14
commit c5e15950df
5 changed files with 241 additions and 73 deletions

View File

@ -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<const Function *> & matches) const
{
std::list<Function>::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<Function>::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<const Token *> 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<const Function *> matches;
// find all the possible functions that could match
const std::size_t args = arguments.size();
for (std::list<Function>::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;
}

View File

@ -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<const Function *> & matches) const;
};
class CPPCHECKLIB SymbolDatabase {

View File

@ -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"

View File

@ -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()); \

View File

@ -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