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

View File

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

View File

@ -142,6 +142,7 @@ private:
TEST_CASE(const34); // ticket #1964 TEST_CASE(const34); // ticket #1964
TEST_CASE(constoperator1); // operator< can often be const TEST_CASE(constoperator1); // operator< can often be const
TEST_CASE(constoperator2); // operator<< TEST_CASE(constoperator2); // operator<<
TEST_CASE(constoperator3);
TEST_CASE(constincdec); // increment/decrement => non-const TEST_CASE(constincdec); // increment/decrement => non-const
TEST_CASE(constReturnReference); TEST_CASE(constReturnReference);
TEST_CASE(constDelete); // delete member variable => not const TEST_CASE(constDelete); // delete member variable => not const
@ -2861,6 +2862,22 @@ private:
ASSERT_EQUALS("", errout.str()); 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() void const5()
{ {
// ticket #1482 // ticket #1482