Tokenizer: collapse operator function names into a single token. ticket: #2519

This commit is contained in:
Daniel Marjamäki 2011-01-27 18:44:20 +01:00
parent 090436ea95
commit 524498e439
8 changed files with 99 additions and 81 deletions

View File

@ -140,11 +140,7 @@ void CheckClass::constructors()
// It's non-static and it's not initialized => error
if (func->type == Function::eOperatorEqual)
{
const Token *operStart = 0;
if (func->token->str() == "=")
operStart = func->token->tokAt(1);
else
operStart = func->token->tokAt(3);
const Token *operStart = func->token->tokAt(1);
bool classNameUsed = false;
for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next())
@ -376,14 +372,14 @@ void CheckClass::initializeVarList(const Function &func, std::list<std::string>
}
// Calling member function?
else if (Token::simpleMatch(ftok, "operator = (") &&
else if (Token::simpleMatch(ftok, "operator= (") &&
ftok->previous()->str() != "::")
{
// check if member function exists
std::list<Function>::const_iterator it;
for (it = scope->functionList.begin(); it != scope->functionList.end(); ++it)
{
if (ftok->next()->str() == it->tokenDef->str() && it->type != Function::eConstructor)
if (ftok->str() == it->tokenDef->str() && it->type != Function::eConstructor)
break;
}
@ -583,11 +579,6 @@ void CheckClass::privateFunctions()
if (Token::findmatch(_tokenizer->tokens(), "; __property ;"))
return;
// #2407 calls from operator() is not detected
// TODO: Don't bailout. Detect the call.
if (Token::findmatch(_tokenizer->tokens(), "operator ( )"))
return;
createSymbolDatabase();
std::list<Scope *>::const_iterator i;
@ -872,8 +863,8 @@ void CheckClass::operatorEq()
{
if (it->type == Function::eOperatorEqual && it->access != Private)
{
if (it->token->strAt(-2) == "void")
operatorEqReturnError(it->token->tokAt(-2));
if (it->token->strAt(-1) == "void")
operatorEqReturnError(it->token->tokAt(-1));
}
}
}
@ -940,7 +931,7 @@ void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, co
// check of *this is returned
else if (!(Token::Match(tok->tokAt(1), "(| * this ;|=") ||
Token::Match(tok->tokAt(1), "(| * this +=") ||
Token::Match(tok->tokAt(1), "operator = (")))
Token::Match(tok->tokAt(1), "operator= (")))
operatorEqRetRefThisError(func->token);
}
}
@ -971,8 +962,8 @@ void CheckClass::operatorEqRetRefThis()
if (func->type == Function::eOperatorEqual && func->hasBody)
{
// make sure return signature is correct
if (Token::Match(func->tokenDef->tokAt(-4), ";|}|{|public:|protected:|private: %type% &") &&
func->tokenDef->strAt(-3) == scope->className)
if (Token::Match(func->tokenDef->tokAt(-3), ";|}|{|public:|protected:|private: %type% &") &&
func->tokenDef->strAt(-2) == scope->className)
{
// find the ')'
const Token *tok = func->token->next()->link();
@ -1027,8 +1018,8 @@ void CheckClass::operatorEqToSelf()
if (it->type == Function::eOperatorEqual && it->hasBody)
{
// make sure return signature is correct
if (Token::Match(it->tokenDef->tokAt(-4), ";|}|{|public:|protected:|private: %type% &") &&
it->tokenDef->strAt(-3) == scope->className)
if (Token::Match(it->tokenDef->tokAt(-3), ";|}|{|public:|protected:|private: %type% &") &&
it->tokenDef->strAt(-2) == scope->className)
{
// check for proper function parameter signature
if ((Token::Match(it->tokenDef->next(), "( const %var% & )") ||

View File

@ -137,12 +137,12 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
function.tokenDef = funcStart;
// operator function
if (function.tokenDef->previous()->str() == "operator")
if (function.tokenDef->str().find("operator") == 0)
{
function.isOperator = true;
// 'operator =' is special
if (function.tokenDef->str() == "=")
if (function.tokenDef->str() == "operator=")
function.type = Function::eOperatorEqual;
}
@ -578,38 +578,6 @@ bool SymbolDatabase::isFunction(const Token *tok, const Token **funcStart, const
return true;
}
// simple operator?
else if (Token::Match(tok, "operator %any% (") && Token::Match(tok->tokAt(2)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(2);
return true;
}
// operator[] or operator()?
else if (Token::Match(tok, "operator %any% %any% (") && Token::Match(tok->tokAt(3)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(3);
return true;
}
// operator new/delete []?
else if (Token::Match(tok, "operator %any% %any% %any% (") && Token::Match(tok->tokAt(4)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(4);
return true;
}
// complex user defined operator?
else if (Token::Match(tok, "operator %any% %any% %any% %any% (") && Token::Match(tok->tokAt(5)->link(), ") const| ;|{|="))
{
*funcStart = tok->next();
*argStart = tok->tokAt(5);
return true;
}
return false;
}
@ -772,20 +740,9 @@ void SymbolDatabase::addFunction(Scope **scope, const Token **tok, const Token *
{
if (!func->hasBody)
{
if (func->isOperator &&
(*tok)->str() == "operator" &&
func->tokenDef->str() == (*tok)->strAt(1))
{
if (argsMatch(scope1, func->tokenDef->tokAt(2), (*tok)->tokAt(3), path, path_length))
{
func->hasBody = true;
func->token = (*tok)->next();
func->arg = argStart;
}
}
else if (func->type == Function::eDestructor &&
(*tok)->previous()->str() == "~" &&
func->tokenDef->str() == (*tok)->str())
if (func->type == Function::eDestructor &&
(*tok)->previous()->str() == "~" &&
func->tokenDef->str() == (*tok)->str())
{
if (argsMatch(scope1, func->tokenDef->next(), (*tok)->next(), path, path_length))
{

View File

@ -513,7 +513,7 @@ bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name)
}
else if (end->str() == "(")
{
if (tok->previous()->str() == "operator")
if (tok->previous()->str().find("operator") == 0)
{
// conversion operator
return false;
@ -2490,6 +2490,10 @@ bool Tokenizer::tokenize(std::istream &code,
// remove exception specifications..
removeExceptionSpecifications(_tokens);
// Collapse operator name tokens into single token
// operator = => operator=
simplifyOperatorName();
// simplify function pointers
simplifyFunctionPointers();
@ -9238,3 +9242,30 @@ const SymbolDatabase *Tokenizer::getSymbolDatabase() const
return _symbolDatabase;
}
void Tokenizer::simplifyOperatorName()
{
for (const Token *tok = _tokens; tok; tok = tok->next())
{
if (Token::Match(tok, ") const| {|;|="))
{
Token *tok1 = tok->link();
Token *tok2 = tok1;
tok1 = tok1->previous();
while (tok1 && !Token::Match(tok1, "operator|{|}|;|public:|private|protected:"))
{
tok1 = tok1->previous();
}
if (tok1 && tok1->str() == "operator")
{
while (tok1->next() != tok2)
{
tok1->str(tok1->str() + tok1->next()->str());
tok1->deleteNext();
}
}
}
}
}

View File

@ -531,6 +531,12 @@ public:
*/
void simplifyQtSignalsSlots();
/**
* Collapse operator name tokens into single token
* operator = => operator=
*/
void simplifyOperatorName();
/**
* This will return a short name describing function parameters
* e.g. parameters: (int a, char b) should get name "int,char,".

View File

@ -3506,7 +3506,7 @@ private:
" typedef int* (Fred::*UnspecifiedBoolType);\n"
" operator UnspecifiedBoolType() { };\n"
"};\n");
ASSERT_EQUALS("[test.cpp:4]: (information) Technically the member function 'Fred::int' can be const.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (information) Technically the member function 'Fred::operatorint**' can be const.\n", errout.str());
checkConst("struct Fred {\n"
" int array[10];\n"

View File

@ -382,8 +382,8 @@ private:
ASSERT_EQUALS("if ( * a )", tok("if ((char)*a)"));
ASSERT_EQUALS("if ( & a )", tok("if ((int)&a)"));
ASSERT_EQUALS("if ( * a )", tok("if ((unsigned int)(unsigned char)*a)"));
ASSERT_EQUALS("class A { A operator * ( int ) ; } ;", tok("class A { A operator *(int); };"));
ASSERT_EQUALS("class A { A operator * ( int ) const ; } ;", tok("class A { A operator *(int) const; };"));
ASSERT_EQUALS("class A { A operator* ( int ) ; } ;", tok("class A { A operator *(int); };"));
ASSERT_EQUALS("class A { A operator* ( int ) const ; } ;", tok("class A { A operator *(int) const; };"));
ASSERT_EQUALS("if ( ! p )", tok("if (p == (char *)(char *)0)"));
}
@ -2578,7 +2578,7 @@ private:
{
// Ticket #1997
const char code[] = "void * operator new[](size_t);";
ASSERT_EQUALS("void * operator new [ ] ( size_t ) ;", tok(code));
ASSERT_EQUALS("void * operatornew[] ( size_t ) ;", tok(code));
}
ASSERT_EQUALS("; a [ 0 ] ;", tok(";a[0*(*p)];"));
@ -4429,7 +4429,7 @@ private:
const std::string expected("struct C { "
"; "
"const void * pr ; " // this gets simplified to a regular pointer
"operator const void ( * ) ( ) & ( ) { return pr ; } "
"operatorconstvoid(*)()& ( ) { return pr ; } "
"} ;");
ASSERT_EQUALS(expected, sizeof_(code));
@ -4713,7 +4713,7 @@ private:
"};\n";
const std::string expected = "class Fred { "
"; "
"operator int * * ( ) const { } "
"operatorint** ( ) const { } "
"} ;";
ASSERT_EQUALS(expected, sizeof_(code));
ASSERT_EQUALS("", errout.str());
@ -4755,9 +4755,9 @@ private:
"Fred::operator F() const { }\n";
const std::string expected = "class Fred { "
"; "
"operator int * * ( ) const ; "
"operatorint** ( ) const ; "
"} ; "
"Fred :: operator int * * ( ) const { }";
"Fred :: operatorint** ( ) const { }";
ASSERT_EQUALS(expected, sizeof_(code));
ASSERT_EQUALS("", errout.str());
}

View File

@ -293,6 +293,8 @@ private:
// Tokenize JAVA
TEST_CASE(java);
TEST_CASE(simplifyOperatorName);
}
@ -2990,7 +2992,7 @@ private:
"1: class Foo\n"
"2: {\n"
"3: public:\n"
"4: void operator = ( const Foo & ) ;\n"
"4: void operator= ( const Foo & ) ;\n"
"5: } ;\n");
ASSERT_EQUALS(expected, actual);
@ -3002,7 +3004,7 @@ private:
"};\n");
const std::string expected("\n\n##file 0\n"
"1: struct Foo {\n"
"2: void * operator new [ ] ( int ) ;\n"
"2: void * operatornew[] ( int ) ;\n"
"3: } ;\n");
ASSERT_EQUALS(expected, actual);
@ -3660,7 +3662,7 @@ private:
const std::string actual(tokenizeAndStringify(code));
const char expected[] = "struct foo {\n"
"void operator delete ( void * obj , size_t sz ) ;\n"
"void operatordelete ( void * obj , size_t sz ) ;\n"
"}";
ASSERT_EQUALS(expected, actual);
@ -5182,6 +5184,38 @@ private:
{
ASSERT_EQUALS("void f ( ) { }", javatest("void f() throws Exception { }"));
}
void simplifyOperatorName()
{
// make sure C code doesn't get changed
const char code1[] = "void operator () {}"
"int main()"
"{"
" operator();"
"}";
const char result1 [] = "void operator ( ) { } "
"int main ( ) "
"{ "
"operator ( ) ; "
"}";
ASSERT_EQUALS(result1, tokenizeAndStringify(code1,false));
const char code2[] = "class Fred"
"{"
" Fred(const Fred & f) { operator = (f); }"
" operator = ();"
"}";
const char result2 [] = "class Fred "
"{ "
"Fred ( const Fred & f ) { operator= ( f ) ; } "
"operator= ( ) ; "
"}";
ASSERT_EQUALS(result2, tokenizeAndStringify(code2,false));
}
};
REGISTER_TEST(TestTokenizer)

View File

@ -460,8 +460,7 @@ private:
" void startListening() {\n"
" }\n"
"};\n");
TODO_ASSERT_EQUALS("[test.cpp:8]: (style) Unused private function 'Fred::startListening'\n", errout.str());
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS("[test.cpp:8]: (style) Unused private function 'Fred::startListening'\n", errout.str());
}
void testDoesNotIdentifyMethodAsFirstFunctionArgument()