Ticket #7117: Properly detect if a const ternary operator is in a template parameter list.

This commit is contained in:
Simon Martin 2016-06-04 20:35:16 +02:00 committed by Daniel Marjamäki
parent 583b340034
commit 3c10b25b3e
2 changed files with 24 additions and 10 deletions

View File

@ -4866,7 +4866,12 @@ bool Tokenizer::simplifyConditions()
bool Tokenizer::simplifyConstTernaryOp()
{
bool ret = false;
const Token *templateParameterEnd = 0; // The end of the current template parameter list, if any
for (Token *tok = list.front(); tok; tok = tok->next()) {
if (tok->str() == "<" && TemplateSimplifier::templateParameters(tok))
templateParameterEnd = tok->findClosingBracket();
if (tok == templateParameterEnd)
templateParameterEnd = 0; // End of the current template parameter list
if (tok->str() != "?")
continue;
@ -4876,20 +4881,18 @@ bool Tokenizer::simplifyConstTernaryOp()
const int offset = (tok->previous()->str() == ")") ? 2 : 1;
bool inTemplateParameter = false;
if (tok->strAt(-2*offset) == "<") {
if (isC() || !TemplateSimplifier::templateParameters(tok->tokAt(-2*offset)))
continue; // '<' is less than; the condition is not a constant
inTemplateParameter = true;
}
// Find the token ":" then go to the next token
Token *semicolon = skipTernaryOp(tok);
if (!semicolon || semicolon->previous()->str() != ":" || !semicolon->next())
Token *colon = skipTernaryOp(tok);
if (!colon || colon->previous()->str() != ":" || !colon->next())
continue;
//handle the GNU extension: "x ? : y" <-> "x ? x : y"
if (semicolon->previous() == tok->next())
if (colon->previous() == tok->next())
tok->insertToken(tok->strAt(-offset));
// go back before the condition, if possible
@ -4903,8 +4906,8 @@ bool Tokenizer::simplifyConstTernaryOp()
}
if (Token::Match(tok->next(), "false|0")) {
// Use code after semicolon, remove code before it.
Token::eraseTokens(tok, semicolon);
// Use code after colon, remove code before it.
Token::eraseTokens(tok, colon);
tok = tok->next();
ret = true;
@ -4916,7 +4919,7 @@ bool Tokenizer::simplifyConstTernaryOp()
tok->deleteNext(2);
unsigned int ternaryOplevel = 0;
for (const Token *endTok = semicolon; endTok; endTok = endTok->next()) {
for (const Token *endTok = colon; endTok; endTok = endTok->next()) {
if (Token::Match(endTok, "(|[|{")) {
endTok = endTok->link();
}
@ -4926,10 +4929,10 @@ bool Tokenizer::simplifyConstTernaryOp()
else if (Token::Match(endTok, ")|}|]|;|,|:|>")) {
if (endTok->str() == ":" && ternaryOplevel)
--ternaryOplevel;
else if (endTok->str() == ">" && !inTemplateParameter)
else if (endTok->str() == ">" && !templateParameterEnd)
;
else {
Token::eraseTokens(semicolon->tokAt(-2), endTok);
Token::eraseTokens(colon->tokAt(-2), endTok);
ret = true;
break;
}

View File

@ -88,6 +88,7 @@ private:
TEST_CASE(template53); // #4335 - bail out for valid code
TEST_CASE(template54); // #6587 - memory corruption upon valid code
TEST_CASE(template55); // #6604 - simplify "const const" to "const" in template instantiations
TEST_CASE(template56); // #7117 - const ternary operator simplification as template parameter
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
TEST_CASE(template_unhandled);
TEST_CASE(template_default_parameter);
@ -1011,6 +1012,16 @@ private:
"A<int> a(0);"));
}
void template56() { // #7117
tok("template<bool B> struct Foo { "
" std::array<int, B ? 1 : 2> mfoo; "
"}; "
"void foo() { "
" Foo<true> myFoo; "
"}", /*simplify=*/true, /*debugwarnings=*/true);
ASSERT_EQUALS("", errout.str());
}
void template_enum() {
const char code1[] = "template <class T>\n"
"struct Unconst {\n"