Fixed ticket #3528 (Tokenizer: improve simplifyFunctionParameters to take count of square brackets)

This commit is contained in:
Edoardo Prezioso 2012-01-23 16:10:15 +01:00
parent 9f139cf414
commit 5953ed7318
4 changed files with 126 additions and 72 deletions

View File

@ -1992,7 +1992,8 @@ bool Tokenizer::tokenize(std::istream &code,
// Convert K&R function declarations to modern C
simplifyVarDecl(true);
simplifyFunctionParameters();
if (!simplifyFunctionParameters())
return false;
// check for simple syntax errors..
for (const Token *tok = _tokens; tok; tok = tok->next()) {
@ -4801,7 +4802,7 @@ void Tokenizer::simplifyCasts()
}
void Tokenizer::simplifyFunctionParameters()
bool Tokenizer::simplifyFunctionParameters()
{
for (Token *tok = _tokens; tok; tok = tok->next()) {
if (tok->str() == "{" || tok->str() == "[" || tok->str() == "(") {
@ -4812,79 +4813,123 @@ void Tokenizer::simplifyFunctionParameters()
else if (Token::Match(tok, "%var% ( %var% [,)]")) {
// We have found old style function, now we need to change it
// backup pointer to the '(' token
Token * const tok1 = tok->next();
// Get list of argument names
std::map<std::string, Token*> argumentNames;
// First step: Get list of argument names in parenthesis
std::map<std::string, Token *> argumentNames;
bool bailOut = false;
for (tok = tok->tokAt(2); tok; tok = tok->tokAt(2)) {
if (!Token::Match(tok, "%var% [,)]")) {
Token * tokparam = NULL;
//floating token used to check for parameters
Token *tok1 = tok;
//goto '('
tok = tok->next();
while (NULL != (tok1 = tok1->tokAt(2))) {
if (!Token::Match(tok1, "%var% [,)]")) {
bailOut = true;
break;
}
if (argumentNames.find(tok->str()) != argumentNames.end()) {
// Invalid code, two arguments with the same name.
// syntaxError(tok);
// If you uncomment it, testrunner will fail:
// testclass.cpp:3910
// because of void Fred::foo5(int, int).
// how should this be handled?
bailOut = true;
break;
}
//same parameters: take note of the parameter
if (argumentNames.find(tok1->str()) != argumentNames.end())
tokparam = tok1;
else
argumentNames[tok1->str()] = tok1;
argumentNames[tok->str()] = tok;
if (tok->next()->str() == ")") {
tok = tok->tokAt(2);
if (tok1->next()->str() == ")") {
tok1 = tok1->tokAt(2);
//expect at least a type name after round brace..
if (!tok1 || !tok1->isName())
bailOut = true;
break;
}
}
if (bailOut) {
tok = tok1->link();
tok = tok->link();
continue;
}
Token *start = tok;
while (tok && tok->str() != "{") {
if (tok->str() == ";") {
tok = tok->previous();
// Move tokens from start to tok into the place of
// argumentNames[tok->str()] and remove the ";"
//there should be the sequence '; {' after the round parenthesis
if (!Token::findsimplematch(tok1, "; {")) {
tok = tok->link();
continue;
}
if (argumentNames.find(tok->str()) == argumentNames.end()) {
// Last step: check out if the declarations between ')' and '{' match the parameters list
std::map<std::string, Token *> argumentNames2;
while (tok1 && tok1->str() != "{") {
if (tok1->str() == ";") {
if (tokparam) {
syntaxError(tokparam);
return false;
}
Token *tok2 = tok1->previous();
while (tok2->str() == "]")
tok2 = tok2->link()->previous();
//it should be a name..
if (!tok2->isName()) {
bailOut = true;
break;
}
// Remove the following ";"
Token *temp = tok->tokAt(2);
tok->deleteNext();
if (argumentNames2.find(tok2->str()) != argumentNames2.end()) {
//same parameter names...
syntaxError(tok1);
return false;
} else
argumentNames2[tok2->str()] = tok2;
// Replace "x" with "int x" or similar
Token::replace(argumentNames[tok->str()], start, tok);
argumentNames.erase(tok->str());
tok = temp;
start = tok;
} else {
tok = tok->next();
if (argumentNames.find(tok2->str()) == argumentNames.end()) {
//non-matching parameter... bailout
bailOut = true;
break;
}
}
}
if (Token::simpleMatch(tok, "{"))
tok = tok->link();
if (tok == NULL) {
break;
tok1 = tok1->next();
}
if (bailOut) {
tok = tok->link();
continue;
}
//the two containers should hold the same size..
if (argumentNames.size() != argumentNames2.size()) {
tok = tok->link();
//error if the second container is not empty
if (!argumentNames2.empty()) {
syntaxError(tok);
return false;
}
continue;
}
while (tok->str() != ")") {
//initialize start and end tokens to be moved
Token *declStart = argumentNames2[tok->next()->str()];
Token *declEnd = declStart;
while (declStart->previous()->str() != ";" && declStart->previous()->str() != ")")
declStart = declStart->previous();
while (declEnd->next()->str() != ";" && declEnd->next()->str() != "{")
declEnd = declEnd->next();
//remove ';' after declaration
declEnd->deleteNext();
//replace the parameter name in the parenthesis with all the declaration
Token::replace(tok->next(), declStart, declEnd);
//since there are changes to tokens, put tok where tok1 is
tok = declEnd->next();
}
//goto forward and continue
tok = tok->next()->link();
}
}
return true;
}
void Tokenizer::simplifyPointerToStandardType()

View File

@ -449,8 +449,9 @@ public:
/**
* Simplify functions like "void f(x) int x; {"
* into "void f(int x) {"
* @return false only if there's a syntax error
*/
void simplifyFunctionParameters();
bool simplifyFunctionParameters();
/**
* Simplify templates

View File

@ -170,11 +170,10 @@ private:
TEST_CASE(strcat1);
TEST_CASE(strcat2);
// Syntax error
TEST_CASE(argumentsWithSameName)
TEST_CASE(simplifyAtol)
TEST_CASE(simplifyHexInString)
TEST_CASE(simplifyTypedef1)
TEST_CASE(simplifyTypedef2)
TEST_CASE(simplifyTypedef3)
@ -3392,25 +3391,6 @@ private:
ASSERT_EQUALS(expect, tok(code));
}
void argumentsWithSameName() {
// This code has syntax error, two variables can not have the same name
{
const char code[] = "void foo(x, x)\n"
" int x;\n"
" int x;\n"
"{}\n";
ASSERT_EQUALS("void foo ( x , x ) int x ; int x ; { }", tok(code));
}
{
const char code[] = "void foo(x, y)\n"
" int x;\n"
" int x;\n"
"{}\n";
ASSERT_EQUALS("void foo ( int x , y ) int x ; { }", tok(code));
}
}
void simplifyAtol() {
ASSERT_EQUALS("a = std :: atol ( x ) ;", tok("a = std::atol(x);"));
ASSERT_EQUALS("a = atol ( \"text\" ) ;", tok("a = atol(\"text\");"));

View File

@ -240,7 +240,8 @@ private:
TEST_CASE(macrodoublesharp);
TEST_CASE(simplify_function_parameters);
TEST_CASE(simplifyFunctionParameters);
TEST_CASE(simplifyFunctionParametersErrors);
TEST_CASE(removeParentheses1); // Ticket #61
TEST_CASE(removeParentheses2);
@ -3985,7 +3986,7 @@ private:
ASSERT_EQUALS("DBG ( fmt , args . . . ) printf ( fmt , ## args ) ", ostr.str());
}
void simplify_function_parameters() {
void simplifyFunctionParameters() {
{
const char code[] = "char a [ ABC ( DEF ) ] ;";
ASSERT_EQUALS(code, tokenizeAndStringify(code, true));
@ -3998,6 +3999,7 @@ private:
ASSERT_EQUALS("void f ( int x ) { }", tokenizeAndStringify("void f(x) int x; { }", true));
ASSERT_EQUALS("void f ( int x , char y ) { }", tokenizeAndStringify("void f(x,y) int x; char y; { }", true));
ASSERT_EQUALS("int main ( int argc , char * argv [ ] ) { }", tokenizeAndStringify("int main(argc,argv) int argc; char *argv[]; { }", true));
// #1067 - Not simplified. Feel free to fix so it is simplified correctly but this syntax is obsolete.
ASSERT_EQUALS("int ( * d ( a , b , c ) ) ( ) int a ; int b ; int c ; { }", tokenizeAndStringify("int (*d(a,b,c))()int a,b,c; { }", true));
@ -4014,6 +4016,32 @@ private:
}
}
void simplifyFunctionParametersErrors() {
//same parameters...
tokenizeAndStringify("void foo(x, x)\n"
" int x;\n"
" int x;\n"
"{}\n");
ASSERT_EQUALS("[test.cpp:1]: (error) syntax error\n", errout.str());
tokenizeAndStringify("void foo(x, y)\n"
" int x;\n"
" int x;\n"
"{}\n");
ASSERT_EQUALS("[test.cpp:3]: (error) syntax error\n", errout.str());
tokenizeAndStringify("void foo(int, int)\n"
"{}\n");
ASSERT_EQUALS("", errout.str());
//non-matching arguments after round braces
tokenizeAndStringify("void foo(x, y, z)\n"
" int x;\n"
" int y;\n"
"{}\n");
ASSERT_EQUALS("[test.cpp:1]: (error) syntax error\n", errout.str());
}
// Simplify "((..))" into "(..)"
void removeParentheses1() {
const char code[] = "void foo()"