summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2025-03-22 13:17:27 -0400
committerKevin Newton <kddnewton@gmail.com>2025-03-30 13:22:41 -0400
commit052794bfe1970e90f4f4f9e37fc362dd27903a8d (patch)
tree6f8da94675e07e2ce7507ca45736aedfbe8b74d9
parent6b5aa432913c8aaef96513618d0edc73cdb6141c (diff)
[ruby/prism] Accept a newline after the defined? keyword
[Bug #21197] https://github.com/ruby/prism/commit/22be955ce9
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/12999
-rw-r--r--lib/prism/translation/parser/compiler.rb38
-rw-r--r--lib/prism/translation/ripper.rb17
-rw-r--r--prism/prism.c19
-rw-r--r--test/prism/errors/defined_empty.txt3
-rw-r--r--test/prism/fixtures/defined.txt9
5 files changed, 73 insertions, 13 deletions
diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb
index aa1cb5d20b..c5356555d0 100644
--- a/lib/prism/translation/parser/compiler.rb
+++ b/lib/prism/translation/parser/compiler.rb
@@ -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
diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb
index dce96e01ab..95f366ac91 100644
--- a/lib/prism/translation/ripper.rb
+++ b/lib/prism/translation/ripper.rb
@@ -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
diff --git a/prism/prism.c b/prism/prism.c
index 8597aa63fa..f52008fe1e 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -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);
diff --git a/test/prism/errors/defined_empty.txt b/test/prism/errors/defined_empty.txt
new file mode 100644
index 0000000000..4d7ea76413
--- /dev/null
+++ b/test/prism/errors/defined_empty.txt
@@ -0,0 +1,3 @@
+defined?()
+ ^ expected an expression after `defined?`
+
diff --git a/test/prism/fixtures/defined.txt b/test/prism/fixtures/defined.txt
index 247fa94e3a..09fc0a29e7 100644
--- a/test/prism/fixtures/defined.txt
+++ b/test/prism/fixtures/defined.txt
@@ -8,3 +8,12 @@ defined? 1
defined?("foo"
)
+
+defined?
+1
+
+defined?
+(1)
+
+defined?
+()