[ruby/prism] Accept a newline after the defined? keyword

[Bug #21197]

22be955ce9
This commit is contained in:
Kevin Newton 2025-03-22 13:17:27 -04:00
parent 6b5aa43291
commit 052794bfe1
Notes: git 2025-03-30 17:22:59 +00:00
5 changed files with 73 additions and 13 deletions

View file

@ -696,13 +696,37 @@ module Prism
# defined?(a)
# ^^^^^^^^^^^
def visit_defined_node(node)
builder.keyword_cmd(
:defined?,
token(node.keyword_loc),
token(node.lparen_loc),
[visit(node.value)],
token(node.rparen_loc)
)
# Very weird circumstances here where something like:
#
# defined?
# (1)
#
# gets parsed in Ruby as having only the `1` expression but in parser
# it gets parsed as having a begin. In this case we need to synthesize
# that begin to match parser's behavior.
if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n")
builder.keyword_cmd(
:defined?,
token(node.keyword_loc),
nil,
[
builder.begin(
token(node.lparen_loc),
visit(node.value),
token(node.rparen_loc)
)
],
nil
)
else
builder.keyword_cmd(
:defined?,
token(node.keyword_loc),
token(node.lparen_loc),
[visit(node.value)],
token(node.rparen_loc)
)
end
end
# if foo then bar else baz end

View file

@ -1615,8 +1615,23 @@ module Prism
# defined?(a)
# ^^^^^^^^^^^
def visit_defined_node(node)
expression = visit(node.value)
# Very weird circumstances here where something like:
#
# defined?
# (1)
#
# gets parsed in Ruby as having only the `1` expression but in Ripper it
# gets parsed as having a parentheses node. In this case we need to
# synthesize that node to match Ripper's behavior.
if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n")
bounds(node.lparen_loc.join(node.rparen_loc))
expression = on_paren(on_stmts_add(on_stmts_new, expression))
end
bounds(node.location)
on_defined(visit(node.value))
on_defined(expression)
end
# if foo then bar else baz end

View file

@ -19607,18 +19607,27 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_token_t lparen;
pm_token_t rparen;
pm_node_t *expression;
context_push(parser, PM_CONTEXT_DEFINED);
bool newline = accept1(parser, PM_TOKEN_NEWLINE);
if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
lparen = parser->previous;
expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
if (parser->recovering) {
if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
expression = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0);
lparen = not_provided(parser);
rparen = not_provided(parser);
} else {
accept1(parser, PM_TOKEN_NEWLINE);
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
rparen = parser->previous;
expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
if (parser->recovering) {
rparen = not_provided(parser);
} else {
accept1(parser, PM_TOKEN_NEWLINE);
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
rparen = parser->previous;
}
}
} else {
lparen = not_provided(parser);

View file

@ -0,0 +1,3 @@
defined?()
^ expected an expression after `defined?`

View file

@ -8,3 +8,12 @@ defined? 1
defined?("foo"
)
defined?
1
defined?
(1)
defined?
()