зеркало из https://github.com/github/ruby.git
[ruby/prism] Reject statements at non-statement posisions
Fix https://github.com/ruby/prism/pull/1547 https://github.com/ruby/prism/commit/cdb643aeab
This commit is contained in:
Родитель
cc7a5dcd06
Коммит
76f9abced7
|
@ -232,6 +232,10 @@ static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
|
|||
[PM_ERR_RESCUE_TERM] = "Expected a closing delimiter for the `rescue` clause",
|
||||
[PM_ERR_RESCUE_VARIABLE] = "Expected an exception variable after `=>` in a rescue statement",
|
||||
[PM_ERR_RETURN_INVALID] = "Invalid `return` in a class or module body",
|
||||
[PM_ERR_STATEMENT_ALIAS] = "Unexpected an `alias` at a non-statement position",
|
||||
[PM_ERR_STATEMENT_POSTEXE_END] = "Unexpected an `END` at a non-statement position",
|
||||
[PM_ERR_STATEMENT_PREEXE_BEGIN] = "Unexpected a `BEGIN` at a non-statement position",
|
||||
[PM_ERR_STATEMENT_UNDEF] = "Unexpected an `undef` at a non-statement position",
|
||||
[PM_ERR_STRING_CONCATENATION] = "Expected a string for concatenation",
|
||||
[PM_ERR_STRING_INTERPOLATED_TERM] = "Expected a closing delimiter for the interpolated string",
|
||||
[PM_ERR_STRING_LITERAL_TERM] = "Expected a closing delimiter for the string literal",
|
||||
|
|
|
@ -226,6 +226,10 @@ typedef enum {
|
|||
PM_ERR_RESCUE_TERM,
|
||||
PM_ERR_RESCUE_VARIABLE,
|
||||
PM_ERR_RETURN_INVALID,
|
||||
PM_ERR_STATEMENT_ALIAS,
|
||||
PM_ERR_STATEMENT_POSTEXE_END,
|
||||
PM_ERR_STATEMENT_PREEXE_BEGIN,
|
||||
PM_ERR_STATEMENT_UNDEF,
|
||||
PM_ERR_STRING_CONCATENATION,
|
||||
PM_ERR_STRING_INTERPOLATED_TERM,
|
||||
PM_ERR_STRING_LITERAL_TERM,
|
||||
|
|
|
@ -14214,6 +14214,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
|
|||
parser_lex(parser);
|
||||
return (pm_node_t *) pm_source_line_node_create(parser, &parser->previous);
|
||||
case PM_TOKEN_KEYWORD_ALIAS: {
|
||||
if (binding_power != PM_BINDING_POWER_STATEMENT) {
|
||||
pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
|
||||
}
|
||||
|
||||
parser_lex(parser);
|
||||
pm_token_t keyword = parser->previous;
|
||||
|
||||
|
@ -14451,6 +14455,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
|
|||
return (pm_node_t *) begin_node;
|
||||
}
|
||||
case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
|
||||
if (binding_power != PM_BINDING_POWER_STATEMENT) {
|
||||
pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
|
||||
}
|
||||
|
||||
parser_lex(parser);
|
||||
pm_token_t keyword = parser->previous;
|
||||
|
||||
|
@ -14901,6 +14909,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
|
|||
);
|
||||
}
|
||||
case PM_TOKEN_KEYWORD_END_UPCASE: {
|
||||
if (binding_power != PM_BINDING_POWER_STATEMENT) {
|
||||
pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
|
||||
}
|
||||
|
||||
parser_lex(parser);
|
||||
pm_token_t keyword = parser->previous;
|
||||
|
||||
|
@ -14983,6 +14995,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
|
|||
parser_lex(parser);
|
||||
return parse_conditional(parser, PM_CONTEXT_IF);
|
||||
case PM_TOKEN_KEYWORD_UNDEF: {
|
||||
if (binding_power != PM_BINDING_POWER_STATEMENT) {
|
||||
pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
|
||||
}
|
||||
|
||||
parser_lex(parser);
|
||||
pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
|
||||
pm_node_t *name = parse_undef_argument(parser);
|
||||
|
@ -16760,6 +16776,19 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagn
|
|||
return node;
|
||||
}
|
||||
|
||||
// The statements BEGIN { ... }, END { ... }, alias ..., and undef ... are statement.
|
||||
// They cannot follow operators, but they can follow modifiers.
|
||||
bool is_statement =
|
||||
PM_NODE_TYPE_P(node, PM_PRE_EXECUTION_NODE) || PM_NODE_TYPE_P(node, PM_POST_EXECUTION_NODE) ||
|
||||
PM_NODE_TYPE_P(node, PM_ALIAS_GLOBAL_VARIABLE_NODE) || PM_NODE_TYPE_P(node, PM_ALIAS_METHOD_NODE) ||
|
||||
PM_NODE_TYPE_P(node, PM_UNDEF_NODE);
|
||||
// TODO: the right condition should `pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER_RESCUE` instead.
|
||||
// However, it does not work because of the `rescue` modifier's binding power trick.
|
||||
// After getting to merge #1879, this TODO can be removed.
|
||||
if (is_statement && pm_binding_powers[parser->current.type].right > PM_BINDING_POWER_MODIFIER_RESCUE + 1) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Otherwise we'll look and see if the next token can be parsed as an infix
|
||||
// operator. If it can, then we'll parse it using parse_expression_infix.
|
||||
pm_binding_powers_t current_binding_powers;
|
||||
|
|
|
@ -1722,6 +1722,54 @@ module Prism
|
|||
]
|
||||
end
|
||||
|
||||
def test_statement_operators
|
||||
source = <<~RUBY
|
||||
alias x y + 1
|
||||
alias x y.z
|
||||
BEGIN { bar } + 1
|
||||
BEGIN { bar }.z
|
||||
END { bar } + 1
|
||||
END { bar }.z
|
||||
undef x + 1
|
||||
undef x.z
|
||||
RUBY
|
||||
message1 = 'Expected a newline or semicolon after the statement'
|
||||
message2 = 'Cannot parse the expression'
|
||||
assert_errors expression(source), source, [
|
||||
[message1, 9..9],
|
||||
[message2, 9..9],
|
||||
[message1, 23..23],
|
||||
[message2, 23..23],
|
||||
[message1, 39..39],
|
||||
[message2, 39..39],
|
||||
[message1, 57..57],
|
||||
[message2, 57..57],
|
||||
[message1, 71..71],
|
||||
[message2, 71..71],
|
||||
[message1, 87..87],
|
||||
[message2, 87..87],
|
||||
[message1, 97..97],
|
||||
[message2, 97..97],
|
||||
[message1, 109..109],
|
||||
[message2, 109..109],
|
||||
]
|
||||
end
|
||||
|
||||
def test_statement_at_non_statement
|
||||
source = <<~RUBY
|
||||
foo(alias x y)
|
||||
foo(BEGIN { bar })
|
||||
foo(END { bar })
|
||||
foo(undef x)
|
||||
RUBY
|
||||
assert_errors expression(source), source, [
|
||||
['Unexpected an `alias` at a non-statement position', 4..9],
|
||||
['Unexpected a `BEGIN` at a non-statement position', 19..24],
|
||||
['Unexpected an `END` at a non-statement position', 38..41],
|
||||
['Unexpected an `undef` at a non-statement position', 55..60],
|
||||
]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")
|
||||
|
|
Загрузка…
Ссылка в новой задаче