Improve support for labels in simplifyAddBraces step (#3278)

Previously only a single regular label before a compound statement was
allowed in simplifyAddBracesPair() after if/switch/do/while/for.
This patch adds support for:
* case-labels;
* labels before a single statement;
* labels before try/catch blocks;
* multiple consecutive labels.

Additionally the code for skipping a case label was extracted into a
separate function from simplifyLabelsCaseDefault() and reused in
simplifyAddBracesPair().
This commit is contained in:
dummyunit 2021-06-02 08:00:37 +03:00 committed by GitHub
parent f64011b669
commit 9652ca39a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 247 additions and 39 deletions

View File

@ -3168,6 +3168,27 @@ static Token *skipTernaryOp(Token *tok)
return tok;
}
// Skips until the colon at the end of the case label, the argument must point to the "case" token.
// In case of success returns the colon token.
// In case of failure returns the token that caused the error.
static Token *skipCaseLabel(Token *tok)
{
assert(tok->str() == "case");
while (nullptr != (tok = tok->next())) {
if (Token::Match(tok, "(|["))
tok = tok->link();
else if (tok->str() == "?") {
Token * tok1 = skipTernaryOp(tok);
if (!tok1)
return tok;
tok = tok1;
}
if (Token::Match(tok, "[:{};]"))
return tok;
}
return nullptr;
}
const Token * Tokenizer::startOfExecutableScope(const Token * tok)
{
if (tok->str() != ")")
@ -3217,32 +3238,15 @@ void Tokenizer::simplifyLabelsCaseDefault()
tok = tok->link();
if (Token::Match(tok, "[;{}:] case")) {
while (nullptr != (tok = tok->next())) {
if (Token::Match(tok, "(|[")) {
tok = tok->link();
} else if (tok->str() == "?") {
Token *tok1 = skipTernaryOp(tok);
if (!tok1) {
syntaxError(tok);
}
tok = tok1;
}
if (Token::Match(tok->next(),"[:{};]"))
break;
}
tok = skipCaseLabel(tok->next());
if (!tok)
break;
if (tok->str() != "case" && tok->next() && tok->next()->str() == ":") {
tok = tok->next();
if (!tok->next())
if (tok->str() != ":" || tok->strAt(-1) == "case" || !tok->next())
syntaxError(tok);
if (tok->next()->str() != ";" && tok->next()->str() != "case")
tok->insertToken(";");
else
tok = tok->previous();
} else {
syntaxError(tok);
}
} else if (Token::Match(tok, "[;{}] %name% : !!;")) {
if (!cpp || !Token::Match(tok->next(), "class|struct|enum")) {
tok = tok->tokAt(2);
@ -6267,30 +6271,46 @@ Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition)
return tok;
}
}
// Skip labels
Token * tokStatement = tokAfterCondition;
while (true) {
if (Token::Match(tokStatement, "%name% :"))
tokStatement = tokStatement->tokAt(2);
else if (tokStatement->str() == "case") {
tokStatement = skipCaseLabel(tokStatement);
if (!tokStatement)
return tok;
if (tokStatement->str() != ":")
syntaxError(tokStatement);
tokStatement = tokStatement->next();
} else
break;
if (!tokStatement)
return tok;
}
Token * tokBracesEnd=nullptr;
if (tokAfterCondition->str()=="{") {
if (tokStatement->str() == "{") {
// already surrounded by braces
tokBracesEnd=tokAfterCondition->link();
} else if (Token::simpleMatch(tokAfterCondition, "try {") &&
Token::simpleMatch(tokAfterCondition->linkAt(1), "} catch (")) {
if (tokStatement != tokAfterCondition) {
// Move the opening brace before labels
Token::move(tokStatement, tokStatement, tokAfterCondition->previous());
}
tokBracesEnd = tokStatement->link();
} else if (Token::simpleMatch(tokStatement, "try {") &&
Token::simpleMatch(tokStatement->linkAt(1), "} catch (")) {
tokAfterCondition->previous()->insertToken("{");
Token * tokOpenBrace = tokAfterCondition->previous();
Token * tokEnd = tokAfterCondition->linkAt(1)->linkAt(2)->linkAt(1);
Token * tokEnd = tokStatement->linkAt(1)->linkAt(2)->linkAt(1);
if (!tokEnd) {
syntaxError(tokAfterCondition);
syntaxError(tokStatement);
}
tokEnd->insertToken("}");
Token * tokCloseBrace = tokEnd->next();
Token::createMutualLinks(tokOpenBrace, tokCloseBrace);
tokBracesEnd = tokCloseBrace;
} else if (Token::Match(tokAfterCondition, "%name% : {")) {
tokAfterCondition->previous()->insertToken("{");
tokAfterCondition->linkAt(2)->insertToken("}");
tokBracesEnd = tokAfterCondition->linkAt(2)->next();
Token::createMutualLinks(tokAfterCondition->previous(), tokBracesEnd);
} else {
Token * tokEnd = simplifyAddBracesToCommand(tokAfterCondition);
Token * tokEnd = simplifyAddBracesToCommand(tokStatement);
if (!tokEnd) // Ticket #4887
return tok;
if (tokEnd->str()!="}") {

View File

@ -124,13 +124,19 @@ private:
TEST_CASE(ifAddBraces18); // #3424 - if if { } else else
TEST_CASE(ifAddBraces19); // #3928 - if for if else
TEST_CASE(ifAddBraces20); // #5012 - syntax error 'else }'
TEST_CASE(ifAddBraces21); // #5332 - if (x) label: {} ..
TEST_CASE(ifAddBracesLabels); // #5332 - if (x) label: {} ..
TEST_CASE(switchAddBracesLabels);
TEST_CASE(whileAddBraces);
TEST_CASE(whileAddBracesLabels);
TEST_CASE(doWhileAddBraces);
TEST_CASE(doWhileAddBracesLabels);
TEST_CASE(forAddBraces1);
TEST_CASE(forAddBraces2); // #5088
TEST_CASE(forAddBracesLabels);
TEST_CASE(simplifyExternC);
TEST_CASE(simplifyKeyword); // #5842 - remove C99 static keyword between []
@ -1227,9 +1233,76 @@ private:
ASSERT_THROW(tokenizeAndStringify(code), InternalError);
}
void ifAddBraces21() { // #5332 - if (x) label: {} ...
const char code[] = "void f() { if(x) label: {} a=1; }";
ASSERT_EQUALS("void f ( ) { if ( x ) { label : ; { } } a = 1 ; }", tokenizeAndStringify(code));
void ifAddBracesLabels() {
// Labels before statement
ASSERT_EQUALS("int f ( int x ) {\n"
"if ( x ) {\n"
"l1 : ; l2 : ; return x ; }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" if (x)\n"
" l1: l2: return x;\n"
"}"));
// Labels before {
ASSERT_EQUALS("int f ( int x ) {\n"
"if ( x )\n"
"{ l1 : ; l2 : ; return x ; }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" if (x)\n"
" l1: l2: { return x; }\n"
"}"));
// Labels before try/catch
ASSERT_EQUALS("int f ( int x ) {\n"
"if ( x ) {\n"
"l1 : ; l2 : ;\n"
"try { throw 1 ; }\n"
"catch ( ... ) { return x ; } }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" if (x)\n"
" l1: l2:\n"
" try { throw 1; }\n"
" catch(...) { return x; }\n"
"}"));
}
void switchAddBracesLabels() {
// Labels before statement
ASSERT_EQUALS("int f ( int x ) {\n"
"switch ( x ) {\n"
"l1 : ; case 0 : ; l2 : ; case ( 1 ) : ; return x ; }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" switch (x)\n"
" l1: case 0: l2: case (1): return x;\n"
"}"));
// Labels before {
ASSERT_EQUALS("int f ( int x ) {\n"
"switch ( x )\n"
"{ l1 : ; case 0 : ; l2 : ; case ( 1 ) : ; return x ; }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" switch (x)\n"
" l1: case 0: l2: case (1): { return x; }\n"
"}"));
// Labels before try/catch
ASSERT_EQUALS("int f ( int x ) {\n"
"switch ( x ) {\n"
"l1 : ; case 0 : ; l2 : ; case ( 1 ) : ;\n"
"try { throw 1 ; }\n"
"catch ( ... ) { return x ; } }\n"
"}",
tokenizeAndStringify("int f(int x) {\n"
" switch (x)\n"
" l1: case 0: l2: case (1):\n"
" try { throw 1; }\n"
" catch(...) { return x; }\n"
"}"));
}
void whileAddBraces() {
@ -1237,6 +1310,42 @@ private:
ASSERT_EQUALS("{ while ( a ) { ; } }", tokenizeAndStringify(code));
}
void whileAddBracesLabels() {
// Labels before statement
ASSERT_EQUALS("void f ( int x ) {\n"
"while ( x ) {\n"
"l1 : ; l2 : ; -- x ; }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" while (x)\n"
" l1: l2: --x;\n"
"}"));
// Labels before {
ASSERT_EQUALS("void f ( int x ) {\n"
"while ( x )\n"
"{ l1 : ; l2 : ; -- x ; }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" while (x)\n"
" l1: l2: { -- x; }\n"
"}"));
// Labels before try/catch
ASSERT_EQUALS("void f ( int x ) {\n"
"while ( x ) {\n"
"l1 : ; l2 : ;\n"
"try { throw 1 ; }\n"
"catch ( ... ) { -- x ; } }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" while (x)\n"
" l1: l2:\n"
" try { throw 1; }\n"
" catch(...) { --x; }\n"
"}"));
}
void doWhileAddBraces() {
{
const char code[] = "{do ; while (0);}";
@ -1307,6 +1416,48 @@ private:
}
}
void doWhileAddBracesLabels() {
// Labels before statement
ASSERT_EQUALS("void f ( int x ) {\n"
"do {\n"
"l1 : ; l2 : ; -- x ; }\n"
"while ( x ) ;\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" do\n"
" l1: l2: --x;\n"
" while (x);\n"
"}"));
// Labels before {
ASSERT_EQUALS("void f ( int x ) {\n"
"do\n"
"{ l1 : ; l2 : ; -- x ; }\n"
"while ( x ) ;\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" do\n"
" l1: l2: { -- x; }\n"
" while (x);\n"
"}"));
// Labels before try/catch
ASSERT_EQUALS("void f ( int x ) {\n"
"do {\n"
"l1 : ; l2 : ;\n"
"try { throw 1 ; }\n"
"catch ( ... ) { -- x ; } }\n"
"while ( x ) ;\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" do\n"
" l1: l2:\n"
" try { throw 1; }\n"
" catch(...) { --x; }\n"
" while (x);\n"
"}"));
}
void forAddBraces1() {
{
const char code[] = "void f() {\n"
@ -1349,6 +1500,43 @@ private:
ASSERT_EQUALS(expected, tokenizeAndStringify(code));
}
void forAddBracesLabels() {
// Labels before statement
ASSERT_EQUALS("void f ( int x ) {\n"
"for ( ; x ; ) {\n"
"l1 : ; l2 : ; -- x ; }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" for ( ; x; )\n"
" l1: l2: --x;\n"
"}"));
// Labels before {
ASSERT_EQUALS("void f ( int x ) {\n"
"for ( ; x ; )\n"
"{ l1 : ; l2 : ; -- x ; }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" for ( ; x; )\n"
" l1: l2: { -- x; }\n"
"}"));
// Labels before try/catch
ASSERT_EQUALS("void f ( int x ) {\n"
"for ( ; x ; ) {\n"
"l1 : ; l2 : ;\n"
"try { throw 1 ; }\n"
"catch ( ... ) { -- x ; } }\n"
"}",
tokenizeAndStringify("void f(int x) {\n"
" for ( ; x; )\n"
" l1: l2:\n"
" try { throw 1; }\n"
" catch(...) { --x; }\n"
"}"));
}
void simplifyExternC() {
ASSERT_EQUALS("int foo ( ) ;", tokenizeAndStringify("extern \"C\" int foo();"));
ASSERT_EQUALS("int foo ( ) ;", tokenizeAndStringify("extern \"C\" { int foo(); }"));