Symbol database: better handling of operator functions. Ticket: #1895
This commit is contained in:
parent
0d530711f6
commit
96d73c189c
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue