Symbol database: better handling of operator functions. Ticket: #1895

This commit is contained in:
Robert Reif 2010-08-30 17:14:20 +02:00 committed by Daniel Marjamäki
parent 0d530711f6
commit 96d73c189c
3 changed files with 98 additions and 8 deletions

View File

@ -47,6 +47,54 @@ CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, Err
}
static bool isFunction(const Token *tok, const Token **argStart)
{
if (tok->previous()->str() == "::")
return false;
// regular function?
if (Token::Match(tok, "%var% (") && Token::Match(tok->next()->link(), ") const| ;|{|=|:"))
{
*argStart = tok->next();
return true;
}
// simple operator?
else if (Token::Match(tok, "operator %any% (") && Token::Match(tok->tokAt(2)->link(), ") const| ;|{|=|:"))
{
*argStart = tok->tokAt(2);
return true;
}
// complex operator?
else if (tok->str() == "operator")
{
// operator[] or operator()?
if ((Token::Match(tok->next(), "( ) (") || Token::Match(tok->next(), "[ ] (")) &&
Token::Match(tok->tokAt(3)->link(), ") const| ;|{|=|:"))
{
*argStart = tok->tokAt(3);
return true;
}
// operator new/delete?
else if (Token::Match(tok->next(), "new|delete (") && Token::Match(tok->tokAt(2)->link(), ") ;|{"))
{
*argStart = tok->tokAt(2);
return true;
}
// operator new/delete []?
else if (Token::Match(tok->next(), "new|delete [ ] (") && Token::Match(tok->tokAt(4)->link(), ") ;|{"))
{
*argStart = tok->tokAt(4);
return true;
}
}
return false;
}
void CheckClass::createSymbolDatabase()
{
// Multiple calls => bail out
@ -97,6 +145,8 @@ void CheckClass::createSymbolDatabase()
// check if in class or structure
else if (!info->isNamespace)
{
const Token *argStart = 0;
// What section are we in..
if (tok->str() == "private:")
info->access = Private;
@ -117,11 +167,13 @@ void CheckClass::createSymbolDatabase()
}
// function?
else if (((Token::Match(tok, "%var% (") || Token::Match(tok, "operator %any% (")) && tok->previous()->str() != "::") &&
Token::Match(tok->str() == "operator" ? tok->tokAt(2)->link() : tok->next()->link(), ") const| ;|{|=|:"))
else if (isFunction(tok, &argStart))
{
Func function;
// save the function definition argument start '('
function.argDef = argStart;
// save the access type
function.access = info->access;
@ -187,11 +239,11 @@ void CheckClass::createSymbolDatabase()
}
// const function
if (function.tokenDef->next()->link()->next()->str() == "const")
if (function.argDef->link()->next()->str() == "const")
function.isConst = true;
// pure virtual function
if (Token::Match(function.tokenDef->next()->link(), ") const| = 0 ;"))
if (Token::Match(function.argDef->link(), ") const| = 0 ;"))
function.isPure = true;
// count the number of constructors
@ -202,7 +254,7 @@ void CheckClass::createSymbolDatabase()
function.token = function.tokenDef;
// jump to end of args
const Token *next = function.tokenDef->next()->link();
const Token *next = function.argDef->link();
// out of line function
if (Token::Match(next, ") const| ;") ||
@ -216,12 +268,27 @@ void CheckClass::createSymbolDatabase()
std::string classPath;
std::string searchPattern;
const Token *funcArgs = function.tokenDef->tokAt(2);
int offset = 1;
if (function.isOperator)
classPattern = "operator " + function.tokenDef->str() + " (";
{
if (Token::Match(function.tokenDef, "(|["))
{
classPattern = "operator " + function.tokenDef->str() + " " + function.tokenDef->next()->str() + " (";
offset = 2;
}
else if (Token::Match(function.tokenDef, "new|delete ["))
{
classPattern = "operator " + function.tokenDef->str() + " " + function.tokenDef->next()->str() + " " + function.tokenDef->next()->strAt(2) + " (";
offset = 3;
}
else
classPattern = "operator " + function.tokenDef->str() + " (";
}
else
classPattern = function.tokenDef->str() + " (";
// look for an implementation outside of class
while (!function.hasBody && nest)
{
classPath = nest->className + std::string(" :: ") + classPath;
@ -242,13 +309,14 @@ void CheckClass::createSymbolDatabase()
while (found->next()->str() != "(")
found = found->next();
if (Token::Match(found->next()->link(), function.isConst ? ") const {" : ") {") ||
if (Token::Match(found->tokAt(offset)->link(), function.isConst ? ") const {" : ") {") ||
(function.type == Func::Constructor && Token::Match(found->next()->link(), ") :|{")))
{
if (argsMatch(funcArgs, found->tokAt(2), classPath, depth))
if (argsMatch(funcArgs, found->tokAt(offset + 1), classPath, depth))
{
function.token = found;
function.hasBody = true;
function.arg = found->tokAt(offset);
info->functionList.push_back(function);
break;
@ -274,6 +342,7 @@ void CheckClass::createSymbolDatabase()
{
function.isInline = true;
function.hasBody = true;
function.arg = function.argDef;
info->functionList.push_back(function);

View File

@ -168,7 +168,9 @@ private:
Func()
: tokenDef(NULL),
argDef(NULL),
token(NULL),
arg(NULL),
access(Public),
hasBody(false),
isInline(false),
@ -184,7 +186,9 @@ private:
}
const Token *tokenDef; // function name token in class definition
const Token *argDef; // function argument start '(' in class definition
const Token *token; // function name token in implementation
const Token *arg; // function argument start '('
AccessControl access; // public/protected/private
bool hasBody; // has implementation
bool isInline; // implementation in class definition

View File

@ -142,6 +142,7 @@ private:
TEST_CASE(const34); // ticket #1964
TEST_CASE(constoperator1); // operator< can often be const
TEST_CASE(constoperator2); // operator<<
TEST_CASE(constoperator3);
TEST_CASE(constincdec); // increment/decrement => non-const
TEST_CASE(constReturnReference);
TEST_CASE(constDelete); // delete member variable => not const
@ -2861,6 +2862,22 @@ private:
ASSERT_EQUALS("", errout.str());
}
void constoperator3()
{
checkConst("struct Fred {\n"
" int array[10];\n"
" int const & operator [] (unsigned int index) const { return array[index]; }\n"
" int & operator [] (unsigned int index) { return array[index]; }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
checkConst("struct Fred {\n"
" int array[10];\n"
" int const & operator [] (unsigned int index) { return array[index]; }\n"
"};\n");
ASSERT_EQUALS("[test.cpp:3]: (style) The function 'Fred::operator[' can be const\n", errout.str());
}
void const5()
{
// ticket #1482