8526: Fix ast construction for ternary operator
This tries to decide a bit more properly when ':' can be part of a ternary operator. More precisely, there are some times when we want to delay the construction of the ast for ':', so that it is place accordingly to the matching '?'. Typically, this fixes an issue with `return val < 0 ? throw 1 : val;`, where the ast for ':' would be constructed during as part of the `throw`, and the ast for `?` would be invalid. This patch is a bit of a hardcode, stating that we don't expect ':' inside a throw, unless there is a complete ternary operator in there (there can't be a range based for loop, a case in a switch). When we reach ':', we know we are and the end of the `throw`.
This commit is contained in:
parent
4023a487ff
commit
7ddb7aef7d
|
@ -465,8 +465,9 @@ struct AST_state {
|
||||||
bool cpp;
|
bool cpp;
|
||||||
int assign;
|
int assign;
|
||||||
bool inCase; // true from case to :
|
bool inCase; // true from case to :
|
||||||
|
bool stopAtColon; // help to properly parse ternary operators
|
||||||
const Token *functionCallEndPar;
|
const Token *functionCallEndPar;
|
||||||
explicit AST_state(bool cpp) : depth(0), inArrayAssignment(0), cpp(cpp), assign(0), inCase(false), functionCallEndPar(nullptr) {}
|
explicit AST_state(bool cpp) : depth(0), inArrayAssignment(0), cpp(cpp), assign(0), inCase(false),stopAtColon(false), functionCallEndPar(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Token * skipDecl(Token *tok)
|
static Token * skipDecl(Token *tok)
|
||||||
|
@ -763,7 +764,10 @@ static void compileTerm(Token *&tok, AST_state& state)
|
||||||
if (tok->str() == "case")
|
if (tok->str() == "case")
|
||||||
state.inCase = true;
|
state.inCase = true;
|
||||||
const bool tokIsReturn = tok->str() == "return";
|
const bool tokIsReturn = tok->str() == "return";
|
||||||
|
const bool stopAtColon = state.stopAtColon;
|
||||||
|
state.stopAtColon=true;
|
||||||
compileUnaryOp(tok, state, compileExpression);
|
compileUnaryOp(tok, state, compileExpression);
|
||||||
|
state.stopAtColon=stopAtColon;
|
||||||
if (tokIsReturn)
|
if (tokIsReturn)
|
||||||
state.op.pop();
|
state.op.pop();
|
||||||
if (state.inCase && Token::simpleMatch(tok, ": ;")) {
|
if (state.inCase && Token::simpleMatch(tok, ": ;")) {
|
||||||
|
@ -1211,6 +1215,8 @@ static void compileAssignTernary(Token *&tok, AST_state& state)
|
||||||
// http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator:
|
// http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator:
|
||||||
// "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored."
|
// "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored."
|
||||||
// Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary.
|
// Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary.
|
||||||
|
const bool stopAtColon = state.stopAtColon;
|
||||||
|
state.stopAtColon = false;
|
||||||
if (tok->strAt(1) == ":") {
|
if (tok->strAt(1) == ":") {
|
||||||
state.op.push(nullptr);
|
state.op.push(nullptr);
|
||||||
}
|
}
|
||||||
|
@ -1218,12 +1224,15 @@ static void compileAssignTernary(Token *&tok, AST_state& state)
|
||||||
state.assign = 0;
|
state.assign = 0;
|
||||||
compileBinOp(tok, state, compileAssignTernary);
|
compileBinOp(tok, state, compileAssignTernary);
|
||||||
state.assign = assign;
|
state.assign = assign;
|
||||||
|
state.stopAtColon = stopAtColon;
|
||||||
} else if (tok->str() == ":") {
|
} else if (tok->str() == ":") {
|
||||||
if (state.depth == 1U && state.inCase) {
|
if (state.depth == 1U && state.inCase) {
|
||||||
state.inCase = false;
|
state.inCase = false;
|
||||||
tok = tok->next();
|
tok = tok->next();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (state.stopAtColon)
|
||||||
|
break;
|
||||||
if (state.assign > 0)
|
if (state.assign > 0)
|
||||||
break;
|
break;
|
||||||
compileBinOp(tok, state, compileAssignTernary);
|
compileBinOp(tok, state, compileAssignTernary);
|
||||||
|
|
|
@ -7486,6 +7486,8 @@ private:
|
||||||
ASSERT_EQUALS("check(result_type00,{invalid:?return", testAst("return check() ? result_type {0, 0} : invalid;"));
|
ASSERT_EQUALS("check(result_type00,{invalid:?return", testAst("return check() ? result_type {0, 0} : invalid;"));
|
||||||
ASSERT_EQUALS("x01:?return", testAst("return x ? 0 : 1;"));
|
ASSERT_EQUALS("x01:?return", testAst("return x ? 0 : 1;"));
|
||||||
ASSERT_EQUALS("x00throw:?return", testAst("return x ? 0 : throw 0;")); // #9768
|
ASSERT_EQUALS("x00throw:?return", testAst("return x ? 0 : throw 0;")); // #9768
|
||||||
|
ASSERT_EQUALS("val0<1throwval:?return", testAst("return val < 0 ? throw 1 : val;")); // #8526
|
||||||
|
ASSERT_EQUALS("ix0<00throw:?=", testAst("int i = x < 0 ? 0 : throw 0;"));
|
||||||
|
|
||||||
ASSERT_EQUALS("a\"\"=", testAst("a=\"\""));
|
ASSERT_EQUALS("a\"\"=", testAst("a=\"\""));
|
||||||
ASSERT_EQUALS("a\'\'=", testAst("a=\'\'"));
|
ASSERT_EQUALS("a\'\'=", testAst("a=\'\'"));
|
||||||
|
|
Loading…
Reference in New Issue