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()
|
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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue