From 5953ed7318bc48b8ff65230f34fc7ee57d920adc Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Mon, 23 Jan 2012 16:10:15 +0100 Subject: [PATCH] Fixed ticket #3528 (Tokenizer: improve simplifyFunctionParameters to take count of square brackets) --- lib/tokenize.cpp | 139 ++++++++++++++++++++++++------------ lib/tokenize.h | 3 +- test/testsimplifytokens.cpp | 24 +------ test/testtokenize.cpp | 32 ++++++++- 4 files changed, 126 insertions(+), 72 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 55d93a047..673c68d4a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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 argumentNames; + // First step: Get list of argument names in parenthesis + std::map 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 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() diff --git a/lib/tokenize.h b/lib/tokenize.h index ec3a6ae74..815f226a1 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -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 diff --git a/test/testsimplifytokens.cpp b/test/testsimplifytokens.cpp index dbea633a9..c55af0ee1 100644 --- a/test/testsimplifytokens.cpp +++ b/test/testsimplifytokens.cpp @@ -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\");")); diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 40aa1723c..0214fa840 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -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()"