From f0649ab46a5235904ea6f9b95aa07cb9375fd463 Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Tue, 8 Jul 2025 23:14:37 +0900 Subject: [PATCH] Make `defined? (x;)` return `expression` when using parse.y parser Follow up [Bug #21029]. Currently, `defined? (x;)` returns `expression` when using Prism parser. See: - https://github.com/ruby/ruby/pull/12949 - https://bugs.ruby-lang.org/issues/21029 However, `defined? (x;)` returns nil when using parse.y, as reported in bug ticket comment and test-all (when using parse.y parser) test result. This change adds a context flag to track trailing semicolons in defined? scope. When a trailing semicolon is detected within a defined? scope, the generated AST node is wrapped with NODE_BLOCK. This change ensures consistent behavior with `defined? (;x)` . --- parse.y | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/parse.y b/parse.y index 3138061b98..7b36caf78d 100644 --- a/parse.y +++ b/parse.y @@ -314,6 +314,7 @@ struct lex_context { unsigned int in_argdef: 1; unsigned int in_def: 1; unsigned int in_class: 1; + unsigned int has_trailing_semicolon: 1; BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2); BITFIELD(enum rescue_context, in_rescue, 2); unsigned int cant_return: 1; @@ -4041,6 +4042,7 @@ arg : asgn(arg_rhs) { p->ctxt.in_defined = $3.in_defined; $$ = new_defined(p, $4, &@$); + p->ctxt.has_trailing_semicolon = $3.has_trailing_semicolon; /*% ripper: defined!($:4) %*/ } | def_endless_method(endless_arg) @@ -4428,6 +4430,7 @@ primary : inline_primary { p->ctxt.in_defined = $4.in_defined; $$ = new_defined(p, $5, &@$); + p->ctxt.has_trailing_semicolon = $4.has_trailing_semicolon; /*% ripper: defined!($:5) %*/ } | keyword_not '(' expr rparen @@ -6706,7 +6709,14 @@ trailer : '\n'? | ',' ; -term : ';' {yyerrok;token_flush(p);} +term : ';' + { + yyerrok; + token_flush(p); + if (p->ctxt.in_defined) { + p->ctxt.has_trailing_semicolon = 1; + } + } | '\n' { @$.end_pos = @$.beg_pos; @@ -13013,6 +13023,9 @@ kwd_append(rb_node_kw_arg_t *kwlist, rb_node_kw_arg_t *kw) static NODE * new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc) { + int had_trailing_semicolon = p->ctxt.has_trailing_semicolon; + p->ctxt.has_trailing_semicolon = 0; + NODE *n = expr; while (n) { if (nd_type_p(n, NODE_BEGIN)) { @@ -13025,6 +13038,12 @@ new_defined(struct parser_params *p, NODE *expr, const YYLTYPE *loc) break; } } + + if (had_trailing_semicolon && !nd_type_p(expr, NODE_BLOCK)) { + NODE *block = NEW_BLOCK(expr, loc); + return NEW_DEFINED(block, loc); + } + return NEW_DEFINED(n, loc); }