diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index 062096feee50a1..00000000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-# YJIT sources and tests
-yjit* @ruby/yjit
-yjit/**/* @ruby/yjit
-doc/yjit/* @ruby/yjit
-bootstraptest/test_yjit* @ruby/yjit
-test/ruby/test_yjit* @ruby/yjit
-yjit/src/cruby_bindings.inc.rs
diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml
new file mode 100644
index 00000000000000..8726df577d39eb
--- /dev/null
+++ b/.github/auto_request_review.yml
@@ -0,0 +1,13 @@
+files:
+ 'yjit*': [team:yjit]
+ 'yjit/**/*': [team:yjit]
+ 'yjit/src/cruby_bindings.inc.rs': []
+ 'doc/yjit/*': [team:yjit]
+ 'bootstraptest/test_yjit*': [team:yjit]
+ 'test/ruby/test_yjit*': [team:yjit]
+options:
+ ignore_draft: true
+ # This currently doesn't work as intended. We want to skip reviews when only
+ # cruby_bingings.inc.rs is modified, but this skips reviews even when other
+ # yjit files are modified as well. To be enabled after fixing the behavior.
+ #last_files_match_only: true
diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml
new file mode 100644
index 00000000000000..ca27244b46547b
--- /dev/null
+++ b/.github/workflows/auto_request_review.yml
@@ -0,0 +1,19 @@
+name: Auto Request Review
+on:
+ pull_request_target:
+ types: [opened, ready_for_review, reopened]
+
+permissions:
+ contents: read
+
+jobs:
+ auto-request-review:
+ name: Auto Request Review
+ runs-on: ubuntu-latest
+ if: ${{ github.repository == 'ruby/ruby' && github.base_ref == 'master' }}
+ steps:
+ - name: Request review based on files changes and/or groups the author belongs to
+ uses: necojackarc/auto-request-review@e89da1a8cd7c8c16d9de9c6e763290b6b0e3d424 # v0.13.0
+ with:
+ # scope: public_repo
+ token: ${{ secrets.MATZBOT_GITHUB_TOKEN }}
diff --git a/NEWS.md b/NEWS.md
index 77e48d8ca0ef38..b8e7d75564fb62 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -43,6 +43,7 @@ The following default gems are updated.
* fiddle 1.1.3
* io-console 0.7.2
* irb 1.12.0
+* json 2.7.2
* net-http 0.4.1
* prism 0.24.0
* rdoc 6.6.3.1
diff --git a/README.md b/README.md
index 8fb3786691f052..eb24a73ee3e3e5 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,6 @@
[![Actions Status: RJIT](https://github.com/ruby/ruby/workflows/RJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
-[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
# What is Ruby?
diff --git a/ast.c b/ast.c
index f716bb995c6246..70f298c7f8adf5 100644
--- a/ast.c
+++ b/ast.c
@@ -183,29 +183,6 @@ node_find(VALUE self, const int node_id)
extern VALUE rb_e_script;
-VALUE
-rb_script_lines_for(VALUE path, bool add)
-{
- VALUE hash, lines;
- ID script_lines;
- CONST_ID(script_lines, "SCRIPT_LINES__");
- if (!rb_const_defined_at(rb_cObject, script_lines)) return Qnil;
- hash = rb_const_get_at(rb_cObject, script_lines);
- if (!RB_TYPE_P(hash, T_HASH)) return Qnil;
- if (add) {
- rb_hash_aset(hash, path, lines = rb_ary_new());
- }
- else if (!RB_TYPE_P((lines = rb_hash_lookup(hash, path)), T_ARRAY)) {
- return Qnil;
- }
- return lines;
-}
-static VALUE
-script_lines(VALUE path)
-{
- return rb_script_lines_for(path, false);
-}
-
static VALUE
node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
{
@@ -267,7 +244,7 @@ ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script
rb_raise(rb_eArgError, "cannot get AST for method defined in eval");
}
- if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
+ if (!NIL_P(lines)) {
node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant, keep_tokens);
}
else if (e_option) {
@@ -551,6 +528,8 @@ node_children(rb_ast_t *ast, const NODE *node)
name[1] = (char)RNODE_BACK_REF(node)->nd_nth;
name[2] = '\0';
return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
+ case NODE_MATCH:
+ return rb_ary_new_from_args(1, rb_node_regx_string_val(node));
case NODE_MATCH2:
if (RNODE_MATCH2(node)->nd_args) {
return rb_ary_new_from_node_args(ast, 3, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value, RNODE_MATCH2(node)->nd_args);
@@ -558,9 +537,6 @@ node_children(rb_ast_t *ast, const NODE *node)
return rb_ary_new_from_node_args(ast, 2, RNODE_MATCH2(node)->nd_recv, RNODE_MATCH2(node)->nd_value);
case NODE_MATCH3:
return rb_ary_new_from_node_args(ast, 2, RNODE_MATCH3(node)->nd_recv, RNODE_MATCH3(node)->nd_value);
- case NODE_MATCH:
- case NODE_LIT:
- return rb_ary_new_from_args(1, RNODE_LIT(node)->nd_lit);
case NODE_STR:
case NODE_XSTR:
return rb_ary_new_from_args(1, rb_node_str_string_val(node));
diff --git a/bootstraptest/test_literal_suffix.rb b/bootstraptest/test_literal_suffix.rb
index 5d813d581866e6..7a4d67d0fac3e3 100644
--- a/bootstraptest/test_literal_suffix.rb
+++ b/bootstraptest/test_literal_suffix.rb
@@ -47,8 +47,8 @@
'1.0000000000000000001r'
assert_equal 'unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\A:(?:\d+:)? syntax error,|\^) (.*)/, 1] end}
+ %q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
assert_equal 'unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\A:(?:\d+:)? syntax error,|\^) (.*)/, 1] end}
+ %q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
assert_equal 'unexpected local variable or method, expecting end-of-input',
- %q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\A:(?:\d+:)? syntax error,|\^) (.*)/, 1] end}
+ %q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)? syntax error,) (.*)/, 1]; end}
diff --git a/bootstraptest/test_syntax.rb b/bootstraptest/test_syntax.rb
index 44bd697d4f9848..fbc9c6f62e377a 100644
--- a/bootstraptest/test_syntax.rb
+++ b/bootstraptest/test_syntax.rb
@@ -529,7 +529,7 @@ def lines
}
def assert_syntax_error expected, code, message = ''
assert_match /^#{Regexp.escape(expected)}/,
- "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/(?:\^|\A:(?:\d+:)?(?! syntax errors? found)(?: syntax error,)?) (.*)/, 1] end', message
+ "begin eval(%q{#{code}}, nil, '', 0)"'; rescue SyntaxError => e; e.message[/(?:\^~*|\A:(?:\d+:)?(?! syntax errors? found)(?: syntax error,)?) (.*)/, 1] end', message
end
assert_syntax_error "unterminated string meets end of file", '().."', '[ruby-dev:29732]'
assert_equal %q{[]}, %q{$&;[]}, '[ruby-dev:31068]'
diff --git a/common.mk b/common.mk
index 173b4fb2a725bd..3fce9df761bf80 100644
--- a/common.mk
+++ b/common.mk
@@ -3534,6 +3534,7 @@ compile.$(OBJEXT): {$(VPATH)}prism/prism.h
compile.$(OBJEXT): {$(VPATH)}prism/version.h
compile.$(OBJEXT): {$(VPATH)}prism_compile.c
compile.$(OBJEXT): {$(VPATH)}prism_compile.h
+compile.$(OBJEXT): {$(VPATH)}ractor.h
compile.$(OBJEXT): {$(VPATH)}re.h
compile.$(OBJEXT): {$(VPATH)}regex.h
compile.$(OBJEXT): {$(VPATH)}ruby_assert.h
diff --git a/compile.c b/compile.c
index 3d81499a23c82f..96e0b921081c23 100644
--- a/compile.c
+++ b/compile.c
@@ -37,6 +37,7 @@
#include "internal/variable.h"
#include "iseq.h"
#include "ractor_core.h"
+#include "ruby/ractor.h"
#include "ruby/re.h"
#include "ruby/util.h"
#include "vm_core.h"
@@ -1927,9 +1928,6 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
}
else {
switch (nd_type(val_node)) {
- case NODE_LIT:
- dv = RNODE_LIT(val_node)->nd_lit;
- break;
case NODE_SYM:
dv = rb_node_sym_string_val(val_node);
break;
@@ -4510,7 +4508,6 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond,
else_label = NEW_LABEL(nd_line(cond));
}
goto again;
- case NODE_LIT: /* NODE_LIT is always true */
case NODE_SYM:
case NODE_LINE:
case NODE_FILE:
@@ -4591,8 +4588,6 @@ static VALUE
get_symbol_value(rb_iseq_t *iseq, const NODE *node)
{
switch (nd_type(node)) {
- case NODE_LIT:
- return RNODE_LIT(node)->nd_lit;
case NODE_SYM:
return rb_node_sym_string_val(node);
default:
@@ -4641,10 +4636,7 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
seen_nodes++;
RUBY_ASSERT(nd_type_p(node, NODE_LIST));
- if (key_node && nd_type_p(key_node, NODE_LIT) && SYMBOL_P(RNODE_LIT(key_node)->nd_lit)) {
- /* can be keywords */
- }
- else if (key_node && nd_type_p(key_node, NODE_SYM)) {
+ if (key_node && nd_type_p(key_node, NODE_SYM)) {
/* can be keywords */
}
else {
@@ -4729,7 +4721,6 @@ static inline bool
static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key)
{
switch (nd_type(node)) {
- case NODE_LIT:
case NODE_SYM:
case NODE_REGX:
case NODE_LINE:
@@ -4788,8 +4779,6 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq)
else {
return rb_fstring(get_string_value(node));
}
- case NODE_LIT:
- return RNODE_LIT(node)->nd_lit;
default:
rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
}
@@ -5068,7 +5057,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
FLUSH_CHUNK();
const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head;
- int empty_kw = nd_type_p(kw, NODE_LIT) && RB_TYPE_P(RNODE_LIT(kw)->nd_lit, T_HASH); /* foo( ..., **{}, ...) */
+ int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */
int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */
int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */
int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */
@@ -5133,13 +5122,6 @@ VALUE
rb_node_case_when_optimizable_literal(const NODE *const node)
{
switch (nd_type(node)) {
- case NODE_LIT: {
- VALUE v = RNODE_LIT(node)->nd_lit;
- if (SYMBOL_P(v)) {
- return v;
- }
- break;
- }
case NODE_INTEGER:
return rb_node_integer_literal_val(node);
case NODE_FLOAT: {
@@ -5815,7 +5797,6 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
}
/* fall through */
case NODE_STR:
- case NODE_LIT:
case NODE_SYM:
case NODE_REGX:
case NODE_LINE:
@@ -6403,8 +6384,6 @@ optimizable_range_item_p(const NODE *n)
{
if (!n) return FALSE;
switch (nd_type(n)) {
- case NODE_LIT:
- return RB_INTEGER_TYPE_P(RNODE_LIT(n)->nd_lit);
case NODE_LINE:
return TRUE;
case NODE_INTEGER:
@@ -6420,8 +6399,6 @@ static VALUE
optimized_range_item(const NODE *n)
{
switch (nd_type(n)) {
- case NODE_LIT:
- return RNODE_LIT(n)->nd_lit;
case NODE_LINE:
return rb_node_line_lineno_val(n);
case NODE_INTEGER:
@@ -7247,7 +7224,6 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSNL(ret, line_node, jump, unmatched);
break;
}
- case NODE_LIT:
case NODE_SYM:
case NODE_REGX:
case NODE_LINE:
@@ -8649,9 +8625,6 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node)
case NODE_SYM:
symbol = rb_node_sym_string_val(node);
break;
- case NODE_LIT:
- symbol = RNODE_LIT(node)->nd_lit;
- break;
default:
goto bad_arg;
}
@@ -8698,9 +8671,6 @@ compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, c
case NODE_SYM:
name = rb_node_sym_string_val(node);
break;
- case NODE_LIT:
- name = RNODE_LIT(node)->nd_lit;
- break;
default:
goto bad_arg;
}
@@ -8939,20 +8909,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co
labels_table = st_init_numtable();
ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
}
- if (nd_type_p(node->nd_args->nd_head, NODE_LIT) &&
- SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
-
- label_name = node->nd_args->nd_head->nd_lit;
- if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
- label = NEW_LABEL(nd_line(line_node));
- label->position = nd_line(line_node);
- st_insert(labels_table, (st_data_t)label_name, (st_data_t)label);
- }
- else {
- label = (LABEL *)data;
- }
- }
- else {
+ {
COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
return COMPILE_NG;
}
@@ -9380,6 +9337,8 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
return COMPILE_OK;
}
+static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value);
+
static int
compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
{
@@ -9423,7 +9382,7 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
/* cref [obj] */
if (!popped) ADD_INSN(ret, node, pop); /* cref */
if (lassign) ADD_LABEL(ret, lassign);
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", RNODE_OP_CDECL(node)->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
/* cref value */
if (popped)
ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */
@@ -9437,7 +9396,7 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
ADD_INSN(ret, node, pop); /* [value] */
}
else {
- CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", RNODE_OP_CDECL(node)->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value));
/* cref obj value */
ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1));
/* cref value */
@@ -9836,8 +9795,7 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
COMPILE_ERROR(ERROR_ARGS "unreachable");
return COMPILE_NG;
}
- else if (nd_type_p(default_value, NODE_LIT) ||
- nd_type_p(default_value, NODE_SYM) ||
+ else if (nd_type_p(default_value, NODE_SYM) ||
nd_type_p(default_value, NODE_REGX) ||
nd_type_p(default_value, NODE_LINE) ||
nd_type_p(default_value, NODE_INTEGER) ||
@@ -9953,6 +9911,367 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node
return COMPILE_OK;
}
+static int
+compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy)
+{
+ ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
+ ADD_SEQ(ret, sub);
+
+ if (copy) {
+ /*
+ * NEW_CALL(fcore, rb_intern("make_shareable_copy"),
+ * NEW_LIST(value, loc), loc);
+ */
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+ else {
+ /*
+ * NEW_CALL(fcore, rb_intern("make_shareable"),
+ * NEW_LIST(value, loc), loc);
+ */
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+
+ return COMPILE_OK;
+}
+
+static VALUE
+node_const_decl_val(const NODE *node)
+{
+ VALUE path;
+ switch (nd_type(node)) {
+ case NODE_CDECL:
+ if (RNODE_CDECL(node)->nd_vid) {
+ path = rb_id2str(RNODE_CDECL(node)->nd_vid);
+ goto end;
+ }
+ else {
+ node = RNODE_CDECL(node)->nd_else;
+ }
+ break;
+ case NODE_COLON2:
+ break;
+ case NODE_COLON3:
+ // ::Const
+ path = rb_str_new_cstr("::");
+ rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
+ goto end;
+ default:
+ rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
+ UNREACHABLE_RETURN(0);
+ }
+
+ path = rb_ary_new();
+ if (node) {
+ for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
+ rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
+ }
+ if (node && nd_type_p(node, NODE_CONST)) {
+ // Const::Name
+ rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
+ }
+ else if (node && nd_type_p(node, NODE_COLON3)) {
+ // ::Const::Name
+ rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
+ rb_ary_push(path, rb_str_new(0, 0));
+ }
+ else {
+ // expression::Name
+ rb_ary_push(path, rb_str_new_cstr("..."));
+ }
+ path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
+ }
+ end:
+ path = rb_fstring(path);
+ return path;
+}
+
+static VALUE
+const_decl_path(NODE *dest)
+{
+ VALUE path = Qnil;
+ if (!nd_type_p(dest, NODE_CALL)) {
+ path = node_const_decl_val(dest);
+ }
+ return path;
+}
+
+static int
+compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value)
+{
+ /*
+ *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest))
+ */
+ VALUE path = const_decl_path(dest);
+ ADD_INSN1(ret, value, putobject, rb_mRubyVMFrozenCore);
+ CHECK(COMPILE(ret, "compile_ensure_shareable_node", value));
+ ADD_INSN1(ret, value, putobject, path);
+ RB_OBJ_WRITTEN(iseq, Qundef, path);
+ ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE));
+
+ return COMPILE_OK;
+}
+
+#ifndef SHAREABLE_BARE_EXPRESSION
+#define SHAREABLE_BARE_EXPRESSION 1
+#endif
+
+static int
+compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p)
+{
+# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \
+ compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p)
+ VALUE lit = Qnil;
+ DECL_ANCHOR(anchor);
+
+ enum node_type type = nd_type(node);
+ switch (type) {
+ case NODE_TRUE:
+ *value_p = Qtrue;
+ goto compile;
+ case NODE_FALSE:
+ *value_p = Qfalse;
+ goto compile;
+ case NODE_NIL:
+ *value_p = Qnil;
+ goto compile;
+ case NODE_SYM:
+ *value_p = rb_node_sym_string_val(node);
+ goto compile;
+ case NODE_REGX:
+ *value_p = rb_node_regx_string_val(node);
+ goto compile;
+ case NODE_LINE:
+ *value_p = rb_node_line_lineno_val(node);
+ goto compile;
+ case NODE_INTEGER:
+ *value_p = rb_node_integer_literal_val(node);
+ goto compile;
+ case NODE_FLOAT:
+ *value_p = rb_node_float_literal_val(node);
+ goto compile;
+ case NODE_RATIONAL:
+ *value_p = rb_node_rational_literal_val(node);
+ goto compile;
+ case NODE_IMAGINARY:
+ *value_p = rb_node_imaginary_literal_val(node);
+ goto compile;
+ case NODE_ENCODING:
+ *value_p = rb_node_encoding_val(node);
+
+ compile:
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+
+ case NODE_DSTR:
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ if (shareable == rb_parser_shareable_literal) {
+ /*
+ * NEW_CALL(node, idUMinus, 0, loc);
+ *
+ * -"#{var}"
+ */
+ ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE));
+ }
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+
+ case NODE_STR:{
+ VALUE lit = rb_fstring(rb_node_str_string_val(node));
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_FILE:{
+ VALUE lit = rb_fstring(rb_node_file_path_val(node));
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_ZLIST:{
+ VALUE lit = rb_ary_new();
+ OBJ_FREEZE_RAW(lit);
+ ADD_INSN1(ret, node, putobject, lit);
+ RB_OBJ_WRITTEN(iseq, Qundef, lit);
+ *value_p = lit;
+ *shareable_literal_p = 1;
+
+ return COMPILE_OK;
+ }
+
+ case NODE_LIST:{
+ INIT_ANCHOR(anchor);
+ lit = rb_ary_new();
+ for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) {
+ VALUE val;
+ int shareable_literal_p2;
+ NODE *elt = RNODE_LIST(n)->nd_head;
+ if (elt) {
+ CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_ary_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (RTEST(lit)) {
+ if (!UNDEF_P(val)) {
+ rb_ary_push(lit, val);
+ }
+ else {
+ rb_ary_clear(lit);
+ lit = Qnil; /* make shareable at runtime */
+ }
+ }
+ }
+ break;
+ }
+ case NODE_HASH:{
+ if (!RNODE_HASH(node)->nd_brace) {
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ return COMPILE_OK;
+ }
+
+ INIT_ANCHOR(anchor);
+ lit = rb_hash_new();
+ for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
+ VALUE key_val;
+ VALUE value_val;
+ int shareable_literal_p2;
+ NODE *key = RNODE_LIST(n)->nd_head;
+ NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
+ if (key) {
+ CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_hash_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (val) {
+ CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2));
+ if (shareable_literal_p2) {
+ /* noop */
+ }
+ else if (RTEST(lit)) {
+ rb_hash_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (RTEST(lit)) {
+ if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) {
+ rb_hash_aset(lit, key_val, value_val);
+ }
+ else {
+ rb_hash_clear(lit);
+ lit = Qnil; /* make shareable at runtime */
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ if (shareable == rb_parser_shareable_literal &&
+ (SHAREABLE_BARE_EXPRESSION || level > 0)) {
+ CHECK(compile_ensure_shareable_node(iseq, ret, dest, node));
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ return COMPILE_OK;
+ }
+ CHECK(COMPILE(ret, "shareable_literal_constant", node));
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ return COMPILE_OK;
+ }
+
+ /* Array or Hash */
+ if (!lit) {
+ if (nd_type(node) == NODE_LIST) {
+ ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
+ }
+ else if (nd_type(node) == NODE_HASH) {
+ int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
+ ADD_INSN1(anchor, node, newhash, INT2FIX(len));
+ }
+ *value_p = Qundef;
+ *shareable_literal_p = 0;
+ ADD_SEQ(ret, anchor);
+ return COMPILE_OK;
+ }
+ if (NIL_P(lit)) {
+ // if shareable_literal, all elements should have been ensured
+ // as shareable
+ if (nd_type(node) == NODE_LIST) {
+ ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen));
+ }
+ else if (nd_type(node) == NODE_HASH) {
+ int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen;
+ ADD_INSN1(anchor, node, newhash, INT2FIX(len));
+ }
+ CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false));
+ *value_p = Qundef;
+ *shareable_literal_p = 1;
+ }
+ else {
+ VALUE val = rb_ractor_make_shareable(lit);
+ ADD_INSN1(ret, node, putobject, val);
+ RB_OBJ_WRITTEN(iseq, Qundef, val);
+ *value_p = val;
+ *shareable_literal_p = 1;
+ }
+
+ return COMPILE_OK;
+}
+
+static int
+compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value)
+{
+ int literal_p = 0;
+ VALUE val;
+ DECL_ANCHOR(anchor);
+ INIT_ANCHOR(anchor);
+
+ switch (shareable) {
+ case rb_parser_shareable_none:
+ CHECK(COMPILE(ret, "compile_shareable_constant_value", value));
+ return COMPILE_OK;
+
+ case rb_parser_shareable_literal:
+ CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
+ ADD_SEQ(ret, anchor);
+ return COMPILE_OK;
+
+ case rb_parser_shareable_copy:
+ case rb_parser_shareable_everything:
+ CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p));
+ if (!literal_p) {
+ CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy));
+ }
+ else {
+ ADD_SEQ(ret, anchor);
+ }
+ return COMPILE_OK;
+ default:
+ rb_bug("unexpected rb_parser_shareability: %d", shareable);
+ }
+}
+
static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped);
/**
compile each node
@@ -10135,7 +10454,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
case NODE_CDECL:{
if (RNODE_CDECL(node)->nd_vid) {
- CHECK(COMPILE(ret, "lvalue", RNODE_CDECL(node)->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
if (!popped) {
ADD_INSN(ret, node, dup);
@@ -10147,7 +10466,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
}
else {
compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else);
- CHECK(COMPILE(ret, "lvalue", RNODE_CDECL(node)->nd_value));
+ CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value));
ADD_INSN(ret, node, swap);
if (!popped) {
@@ -10304,14 +10623,6 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no
case NODE_MATCH3:
CHECK(compile_match(iseq, ret, node, popped, type));
break;
- case NODE_LIT:{
- debugp_param("lit", RNODE_LIT(node)->nd_lit);
- if (!popped) {
- ADD_INSN1(ret, node, putobject, RNODE_LIT(node)->nd_lit);
- RB_OBJ_WRITTEN(iseq, Qundef, RNODE_LIT(node)->nd_lit);
- }
- break;
- }
case NODE_SYM:{
if (!popped) {
ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node));
diff --git a/configure.ac b/configure.ac
index b09f7f300f50f3..28d006e4368b7d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2099,7 +2099,6 @@ AC_CHECK_FUNCS(gettimeofday) # for making ac_cv_func_gettimeofday
AC_CHECK_FUNCS(getuid)
AC_CHECK_FUNCS(getuidx)
AC_CHECK_FUNCS(gmtime_r)
-AC_CHECK_FUNCS(grantpt)
AC_CHECK_FUNCS(initgroups)
AC_CHECK_FUNCS(ioctl)
AC_CHECK_FUNCS(isfinite)
diff --git a/doc/packed_data.rdoc b/doc/packed_data.rdoc
index ec0e2c07f01b6b..17bbf92023d7a5 100644
--- a/doc/packed_data.rdoc
+++ b/doc/packed_data.rdoc
@@ -554,10 +554,12 @@ for one byte in the input or output string.
- 'u' - UU-encoded string:
- [0].pack("U") # => "\u0000"
- [0x3fffffff].pack("U") # => "\xFC\xBF\xBF\xBF\xBF\xBF"
- [0x40000000].pack("U") # => "\xFD\x80\x80\x80\x80\x80"
- [0x7fffffff].pack("U") # => "\xFD\xBF\xBF\xBF\xBF\xBF"
+ [""].pack("u") # => ""
+ ["a"].pack("u") # => "!80``\n"
+ ["aaa"].pack("u") # => "#86%A\n"
+
+ "".unpack("u") # => [""]
+ "#86)C\n".unpack("u") # => ["abc"]
== Offset Directives
diff --git a/doc/yjit/yjit.md b/doc/yjit/yjit.md
index 4508bce25f0715..8ea3409e485e4d 100644
--- a/doc/yjit/yjit.md
+++ b/doc/yjit/yjit.md
@@ -265,10 +265,12 @@ This section contains tips on writing Ruby code that will run as fast as possibl
- Avoid redefining the meaning of `nil`, equality, etc.
- Avoid allocating objects in the hot parts of your code
- Minimize layers of indirection
- - Avoid classes that wrap objects if you can
- - Avoid methods that just call another method, trivial one-liner methods
-- Try to write code so that the same variables always have the same type
-- CRuby method calls are costly. Avoid things such as methods that only return a value from a hash or return a constant.
+ - Avoid writing wrapper classes if you can (e.g. a class that only wraps a Ruby hash)
+ - Avoid methods that just call another method
+- Ruby method calls are costly. Avoid things such as methods that only return a value from a hash
+- Try to write code so that the same variables and method arguments always have the same type
+- Avoid using `TracePoint` as it can cause YJIT to deoptimize code
+- Avoid using `Binding` as it can cause YJIT to deoptimize code
You can also use the `--yjit-stats` command-line option to see which bytecodes cause YJIT to exit, and refactor your code to avoid using these instructions in the hottest methods of your code.
diff --git a/enc/make_encmake.rb b/enc/make_encmake.rb
index 09e19f65514499..9761edd6d9b70d 100755
--- a/enc/make_encmake.rb
+++ b/enc/make_encmake.rb
@@ -124,7 +124,7 @@ def target_transcoders
erb = ERB.new(File.read(depend), trim_mode: '%')
erb.filename = depend
tmp = erb.result(binding)
- dep = "\n#### depend ####\n\n" << depend_rules(tmp).join
+ dep = "\n#### depend ####\n\n" + depend_rules(tmp).join
else
dep = ""
end
diff --git a/ext/json/lib/json/common.rb b/ext/json/lib/json/common.rb
index 090066012d9a26..95098d3bb48f79 100644
--- a/ext/json/lib/json/common.rb
+++ b/ext/json/lib/json/common.rb
@@ -1,8 +1,9 @@
#frozen_string_literal: false
require 'json/version'
-require 'json/generic_object'
module JSON
+ autoload :GenericObject, 'json/generic_object'
+
NOT_SET = Object.new.freeze
private_constant :NOT_SET
diff --git a/ext/json/lib/json/generic_object.rb b/ext/json/lib/json/generic_object.rb
index b7b8b04ec43f95..56efda64955bb5 100644
--- a/ext/json/lib/json/generic_object.rb
+++ b/ext/json/lib/json/generic_object.rb
@@ -2,6 +2,7 @@
begin
require 'ostruct'
rescue LoadError
+ warn "JSON::GenericObject requires 'ostruct'. Please install it with `gem install ostruct`."
end
module JSON
diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb
index b43ceecdcd7ef6..836f47edf40c68 100644
--- a/ext/json/lib/json/version.rb
+++ b/ext/json/lib/json/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module JSON
# JSON version
- VERSION = '2.7.1'
+ VERSION = '2.7.2'
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index 8dca8ba281dde6..49d92d15d41d47 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -258,13 +258,19 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
/* Unix98 PTY */
int masterfd = -1, slavefd = -1;
char *slavedevice;
+ struct sigaction dfl, old;
+
+ dfl.sa_handler = SIG_DFL;
+ dfl.sa_flags = 0;
+ sigemptyset(&dfl.sa_mask);
#if defined(__sun) || defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version < 902000)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
/* FreeBSD 9.2 or later supports O_CLOEXEC
* http://www.freebsd.org/cgi/query-pr.cgi?pr=162374 */
if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
- if (rb_grantpt(masterfd) == -1) goto error;
+ if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
+ if (grantpt(masterfd) == -1) goto grantpt_error;
rb_fd_fix_cloexec(masterfd);
#else
{
@@ -278,8 +284,10 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
if ((masterfd = posix_openpt(flags)) == -1) goto error;
}
rb_fd_fix_cloexec(masterfd);
- if (rb_grantpt(masterfd) == -1) goto error;
+ if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
+ if (grantpt(masterfd) == -1) goto grantpt_error;
#endif
+ if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
if (unlockpt(masterfd) == -1) goto error;
if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
@@ -297,6 +305,8 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
strlcpy(SlaveName, slavedevice, DEVICELEN);
return 0;
+ grantpt_error:
+ sigaction(SIGCHLD, &old, NULL);
error:
if (slavefd != -1) close(slavefd);
if (masterfd != -1) close(masterfd);
@@ -348,17 +358,21 @@ get_device_once(int *master, int *slave, char SlaveName[DEVICELEN], int nomesg,
extern char *ptsname(int);
extern int unlockpt(int);
+ extern int grantpt(int);
#if defined(__sun)
/* workaround for Solaris 10: grantpt() doesn't work if FD_CLOEXEC is set. [ruby-dev:44688] */
if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
- if(rb_grantpt(masterfd) == -1) goto error;
+ s = signal(SIGCHLD, SIG_DFL);
+ if(grantpt(masterfd) == -1) goto error;
rb_fd_fix_cloexec(masterfd);
#else
if((masterfd = rb_cloexec_open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(masterfd);
- if(rb_grantpt(masterfd) == -1) goto error;
+ s = signal(SIGCHLD, SIG_DFL);
+ if(grantpt(masterfd) == -1) goto error;
#endif
+ signal(SIGCHLD, s);
if(unlockpt(masterfd) == -1) goto error;
if((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
diff --git a/internal/ruby_parser.h b/internal/ruby_parser.h
index 6a2dcab5ca8690..7dc6a46d4f42ac 100644
--- a/internal/ruby_parser.h
+++ b/internal/ruby_parser.h
@@ -28,7 +28,6 @@ VALUE rb_node_dregx_string_val(const NODE *);
VALUE rb_node_line_lineno_val(const NODE *);
VALUE rb_node_file_path_val(const NODE *);
VALUE rb_node_encoding_val(const NODE *);
-VALUE rb_node_const_decl_val(const NODE *node);
VALUE rb_node_integer_literal_val(const NODE *);
VALUE rb_node_float_literal_val(const NODE *);
diff --git a/internal/signal.h b/internal/signal.h
index 660cd95f78742d..2363bf412cfbb2 100644
--- a/internal/signal.h
+++ b/internal/signal.h
@@ -19,7 +19,6 @@ void (*ruby_posix_signal(int, void (*)(int)))(int);
RUBY_SYMBOL_EXPORT_BEGIN
/* signal.c (export) */
-int rb_grantpt(int fd);
RUBY_SYMBOL_EXPORT_END
#endif /* INTERNAL_SIGNAL_H */
diff --git a/io.c b/io.c
index f773de8c3f4d4a..feff4459437cca 100644
--- a/io.c
+++ b/io.c
@@ -14571,14 +14571,14 @@ argf_write_io(VALUE argf)
/*
* call-seq:
- * ARGF.write(string) -> integer
+ * ARGF.write(*objects) -> integer
*
- * Writes _string_ if inplace mode.
+ * Writes each of the given +objects+ if inplace mode.
*/
static VALUE
-argf_write(VALUE argf, VALUE str)
+argf_write(int argc, VALUE *argv, VALUE argf)
{
- return rb_io_write(argf_write_io(argf), str);
+ return rb_io_writev(argf_write_io(argf), argc, argv);
}
void
@@ -15823,7 +15823,7 @@ Init_IO(void)
rb_define_method(rb_cARGF, "binmode", argf_binmode_m, 0);
rb_define_method(rb_cARGF, "binmode?", argf_binmode_p, 0);
- rb_define_method(rb_cARGF, "write", argf_write, 1);
+ rb_define_method(rb_cARGF, "write", argf_write, -1);
rb_define_method(rb_cARGF, "print", rb_io_print, -1);
rb_define_method(rb_cARGF, "putc", rb_io_putc, 1);
rb_define_method(rb_cARGF, "puts", rb_io_puts, -1);
diff --git a/iseq.c b/iseq.c
index f7b1df806d6e83..c0943d19db6b68 100644
--- a/iseq.c
+++ b/iseq.c
@@ -1386,18 +1386,30 @@ rb_iseq_remove_coverage_all(void)
static void
iseqw_mark(void *ptr)
{
- rb_gc_mark((VALUE)ptr);
+ rb_gc_mark_movable(*(VALUE *)ptr);
}
static size_t
iseqw_memsize(const void *ptr)
{
- return rb_iseq_memsize((const rb_iseq_t *)ptr);
+ return rb_iseq_memsize(*(const rb_iseq_t **)ptr);
+}
+
+static void
+iseqw_ref_update(void *ptr)
+{
+ VALUE *vptr = ptr;
+ *vptr = rb_gc_location(*vptr);
}
static const rb_data_type_t iseqw_data_type = {
"T_IMEMO/iseq",
- {iseqw_mark, NULL, iseqw_memsize,},
+ {
+ iseqw_mark,
+ RUBY_TYPED_DEFAULT_FREE,
+ iseqw_memsize,
+ iseqw_ref_update,
+ },
0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
};
@@ -1405,18 +1417,16 @@ static VALUE
iseqw_new(const rb_iseq_t *iseq)
{
if (iseq->wrapper) {
- if (rb_check_typeddata(iseq->wrapper, &iseqw_data_type) != iseq) {
+ if (*(const rb_iseq_t **)rb_check_typeddata(iseq->wrapper, &iseqw_data_type) != iseq) {
rb_raise(rb_eTypeError, "wrong iseq wrapper: %" PRIsVALUE " for %p",
iseq->wrapper, (void *)iseq);
}
return iseq->wrapper;
}
else {
- union { const rb_iseq_t *in; void *out; } deconst;
- VALUE obj;
- deconst.in = iseq;
- obj = TypedData_Wrap_Struct(rb_cISeq, &iseqw_data_type, deconst.out);
- RB_OBJ_WRITTEN(obj, Qundef, iseq);
+ rb_iseq_t **ptr;
+ VALUE obj = TypedData_Make_Struct(rb_cISeq, rb_iseq_t *, &iseqw_data_type, ptr);
+ RB_OBJ_WRITE(obj, ptr, iseq);
FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
@@ -1742,7 +1752,9 @@ iseqw_s_compile_option_get(VALUE self)
static const rb_iseq_t *
iseqw_check(VALUE iseqw)
{
- rb_iseq_t *iseq = DATA_PTR(iseqw);
+ rb_iseq_t **iseq_ptr;
+ TypedData_Get_Struct(iseqw, rb_iseq_t *, &iseqw_data_type, iseq_ptr);
+ rb_iseq_t *iseq = *iseq_ptr;
if (!ISEQ_BODY(iseq)) {
rb_ibf_load_iseq_complete(iseq);
diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb
index ed4138f0d9b48b..f3e1708e238a61 100644
--- a/lib/bundled_gems.rb
+++ b/lib/bundled_gems.rb
@@ -110,7 +110,7 @@ def self.warning?(name, specs: nil)
return if specs.include?(gem)
caller = caller_locations(3, 3).find {|c| c&.absolute_path}
return if find_gem(caller&.absolute_path)
- elsif SINCE[name]
+ elsif SINCE[name] && !path
gem = true
else
return
diff --git a/lib/reline.rb b/lib/reline.rb
index b43584fc9bccbf..f730f8af125999 100644
--- a/lib/reline.rb
+++ b/lib/reline.rb
@@ -254,32 +254,35 @@ def get_screen_size
Reline::DEFAULT_DIALOG_CONTEXT = Array.new
def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination)
+ unless confirm_multiline_termination
+ raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
+ end
+
Reline.update_iogate
io_gate.with_raw_input do
- unless confirm_multiline_termination
- raise ArgumentError.new('#readmultiline needs block to confirm multiline termination')
- end
inner_readline(prompt, add_hist, true, &confirm_multiline_termination)
+ end
- whole_buffer = line_editor.whole_buffer.dup
- whole_buffer.taint if RUBY_VERSION < '2.7'
- if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0
- Reline::HISTORY << whole_buffer
- end
+ whole_buffer = line_editor.whole_buffer.dup
+ whole_buffer.taint if RUBY_VERSION < '2.7'
+ if add_hist and whole_buffer and whole_buffer.chomp("\n").size > 0
+ Reline::HISTORY << whole_buffer
+ end
- if line_editor.eof?
- line_editor.reset_line
- # Return nil if the input is aborted by C-d.
- nil
- else
- whole_buffer
- end
+ if line_editor.eof?
+ line_editor.reset_line
+ # Return nil if the input is aborted by C-d.
+ nil
+ else
+ whole_buffer
end
end
def readline(prompt = '', add_hist = false)
Reline.update_iogate
- inner_readline(prompt, add_hist, false)
+ io_gate.with_raw_input do
+ inner_readline(prompt, add_hist, false)
+ end
line = line_editor.line.dup
line.taint if RUBY_VERSION < '2.7'
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb
index 2d7c759ea25f54..0d7226b36f41e0 100644
--- a/lib/reline/ansi.rb
+++ b/lib/reline/ansi.rb
@@ -151,7 +151,11 @@ def self.output=(val)
end
def self.with_raw_input
- @@input.raw { yield }
+ if @@input.tty?
+ @@input.raw(intr: true) { yield }
+ else
+ yield
+ end
end
@@buf = []
@@ -159,11 +163,13 @@ def self.inner_getc(timeout_second)
unless @@buf.empty?
return @@buf.shift
end
- until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte }
- timeout_second -= 0.1
+ until @@input.wait_readable(0.01)
+ timeout_second -= 0.01
return nil if timeout_second <= 0
- Reline.core.line_editor.resize
+
+ Reline.core.line_editor.handle_signal
end
+ c = @@input.getbyte
(c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c
rescue Errno::EIO
# Maybe the I/O has been closed.
diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb
index 0ac1c6c56d40bc..d52151ad3cd231 100644
--- a/lib/reline/general_io.rb
+++ b/lib/reline/general_io.rb
@@ -46,6 +46,7 @@ def self.getc(_timeout_second)
end
c = nil
loop do
+ Reline.core.line_editor.handle_signal
result = @@input.wait_readable(0.1)
next if result.nil?
c = @@input.read(1)
diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb
index d5c158ac746e54..c236044e41fed9 100644
--- a/lib/reline/line_editor.rb
+++ b/lib/reline/line_editor.rb
@@ -138,9 +138,6 @@ def reset(prompt = '', encoding:)
@screen_size = Reline::IOGate.get_screen_size
reset_variables(prompt, encoding: encoding)
@rendered_screen.base_y = Reline::IOGate.cursor_pos.y
- Reline::IOGate.set_winch_handler do
- @resized = true
- end
if ENV.key?('RELINE_ALT_SCROLLBAR')
@full_block = '::'
@upper_half_block = "''"
@@ -164,7 +161,12 @@ def reset(prompt = '', encoding:)
end
end
- def resize
+ def handle_signal
+ handle_interrupted
+ handle_resized
+ end
+
+ private def handle_resized
return unless @resized
@screen_size = Reline::IOGate.get_screen_size
@@ -177,25 +179,35 @@ def resize
render_differential
end
+ private def handle_interrupted
+ return unless @interrupted
+
+ @interrupted = false
+ clear_dialogs
+ scrolldown = render_differential
+ Reline::IOGate.scroll_down scrolldown
+ Reline::IOGate.move_cursor_column 0
+ @rendered_screen.lines = []
+ @rendered_screen.cursor_y = 0
+ case @old_trap
+ when 'DEFAULT', 'SYSTEM_DEFAULT'
+ raise Interrupt
+ when 'IGNORE'
+ # Do nothing
+ when 'EXIT'
+ exit
+ else
+ @old_trap.call if @old_trap.respond_to?(:call)
+ end
+ end
+
def set_signal_handlers
- @old_trap = Signal.trap('INT') {
- clear_dialogs
- scrolldown = render_differential
- Reline::IOGate.scroll_down scrolldown
- Reline::IOGate.move_cursor_column 0
- @rendered_screen.lines = []
- @rendered_screen.cursor_y = 0
- case @old_trap
- when 'DEFAULT', 'SYSTEM_DEFAULT'
- raise Interrupt
- when 'IGNORE'
- # Do nothing
- when 'EXIT'
- exit
- else
- @old_trap.call if @old_trap.respond_to?(:call)
- end
- }
+ Reline::IOGate.set_winch_handler do
+ @resized = true
+ end
+ @old_trap = Signal.trap('INT') do
+ @interrupted = true
+ end
end
def finalize
@@ -212,7 +224,6 @@ def reset_variables(prompt = '', encoding:)
@encoding = encoding
@is_multiline = false
@finished = false
- @cleared = false
@history_pointer = nil
@kill_ring ||= Reline::KillRing.new
@vi_clipboard = ''
@@ -234,6 +245,7 @@ def reset_variables(prompt = '', encoding:)
@in_pasting = false
@auto_indent_proc = nil
@dialogs = []
+ @interrupted = false
@resized = false
@cache = {}
@rendered_screen = RenderedScreen.new(base_y: 0, lines: [], cursor_y: 0)
@@ -541,19 +553,7 @@ def rest_height(wrapped_cursor_y)
screen_height - wrapped_cursor_y + screen_scroll_top - @rendered_screen.base_y - 1
end
- def handle_cleared
- return unless @cleared
-
- @cleared = false
- Reline::IOGate.clear_screen
- @screen_size = Reline::IOGate.get_screen_size
- @rendered_screen.lines = []
- @rendered_screen.base_y = 0
- @rendered_screen.cursor_y = 0
- end
-
def rerender
- handle_cleared
render_differential unless @in_pasting
end
@@ -794,7 +794,7 @@ def add_dialog_proc(name, p, context = nil)
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: complete)
after.lines("\n").map { |l| l.chomp('') }
else
- before
+ before.map { |l| Reline::Unicode.escape_for_print(l) }
end
end
@@ -1221,10 +1221,11 @@ def call_completion_proc_with_checking_args(pre, target, post)
new_indent = @auto_indent_proc.(@buffer_of_lines.take(line_index + 1).push(''), line_index, byte_pointer, add_newline)
return unless new_indent
- @buffer_of_lines[line_index] = ' ' * new_indent + line.lstrip
+ new_line = ' ' * new_indent + line.lstrip
+ @buffer_of_lines[line_index] = new_line
if @line_index == line_index
- old_indent = line[/\A */].size
- @byte_pointer = [@byte_pointer + new_indent - old_indent, 0].max
+ indent_diff = new_line.bytesize - line.bytesize
+ @byte_pointer = [@byte_pointer + indent_diff, 0].max
end
end
@@ -2073,7 +2074,11 @@ def finish
alias_method :yank_pop, :em_yank_pop
private def ed_clear_screen(key)
- @cleared = true
+ Reline::IOGate.clear_screen
+ @screen_size = Reline::IOGate.get_screen_size
+ @rendered_screen.lines = []
+ @rendered_screen.base_y = 0
+ @rendered_screen.cursor_y = 0
end
alias_method :clear_screen, :ed_clear_screen
diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb
index 28f28e15ccd119..ee3f73e3830575 100644
--- a/lib/reline/windows.rb
+++ b/lib/reline/windows.rb
@@ -259,7 +259,7 @@ def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, ch
def self.check_input_event
num_of_events = 0.chr * 8
while @@output_buf.empty?
- Reline.core.line_editor.resize
+ Reline.core.line_editor.handle_signal
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
# prevent for background consolemode change
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 6f83fe2c79d0b5..7874ad0dc9a1ca 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -210,22 +210,34 @@ def initialize(args)
@hash = @hash.merge environment_config
end
+ @hash.transform_keys! do |k|
+ # gemhome and gempath are not working with symbol keys
+ if %w[backtrace bulk_threshold verbose update_sources cert_expiration_length_days
+ install_extension_in_lib ipv4_fallback_enabled sources disable_default_gem_server
+ ssl_verify_mode ssl_ca_cert ssl_client_cert].include?(k)
+ k.to_sym
+ else
+ k
+ end
+ end
+
# HACK: these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
- @home = @hash[:gemhome] if @hash.key? :gemhome
- @path = @hash[:gempath] if @hash.key? :gempath
- @update_sources = @hash[:update_sources] if @hash.key? :update_sources
@verbose = @hash[:verbose] if @hash.key? :verbose
- @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
- @sources = @hash[:sources] if @hash.key? :sources
+ @update_sources = @hash[:update_sources] if @hash.key? :update_sources
+ # TODO: We should handle concurrent_downloads same as other options
@cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
@install_extension_in_lib = @hash[:install_extension_in_lib] if @hash.key? :install_extension_in_lib
@ipv4_fallback_enabled = @hash[:ipv4_fallback_enabled] if @hash.key? :ipv4_fallback_enabled
- @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
- @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
- @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
+ @home = @hash[:gemhome] if @hash.key? :gemhome
+ @path = @hash[:gempath] if @hash.key? :gempath
+ @sources = @hash[:sources] if @hash.key? :sources
+ @disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
+ @ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
+ @ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
+ @ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
@api_keys = nil
@rubygems_api_key = nil
diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py
index 054c206cef02f3..86b5bdda2d8a13 100644
--- a/misc/lldb_rb/utils.py
+++ b/misc/lldb_rb/utils.py
@@ -371,8 +371,6 @@ def inspect(self, val):
self._append_expression("*(struct RNode_MATCH2 *) %0#x" % val.GetValueAsUnsigned())
elif nd_type == self.ruby_globals["NODE_MATCH3"]:
self._append_expression("*(struct RNode_MATCH3 *) %0#x" % val.GetValueAsUnsigned())
- elif nd_type == self.ruby_globals["NODE_LIT"]:
- self._append_expression("*(struct RNode_LIT *) %0#x" % val.GetValueAsUnsigned())
elif nd_type == self.ruby_globals["NODE_STR"]:
self._append_expression("*(struct RNode_STR *) %0#x" % val.GetValueAsUnsigned())
elif nd_type == self.ruby_globals["NODE_DSTR"]:
diff --git a/node.c b/node.c
index 5e27915e1d7016..c33b1dc73e3073 100644
--- a/node.c
+++ b/node.c
@@ -292,12 +292,7 @@ RBIMPL_ATTR_PURE()
static bool
nodetype_markable_p(enum node_type type)
{
- switch (type) {
- case NODE_LIT:
- return true;
- default:
- return false;
- }
+ return false;
}
NODE *
@@ -389,29 +384,10 @@ iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, vo
}
}
-static void
-mark_and_move_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
-{
-#ifdef UNIVERSAL_PARSER
- bug_report_func rb_bug = ast->node_buffer->config->bug;
-#endif
-
- switch (nd_type(node)) {
- case NODE_LIT:
- rb_gc_mark_and_move(&RNODE_LIT(node)->nd_lit);
- break;
- default:
- rb_bug("unreachable node %s", ruby_node_name(nd_type(node)));
- }
-}
-
void
rb_ast_mark_and_move(rb_ast_t *ast, bool reference_updating)
{
if (ast->node_buffer) {
- node_buffer_t *nb = ast->node_buffer;
- iterate_node_values(ast, &nb->markable, mark_and_move_ast_value, NULL);
-
if (ast->body.script_lines) rb_gc_mark_and_move(&ast->body.script_lines);
}
}
diff --git a/node_dump.c b/node_dump.c
index c4195d571ce1f4..37abea8441b4ea 100644
--- a/node_dump.c
+++ b/node_dump.c
@@ -60,6 +60,22 @@
field_flag; /* should be optimized away */ \
reset, field_flag = 0)
+#define A_SHAREABILITY(shareability) \
+ switch (shareability) { \
+ case rb_parser_shareable_none: \
+ rb_str_cat_cstr(buf, "none"); \
+ break; \
+ case rb_parser_shareable_literal: \
+ rb_str_cat_cstr(buf, "literal"); \
+ break; \
+ case rb_parser_shareable_copy: \
+ rb_str_cat_cstr(buf, "experimental_copy"); \
+ break; \
+ case rb_parser_shareable_everything: \
+ rb_str_cat_cstr(buf, "experimental_everything"); \
+ break; \
+ }
+
#define SIMPLE_FIELD1(name, ann) SIMPLE_FIELD(FIELD_NAME_LEN(name, ann), FIELD_NAME_DESC(name, ann))
#define F_CUSTOM1(name, ann) SIMPLE_FIELD1(#name, ann)
#define F_ID(name, type, ann) SIMPLE_FIELD1(#name, ann) A_ID(type(node)->name)
@@ -68,6 +84,7 @@
#define F_LIT(name, type, ann) SIMPLE_FIELD1(#name, ann) A_LIT(type(node)->name)
#define F_VALUE(name, val, ann) SIMPLE_FIELD1(#name, ann) A_LIT(val)
#define F_MSG(name, ann, desc) SIMPLE_FIELD1(#name, ann) A(desc)
+#define F_SHAREABILITY(name, type, ann) SIMPLE_FIELD1(#name, ann) A_SHAREABILITY(type(node)->name)
#define F_NODE(name, type, ann) \
COMPOUND_FIELD1(#name, ann) {dump_node(buf, indent, comment, RNODE(type(node)->name));}
@@ -463,6 +480,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
F_MSG(nd_vid, "constant", "0 (see extension field)");
F_NODE(nd_else, RNODE_CDECL, "extension");
}
+ F_SHAREABILITY(shareability, RNODE_CDECL, "shareability");
LAST_NODE;
F_NODE(nd_value, RNODE_CDECL, "rvalue");
return;
@@ -513,6 +531,7 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
ANN("example: A::B ||= 1");
F_NODE(nd_head, RNODE_OP_CDECL, "constant");
F_ID(nd_aid, RNODE_OP_CDECL, "operator");
+ F_SHAREABILITY(shareability, RNODE_OP_CDECL, "shareability");
LAST_NODE;
F_NODE(nd_value, RNODE_OP_CDECL, "rvalue");
return;
@@ -705,12 +724,6 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node)
F_NODE(nd_value, RNODE_MATCH3, "regexp (argument)");
return;
- case NODE_LIT:
- ANN("literal");
- ANN("format: [nd_lit]");
- ANN("example: :sym, /foo/");
- F_LIT(nd_lit, RNODE_LIT, "literal");
- return;
case NODE_STR:
ANN("string literal");
ANN("format: [nd_lit]");
diff --git a/parse.y b/parse.y
index 7bea55cb2b6052..faee1df7081d81 100644
--- a/parse.y
+++ b/parse.y
@@ -302,13 +302,6 @@ VALUE rb_ripper_none;
#include "ripper_init.h"
#endif
-enum shareability {
- shareable_none,
- shareable_literal,
- shareable_copy,
- shareable_everything,
-};
-
enum rescue_context {
before_rescue,
after_rescue,
@@ -322,7 +315,7 @@ struct lex_context {
unsigned int in_argdef: 1;
unsigned int in_def: 1;
unsigned int in_class: 1;
- BITFIELD(enum shareability, shareable_constant_value, 2);
+ BITFIELD(enum rb_parser_shareability, shareable_constant_value, 2);
BITFIELD(enum rescue_context, in_rescue, 2);
};
@@ -1095,13 +1088,13 @@ static rb_node_lasgn_t *rb_node_lasgn_new(struct parser_params *p, ID nd_vid, NO
static rb_node_dasgn_t *rb_node_dasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
static rb_node_gasgn_t *rb_node_gasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
static rb_node_iasgn_t *rb_node_iasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
-static rb_node_cdecl_t *rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, const YYLTYPE *loc);
+static rb_node_cdecl_t *rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc);
static rb_node_cvasgn_t *rb_node_cvasgn_new(struct parser_params *p, ID nd_vid, NODE *nd_value, const YYLTYPE *loc);
static rb_node_op_asgn1_t *rb_node_op_asgn1_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *index, NODE *rvalue, const YYLTYPE *loc);
static rb_node_op_asgn2_t *rb_node_op_asgn2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, ID nd_vid, ID nd_mid, bool nd_aid, const YYLTYPE *loc);
static rb_node_op_asgn_or_t *rb_node_op_asgn_or_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc);
static rb_node_op_asgn_and_t *rb_node_op_asgn_and_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, const YYLTYPE *loc);
-static rb_node_op_cdecl_t *rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, const YYLTYPE *loc);
+static rb_node_op_cdecl_t *rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc);
static rb_node_call_t *rb_node_call_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
static rb_node_opcall_t *rb_node_opcall_new(struct parser_params *p, NODE *nd_recv, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
static rb_node_fcall_t *rb_node_fcall_new(struct parser_params *p, ID nd_mid, NODE *nd_args, const YYLTYPE *loc);
@@ -1125,7 +1118,6 @@ static rb_node_nth_ref_t *rb_node_nth_ref_new(struct parser_params *p, long nd_n
static rb_node_back_ref_t *rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc);
static rb_node_match2_t *rb_node_match2_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc);
static rb_node_match3_t *rb_node_match3_new(struct parser_params *p, NODE *nd_recv, NODE *nd_value, const YYLTYPE *loc);
-static rb_node_lit_t *rb_node_lit_new(struct parser_params *p, VALUE nd_lit, const YYLTYPE *loc);
static rb_node_integer_t * rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc);
static rb_node_float_t * rb_node_float_new(struct parser_params *p, char* val, const YYLTYPE *loc);
static rb_node_rational_t * rb_node_rational_new(struct parser_params *p, char* val, int base, int seen_point, const YYLTYPE *loc);
@@ -1204,13 +1196,13 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#define NEW_DASGN(v,val,loc) (NODE *)rb_node_dasgn_new(p,v,val,loc)
#define NEW_GASGN(v,val,loc) (NODE *)rb_node_gasgn_new(p,v,val,loc)
#define NEW_IASGN(v,val,loc) (NODE *)rb_node_iasgn_new(p,v,val,loc)
-#define NEW_CDECL(v,val,path,loc) (NODE *)rb_node_cdecl_new(p,v,val,path,loc)
+#define NEW_CDECL(v,val,path,share,loc) (NODE *)rb_node_cdecl_new(p,v,val,path,share,loc)
#define NEW_CVASGN(v,val,loc) (NODE *)rb_node_cvasgn_new(p,v,val,loc)
#define NEW_OP_ASGN1(r,id,idx,rval,loc) (NODE *)rb_node_op_asgn1_new(p,r,id,idx,rval,loc)
#define NEW_OP_ASGN2(r,t,i,o,val,loc) (NODE *)rb_node_op_asgn2_new(p,r,val,i,o,t,loc)
#define NEW_OP_ASGN_OR(i,val,loc) (NODE *)rb_node_op_asgn_or_new(p,i,val,loc)
#define NEW_OP_ASGN_AND(i,val,loc) (NODE *)rb_node_op_asgn_and_new(p,i,val,loc)
-#define NEW_OP_CDECL(v,op,val,loc) (NODE *)rb_node_op_cdecl_new(p,v,val,op,loc)
+#define NEW_OP_CDECL(v,op,val,share,loc) (NODE *)rb_node_op_cdecl_new(p,v,val,op,share,loc)
#define NEW_CALL(r,m,a,loc) (NODE *)rb_node_call_new(p,r,m,a,loc)
#define NEW_OPCALL(r,m,a,loc) (NODE *)rb_node_opcall_new(p,r,m,a,loc)
#define NEW_FCALL(m,a,loc) rb_node_fcall_new(p,m,a,loc)
@@ -1234,7 +1226,6 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE
#define NEW_BACK_REF(n,loc) (NODE *)rb_node_back_ref_new(p,n,loc)
#define NEW_MATCH2(n1,n2,loc) (NODE *)rb_node_match2_new(p,n1,n2,loc)
#define NEW_MATCH3(r,n2,loc) (NODE *)rb_node_match3_new(p,r,n2,loc)
-#define NEW_LIT(l,loc) (NODE *)rb_node_lit_new(p,l,loc)
#define NEW_INTEGER(val, base,loc) (NODE *)rb_node_integer_new(p,val,base,loc)
#define NEW_FLOAT(val,loc) (NODE *)rb_node_float_new(p,val,loc)
#define NEW_RATIONAL(val,base,seen_point,loc) (NODE *)rb_node_rational_new(p,val,base,seen_point,loc)
@@ -2651,9 +2642,6 @@ rb_parser_tokens_free(rb_parser_t *p, rb_parser_ary_t *tokens)
case NODE_IMAGINARY:
rb_parser_printf(p, "%+"PRIsVALUE, rb_node_imaginary_literal_val($$));
break;
- case NODE_LIT:
- rb_parser_printf(p, "%+"PRIsVALUE, RNODE_LIT($$)->nd_lit);
- break;
default:
break;
}
@@ -6857,7 +6845,6 @@ singleton : var_ref
case NODE_DXSTR:
case NODE_REGX:
case NODE_DREGX:
- case NODE_LIT:
case NODE_SYM:
case NODE_LINE:
case NODE_FILE:
@@ -9751,23 +9738,23 @@ parser_set_shareable_constant_value(struct parser_params *p, const char *name, c
switch (*val) {
case 'n': case 'N':
if (STRCASECMP(val, "none") == 0) {
- p->ctxt.shareable_constant_value = shareable_none;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_none;
return;
}
break;
case 'l': case 'L':
if (STRCASECMP(val, "literal") == 0) {
- p->ctxt.shareable_constant_value = shareable_literal;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_literal;
return;
}
break;
case 'e': case 'E':
if (STRCASECMP(val, "experimental_copy") == 0) {
- p->ctxt.shareable_constant_value = shareable_copy;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_copy;
return;
}
if (STRCASECMP(val, "experimental_everything") == 0) {
- p->ctxt.shareable_constant_value = shareable_everything;
+ p->ctxt.shareable_constant_value = rb_parser_shareable_everything;
return;
}
break;
@@ -12233,15 +12220,6 @@ rb_node_back_ref_new(struct parser_params *p, long nd_nth, const YYLTYPE *loc)
return n;
}
-static rb_node_lit_t *
-rb_node_lit_new(struct parser_params *p, VALUE nd_lit, const YYLTYPE *loc)
-{
- rb_node_lit_t *n = NODE_NEWNODE(NODE_LIT, rb_node_lit_t, loc);
- n->nd_lit = nd_lit;
-
- return n;
-}
-
static rb_node_integer_t *
rb_node_integer_new(struct parser_params *p, char* val, int base, const YYLTYPE *loc)
{
@@ -12652,23 +12630,25 @@ rb_node_encoding_new(struct parser_params *p, const YYLTYPE *loc)
}
static rb_node_cdecl_t *
-rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, const YYLTYPE *loc)
+rb_node_cdecl_new(struct parser_params *p, ID nd_vid, NODE *nd_value, NODE *nd_else, enum rb_parser_shareability shareability, const YYLTYPE *loc)
{
rb_node_cdecl_t *n = NODE_NEWNODE(NODE_CDECL, rb_node_cdecl_t, loc);
n->nd_vid = nd_vid;
n->nd_value = nd_value;
n->nd_else = nd_else;
+ n->shareability = shareability;
return n;
}
static rb_node_op_cdecl_t *
-rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, const YYLTYPE *loc)
+rb_node_op_cdecl_new(struct parser_params *p, NODE *nd_head, NODE *nd_value, ID nd_aid, enum rb_parser_shareability shareability, const YYLTYPE *loc)
{
rb_node_op_cdecl_t *n = NODE_NEWNODE(NODE_OP_CDECL, rb_node_op_cdecl_t, loc);
n->nd_head = nd_head;
n->nd_value = nd_value;
n->nd_aid = nd_aid;
+ n->shareability = shareability;
return n;
}
@@ -13772,7 +13752,7 @@ assignable(struct parser_params *p, ID id, NODE *val, const YYLTYPE *loc)
case NODE_LASGN: return NEW_LASGN(id, val, loc);
case NODE_GASGN: return NEW_GASGN(id, val, loc);
case NODE_IASGN: return NEW_IASGN(id, val, loc);
- case NODE_CDECL: return NEW_CDECL(id, val, 0, loc);
+ case NODE_CDECL: return NEW_CDECL(id, val, 0, p->ctxt.shareable_constant_value, loc);
case NODE_CVASGN: return NEW_CVASGN(id, val, loc);
}
/* TODO: FIXME */
@@ -14043,256 +14023,8 @@ mark_lvar_used(struct parser_params *p, NODE *rhs)
}
}
-static NODE *
-const_decl_path(struct parser_params *p, NODE *dest)
-{
- NODE *n = dest;
- if (!nd_type_p(dest, NODE_CALL)) {
- const YYLTYPE *loc = &dest->nd_loc;
- VALUE path = rb_node_const_decl_val(dest);
- n = NEW_LIT(path, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(n)->nd_lit);
- }
- return n;
-}
-
-static NODE *
-make_shareable_node(struct parser_params *p, NODE *value, bool copy, const YYLTYPE *loc)
-{
- NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
-
- if (copy) {
- return NEW_CALL(fcore, rb_intern("make_shareable_copy"),
- NEW_LIST(value, loc), loc);
- }
- else {
- return NEW_CALL(fcore, rb_intern("make_shareable"),
- NEW_LIST(value, loc), loc);
- }
-}
-
-static NODE *
-ensure_shareable_node(struct parser_params *p, NODE *dest, NODE *value, const YYLTYPE *loc)
-{
- NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
- NODE *args = NEW_LIST(value, loc);
- args = list_append(p, args, const_decl_path(p, dest));
- return NEW_CALL(fcore, rb_intern("ensure_shareable"), args, loc);
-}
-
static int is_static_content(NODE *node);
-static VALUE
-shareable_literal_value(struct parser_params *p, NODE *node)
-{
- if (!node) return Qnil;
- enum node_type type = nd_type(node);
- switch (type) {
- case NODE_TRUE:
- return Qtrue;
- case NODE_FALSE:
- return Qfalse;
- case NODE_NIL:
- return Qnil;
- case NODE_SYM:
- return rb_node_sym_string_val(node);
- case NODE_LINE:
- return rb_node_line_lineno_val(node);
- case NODE_INTEGER:
- return rb_node_integer_literal_val(node);
- case NODE_FLOAT:
- return rb_node_float_literal_val(node);
- case NODE_RATIONAL:
- return rb_node_rational_literal_val(node);
- case NODE_IMAGINARY:
- return rb_node_imaginary_literal_val(node);
- case NODE_ENCODING:
- return rb_node_encoding_val(node);
- case NODE_REGX:
- return rb_node_regx_string_val(node);
- case NODE_LIT:
- return RNODE_LIT(node)->nd_lit;
- default:
- return Qundef;
- }
-}
-
-#ifndef SHAREABLE_BARE_EXPRESSION
-#define SHAREABLE_BARE_EXPRESSION 1
-#endif
-
-static NODE *
-shareable_literal_constant(struct parser_params *p, enum shareability shareable,
- NODE *dest, NODE *value, const YYLTYPE *loc, size_t level)
-{
-# define shareable_literal_constant_next(n) \
- shareable_literal_constant(p, shareable, dest, (n), &(n)->nd_loc, level+1)
- VALUE lit = Qnil;
-
- if (!value) return 0;
- enum node_type type = nd_type(value);
- switch (type) {
- case NODE_TRUE:
- case NODE_FALSE:
- case NODE_NIL:
- case NODE_LIT:
- case NODE_SYM:
- case NODE_REGX:
- case NODE_LINE:
- case NODE_INTEGER:
- case NODE_FLOAT:
- case NODE_RATIONAL:
- case NODE_IMAGINARY:
- case NODE_ENCODING:
- return value;
-
- case NODE_DSTR:
- if (shareable == shareable_literal) {
- value = NEW_CALL(value, idUMinus, 0, loc);
- }
- return value;
-
- case NODE_STR:
- lit = rb_str_to_interned_str(rb_node_str_string_val(value));
- value = NEW_LIT(lit, loc);
- RB_OBJ_WRITE(p->ast, &RNODE_LIT(value)->nd_lit, lit);
- return value;
-
- case NODE_FILE:
- lit = rb_str_to_interned_str(rb_node_file_path_val(value));
- value = NEW_LIT(lit, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(value)->nd_lit);
- return value;
-
- case NODE_ZLIST:
- lit = rb_ary_new();
- OBJ_FREEZE_RAW(lit);
- NODE *n = NEW_LIT(lit, loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(n)->nd_lit);
- return n;
-
- case NODE_LIST:
- lit = rb_ary_new();
- for (NODE *n = value; n; n = RNODE_LIST(n)->nd_next) {
- NODE *elt = RNODE_LIST(n)->nd_head;
- if (elt) {
- elt = shareable_literal_constant_next(elt);
- if (elt) {
- RNODE_LIST(n)->nd_head = elt;
- }
- else if (RTEST(lit)) {
- rb_ary_clear(lit);
- lit = Qfalse;
- }
- }
- if (RTEST(lit)) {
- VALUE e = shareable_literal_value(p, elt);
- if (!UNDEF_P(e)) {
- rb_ary_push(lit, e);
- }
- else {
- rb_ary_clear(lit);
- lit = Qnil; /* make shareable at runtime */
- }
- }
- }
- break;
-
- case NODE_HASH:
- if (!RNODE_HASH(value)->nd_brace) return 0;
- lit = rb_hash_new();
- for (NODE *n = RNODE_HASH(value)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) {
- NODE *key = RNODE_LIST(n)->nd_head;
- NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head;
- if (key) {
- key = shareable_literal_constant_next(key);
- if (key) {
- RNODE_LIST(n)->nd_head = key;
- }
- else if (RTEST(lit)) {
- rb_hash_clear(lit);
- lit = Qfalse;
- }
- }
- if (val) {
- val = shareable_literal_constant_next(val);
- if (val) {
- RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head = val;
- }
- else if (RTEST(lit)) {
- rb_hash_clear(lit);
- lit = Qfalse;
- }
- }
- if (RTEST(lit)) {
- VALUE k = shareable_literal_value(p, key);
- VALUE v = shareable_literal_value(p, val);
- if (!UNDEF_P(k) && !UNDEF_P(v)) {
- rb_hash_aset(lit, k, v);
- }
- else {
- rb_hash_clear(lit);
- lit = Qnil; /* make shareable at runtime */
- }
- }
- }
- break;
-
- default:
- if (shareable == shareable_literal &&
- (SHAREABLE_BARE_EXPRESSION || level > 0)) {
- return ensure_shareable_node(p, dest, value, loc);
- }
- return 0;
- }
-
- /* Array or Hash */
- if (!lit) return 0;
- if (NIL_P(lit)) {
- // if shareable_literal, all elements should have been ensured
- // as shareable
- value = make_shareable_node(p, value, false, loc);
- }
- else {
- value = NEW_LIT(rb_ractor_make_shareable(lit), loc);
- RB_OBJ_WRITTEN(p->ast, Qnil, RNODE_LIT(value)->nd_lit);
- }
-
- return value;
-# undef shareable_literal_constant_next
-}
-
-static NODE *
-shareable_constant_value(struct parser_params *p, enum shareability shareable,
- NODE *lhs, NODE *value, const YYLTYPE *loc)
-{
- if (!value) return 0;
- switch (shareable) {
- case shareable_none:
- return value;
-
- case shareable_literal:
- {
- NODE *lit = shareable_literal_constant(p, shareable, lhs, value, loc, 0);
- if (lit) return lit;
- return value;
- }
- break;
-
- case shareable_copy:
- case shareable_everything:
- {
- NODE *lit = shareable_literal_constant(p, shareable, lhs, value, loc, 0);
- if (lit) return lit;
- return make_shareable_node(p, value, shareable == shareable_copy, loc);
- }
- break;
-
- default:
- UNREACHABLE_RETURN(0);
- }
-}
-
static NODE *
node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ctxt, const YYLTYPE *loc)
{
@@ -14300,9 +14032,6 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ct
switch (nd_type(lhs)) {
case NODE_CDECL:
- rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
- /* fallthru */
-
case NODE_GASGN:
case NODE_IASGN:
case NODE_LASGN:
@@ -14489,7 +14218,6 @@ void_expr(struct parser_params *p, NODE *node)
case NODE_CONST:
useless = "a constant";
break;
- case NODE_LIT:
case NODE_SYM:
case NODE_LINE:
case NODE_FILE:
@@ -14635,7 +14363,6 @@ is_static_content(NODE *node)
do {
if (!is_static_content(RNODE_LIST(node)->nd_head)) return 0;
} while ((node = RNODE_LIST(node)->nd_next) != 0);
- case NODE_LIT:
case NODE_SYM:
case NODE_REGX:
case NODE_LINE:
@@ -14770,23 +14497,9 @@ cond0(struct parser_params *p, NODE *node, enum cond_type type, const YYLTYPE *l
case NODE_SYM:
case NODE_DSYM:
- warn_symbol:
SWITCH_BY_COND_TYPE(type, warning, "symbol ");
break;
- case NODE_LIT:
- if (RNODE_LIT(node)->nd_lit == Qtrue ||
- RNODE_LIT(node)->nd_lit == Qfalse) {
- /* booleans are OK, e.g., while true */
- }
- else if (SYMBOL_P(RNODE_LIT(node)->nd_lit)) {
- goto warn_symbol;
- }
- else {
- SWITCH_BY_COND_TYPE(type, warning, "");
- }
- break;
-
case NODE_LINE:
SWITCH_BY_COND_TYPE(type, warning, "");
break;
@@ -15281,28 +14994,12 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c
if (lhs) {
ID vid = get_nd_vid(p, lhs);
YYLTYPE lhs_loc = lhs->nd_loc;
- int shareable = ctxt.shareable_constant_value;
- if (shareable) {
- switch (nd_type(lhs)) {
- case NODE_CDECL:
- case NODE_COLON2:
- case NODE_COLON3:
- break;
- default:
- shareable = 0;
- break;
- }
- }
if (op == tOROP) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
set_nd_value(p, lhs, rhs);
nd_set_loc(lhs, loc);
asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc);
}
else if (op == tANDOP) {
- if (shareable) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
- }
set_nd_value(p, lhs, rhs);
nd_set_loc(lhs, loc);
asgn = NEW_OP_ASGN_AND(gettable(p, vid, &lhs_loc), lhs, loc);
@@ -15310,9 +15007,6 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c
else {
asgn = lhs;
rhs = NEW_CALL(gettable(p, vid, &lhs_loc), op, NEW_LIST(rhs, &rhs->nd_loc), loc);
- if (shareable) {
- rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
- }
set_nd_value(p, asgn, rhs);
nd_set_loc(asgn, loc);
}
@@ -15353,8 +15047,7 @@ new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct
NODE *asgn;
if (lhs) {
- rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
- asgn = NEW_OP_CDECL(lhs, op, rhs, loc);
+ asgn = NEW_OP_CDECL(lhs, op, rhs, ctxt.shareable_constant_value, loc);
}
else {
asgn = NEW_ERROR(loc);
@@ -15369,7 +15062,7 @@ const_decl(struct parser_params *p, NODE *path, const YYLTYPE *loc)
if (p->ctxt.in_def) {
yyerror1(loc, "dynamic constant assignment");
}
- return NEW_CDECL(0, 0, (path), loc);
+ return NEW_CDECL(0, 0, (path), p->ctxt.shareable_constant_value, loc);
}
#ifdef RIPPER
static VALUE
diff --git a/prism/config.yml b/prism/config.yml
index c813b02d9f15b2..df3a4714e6479d 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -122,6 +122,7 @@ errors:
- INCOMPLETE_VARIABLE_INSTANCE
- INCOMPLETE_VARIABLE_INSTANCE_3_3_0
- INSTANCE_VARIABLE_BARE
+ - INVALID_BLOCK_EXIT
- INVALID_CHARACTER
- INVALID_ENCODING_MAGIC_COMMENT
- INVALID_FLOAT_EXPONENT
@@ -135,8 +136,12 @@ errors:
- INVALID_NUMBER_UNDERSCORE
- INVALID_PERCENT
- INVALID_PRINTABLE_CHARACTER
+ - INVALID_RETRY_AFTER_ELSE
+ - INVALID_RETRY_AFTER_ENSURE
+ - INVALID_RETRY_WITHOUT_RESCUE
- INVALID_VARIABLE_GLOBAL
- INVALID_VARIABLE_GLOBAL_3_3_0
+ - INVALID_YIELD
- IT_NOT_ALLOWED_NUMBERED
- IT_NOT_ALLOWED_ORDINARY
- LAMBDA_OPEN
diff --git a/prism/node.h b/prism/node.h
index a001b4a9e470e4..8736e59a94d1a4 100644
--- a/prism/node.h
+++ b/prism/node.h
@@ -11,15 +11,11 @@
#include "prism/util/pm_buffer.h"
/**
- * Attempts to grow the node list to the next size. If there is already
- * capacity in the list, this function does nothing. Otherwise it reallocates
- * the list to be twice as large as it was before. If the reallocation fails,
- * this function returns false, otherwise it returns true.
- *
- * @param list The list to grow.
- * @return True if the list was successfully grown, false otherwise.
+ * Loop through each node in the node list, writing each node to the given
+ * pm_node_t pointer.
*/
-bool pm_node_list_grow(pm_node_list_t *list);
+#define PM_NODE_LIST_FOREACH(list, index, node) \
+ for (size_t index = 0; index < (list)->size && ((node) = (list)->nodes[index]); index++)
/**
* Append a new node onto the end of the node list.
@@ -35,8 +31,15 @@ void pm_node_list_append(pm_node_list_t *list, pm_node_t *node);
* @param list The list to prepend to.
* @param node The node to prepend.
*/
-void
-pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node);
+void pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node);
+
+/**
+ * Concatenate the given node list onto the end of the other node list.
+ *
+ * @param list The list to concatenate onto.
+ * @param other The list to concatenate.
+ */
+void pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other);
/**
* Free the internal memory associated with the given node list.
diff --git a/prism/parser.h b/prism/parser.h
index f706a67de7c75d..889a1871493ffa 100644
--- a/prism/parser.h
+++ b/prism/parser.h
@@ -268,12 +268,30 @@ typedef enum {
/** a begin statement */
PM_CONTEXT_BEGIN,
+ /** an ensure statement with an explicit begin */
+ PM_CONTEXT_BEGIN_ENSURE,
+
+ /** a rescue else statement with an explicit begin */
+ PM_CONTEXT_BEGIN_ELSE,
+
+ /** a rescue statement with an explicit begin */
+ PM_CONTEXT_BEGIN_RESCUE,
+
/** expressions in block arguments using braces */
PM_CONTEXT_BLOCK_BRACES,
/** expressions in block arguments using do..end */
PM_CONTEXT_BLOCK_KEYWORDS,
+ /** an ensure statement within a do..end block */
+ PM_CONTEXT_BLOCK_ENSURE,
+
+ /** a rescue else statement within a do..end block */
+ PM_CONTEXT_BLOCK_ELSE,
+
+ /** a rescue statement within a do..end block */
+ PM_CONTEXT_BLOCK_RESCUE,
+
/** a case when statements */
PM_CONTEXT_CASE_WHEN,
@@ -283,12 +301,33 @@ typedef enum {
/** a class declaration */
PM_CONTEXT_CLASS,
+ /** an ensure statement within a class statement */
+ PM_CONTEXT_CLASS_ENSURE,
+
+ /** a rescue else statement within a class statement */
+ PM_CONTEXT_CLASS_ELSE,
+
+ /** a rescue statement within a class statement */
+ PM_CONTEXT_CLASS_RESCUE,
+
/** a method definition */
PM_CONTEXT_DEF,
+ /** an ensure statement within a method definition */
+ PM_CONTEXT_DEF_ENSURE,
+
+ /** a rescue else statement within a method definition */
+ PM_CONTEXT_DEF_ELSE,
+
+ /** a rescue statement within a method definition */
+ PM_CONTEXT_DEF_RESCUE,
+
/** a method definition's parameters */
PM_CONTEXT_DEF_PARAMS,
+ /** a defined? expression */
+ PM_CONTEXT_DEFINED,
+
/** a method definition's default parameter */
PM_CONTEXT_DEFAULT_PARAMS,
@@ -301,12 +340,6 @@ typedef enum {
/** an interpolated expression */
PM_CONTEXT_EMBEXPR,
- /** an ensure statement */
- PM_CONTEXT_ENSURE,
-
- /** an ensure statement within a method definition */
- PM_CONTEXT_ENSURE_DEF,
-
/** a for loop */
PM_CONTEXT_FOR,
@@ -322,12 +355,30 @@ typedef enum {
/** a lambda expression with do..end */
PM_CONTEXT_LAMBDA_DO_END,
+ /** an ensure statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_ENSURE,
+
+ /** a rescue else statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_ELSE,
+
+ /** a rescue statement within a lambda expression */
+ PM_CONTEXT_LAMBDA_RESCUE,
+
/** the top level context */
PM_CONTEXT_MAIN,
/** a module declaration */
PM_CONTEXT_MODULE,
+ /** an ensure statement within a module statement */
+ PM_CONTEXT_MODULE_ENSURE,
+
+ /** a rescue else statement within a module statement */
+ PM_CONTEXT_MODULE_ELSE,
+
+ /** a rescue statement within a module statement */
+ PM_CONTEXT_MODULE_RESCUE,
+
/** a parenthesized expression */
PM_CONTEXT_PARENS,
@@ -340,20 +391,23 @@ typedef enum {
/** a BEGIN block */
PM_CONTEXT_PREEXE,
- /** a rescue else statement */
- PM_CONTEXT_RESCUE_ELSE,
+ /** a modifier rescue clause */
+ PM_CONTEXT_RESCUE_MODIFIER,
- /** a rescue else statement within a method definition */
- PM_CONTEXT_RESCUE_ELSE_DEF,
+ /** a singleton class definition */
+ PM_CONTEXT_SCLASS,
- /** a rescue statement */
- PM_CONTEXT_RESCUE,
+ /** an ensure statement with a singleton class */
+ PM_CONTEXT_SCLASS_ENSURE,
- /** a rescue statement within a method definition */
- PM_CONTEXT_RESCUE_DEF,
+ /** a rescue else statement with a singleton class */
+ PM_CONTEXT_SCLASS_ELSE,
- /** a singleton class definition */
- PM_CONTEXT_SCLASS,
+ /** a rescue statement with a singleton class */
+ PM_CONTEXT_SCLASS_RESCUE,
+
+ /** a ternary expression */
+ PM_CONTEXT_TERNARY,
/** an unless statement */
PM_CONTEXT_UNLESS,
@@ -716,6 +770,19 @@ struct pm_parser {
/** The current parameter name id on parsing its default value. */
pm_constant_id_t current_param_name;
+ /**
+ * When parsing block exits (e.g., break, next, redo), we need to validate
+ * that they are in correct contexts. For the most part we can do this by
+ * looking at our parent contexts. However, modifier while and until
+ * expressions can change that context to make block exits valid. In these
+ * cases, we need to keep track of the block exits and then validate them
+ * after the expression has been parsed.
+ *
+ * We use a pointer here because we don't want to keep a whole list attached
+ * since this will only be used in the context of begin/end expressions.
+ */
+ pm_node_list_t *current_block_exits;
+
/** The version of prism that we should use to parse. */
pm_options_version_t version;
@@ -732,6 +799,12 @@ struct pm_parser {
*/
int8_t frozen_string_literal;
+ /**
+ * Whether or not we are parsing an eval string. This impacts whether or not
+ * we should evaluate if block exits/yields are valid.
+ */
+ bool parsing_eval;
+
/** Whether or not we're at the beginning of a command. */
bool command_start;
diff --git a/prism/prism.c b/prism/prism.c
index d03a11583f7a4d..74b738a416cfd2 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -33,39 +33,57 @@ PRISM_ATTRIBUTE_UNUSED static const char *
debug_context(pm_context_t context) {
switch (context) {
case PM_CONTEXT_BEGIN: return "BEGIN";
- case PM_CONTEXT_CLASS: return "CLASS";
+ case PM_CONTEXT_BEGIN_ENSURE: return "BEGIN_ENSURE";
+ case PM_CONTEXT_BEGIN_ELSE: return "BEGIN_ELSE";
+ case PM_CONTEXT_BEGIN_RESCUE: return "BEGIN_RESCUE";
+ case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES";
+ case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS";
+ case PM_CONTEXT_BLOCK_ENSURE: return "BLOCK_ENSURE";
+ case PM_CONTEXT_BLOCK_ELSE: return "BLOCK_ELSE";
+ case PM_CONTEXT_BLOCK_RESCUE: return "BLOCK_RESCUE";
case PM_CONTEXT_CASE_IN: return "CASE_IN";
case PM_CONTEXT_CASE_WHEN: return "CASE_WHEN";
+ case PM_CONTEXT_CLASS: return "CLASS";
+ case PM_CONTEXT_CLASS_ELSE: return "CLASS_ELSE";
+ case PM_CONTEXT_CLASS_ENSURE: return "CLASS_ENSURE";
+ case PM_CONTEXT_CLASS_RESCUE: return "CLASS_RESCUE";
case PM_CONTEXT_DEF: return "DEF";
case PM_CONTEXT_DEF_PARAMS: return "DEF_PARAMS";
+ case PM_CONTEXT_DEF_ENSURE: return "DEF_ENSURE";
+ case PM_CONTEXT_DEF_ELSE: return "DEF_ELSE";
+ case PM_CONTEXT_DEF_RESCUE: return "DEF_RESCUE";
case PM_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS";
- case PM_CONTEXT_ENSURE: return "ENSURE";
- case PM_CONTEXT_ENSURE_DEF: return "ENSURE_DEF";
+ case PM_CONTEXT_DEFINED: return "DEFINED";
case PM_CONTEXT_ELSE: return "ELSE";
case PM_CONTEXT_ELSIF: return "ELSIF";
case PM_CONTEXT_EMBEXPR: return "EMBEXPR";
- case PM_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES";
- case PM_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS";
- case PM_CONTEXT_FOR: return "FOR";
case PM_CONTEXT_FOR_INDEX: return "FOR_INDEX";
+ case PM_CONTEXT_FOR: return "FOR";
case PM_CONTEXT_IF: return "IF";
+ case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES";
+ case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END";
+ case PM_CONTEXT_LAMBDA_ENSURE: return "LAMBDA_ENSURE";
+ case PM_CONTEXT_LAMBDA_ELSE: return "LAMBDA_ELSE";
+ case PM_CONTEXT_LAMBDA_RESCUE: return "LAMBDA_RESCUE";
case PM_CONTEXT_MAIN: return "MAIN";
case PM_CONTEXT_MODULE: return "MODULE";
+ case PM_CONTEXT_MODULE_ELSE: return "MODULE_ELSE";
+ case PM_CONTEXT_MODULE_ENSURE: return "MODULE_ENSURE";
+ case PM_CONTEXT_MODULE_RESCUE: return "MODULE_RESCUE";
case PM_CONTEXT_NONE: return "NONE";
case PM_CONTEXT_PARENS: return "PARENS";
case PM_CONTEXT_POSTEXE: return "POSTEXE";
case PM_CONTEXT_PREDICATE: return "PREDICATE";
case PM_CONTEXT_PREEXE: return "PREEXE";
- case PM_CONTEXT_RESCUE: return "RESCUE";
- case PM_CONTEXT_RESCUE_ELSE: return "RESCUE_ELSE";
- case PM_CONTEXT_RESCUE_ELSE_DEF: return "RESCUE_ELSE_DEF";
- case PM_CONTEXT_RESCUE_DEF: return "RESCUE_DEF";
+ case PM_CONTEXT_RESCUE_MODIFIER: return "RESCUE_MODIFIER";
case PM_CONTEXT_SCLASS: return "SCLASS";
+ case PM_CONTEXT_SCLASS_ENSURE: return "SCLASS_ENSURE";
+ case PM_CONTEXT_SCLASS_ELSE: return "SCLASS_ELSE";
+ case PM_CONTEXT_SCLASS_RESCUE: return "SCLASS_RESCUE";
+ case PM_CONTEXT_TERNARY: return "TERNARY";
case PM_CONTEXT_UNLESS: return "UNLESS";
case PM_CONTEXT_UNTIL: return "UNTIL";
case PM_CONTEXT_WHILE: return "WHILE";
- case PM_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES";
- case PM_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END";
}
return NULL;
}
@@ -832,11 +850,14 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
switch (PM_NODE_TYPE(node)) {
case PM_ARRAY_NODE: {
const pm_array_node_t *cast = (const pm_array_node_t *) node;
- for (size_t index = 0; index < cast->elements.size; index++) {
- if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) {
+ const pm_node_t *element;
+
+ PM_NODE_LIST_FOREACH(&cast->elements, index, element) {
+ if (!pm_conditional_predicate_warn_write_literal_p(element)) {
return false;
}
}
+
return true;
}
case PM_FALSE_NODE:
@@ -1610,9 +1631,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node
// For now we're going to just copy over each pointer manually. This could be
// much more efficient, as we could instead resize the node list.
bool found_rest = false;
- for (size_t index = 0; index < nodes->size; index++) {
- pm_node_t *child = nodes->nodes[index];
+ pm_node_t *child;
+ PM_NODE_LIST_FOREACH(nodes, index, child) {
if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
node->rest = child;
found_rest = true;
@@ -3727,8 +3748,8 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme
.closing_loc = PM_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE
};
- for (size_t index = 0; index < elements->size; index++) {
- pm_node_t *element = elements->nodes[index];
+ pm_node_t *element;
+ PM_NODE_LIST_FOREACH(elements, index, element) {
pm_node_list_append(&node->elements, element);
}
@@ -4486,8 +4507,9 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin
};
if (parts != NULL) {
- for (size_t index = 0; index < parts->size; index++) {
- pm_interpolated_string_node_append(parser, node, parts->nodes[index]);
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_interpolated_string_node_append(parser, node, part);
}
}
@@ -4549,8 +4571,9 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin
};
if (parts != NULL) {
- for (size_t index = 0; index < parts->size; index++) {
- pm_interpolated_symbol_node_append(node, parts->nodes[index]);
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_interpolated_symbol_node_append(node, part);
}
}
@@ -4841,7 +4864,7 @@ pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, c
static pm_local_variable_read_node_t *
pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth) {
if (parser->current_param_name == name_id) {
- pm_parser_err_token(parser, name, PM_ERR_PARAMETER_CIRCULAR);
+ PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, *name, PM_ERR_PARAMETER_CIRCULAR);
}
pm_local_variable_read_node_t *node = PM_ALLOC_NODE(parser, pm_local_variable_read_node_t);
@@ -7591,6 +7614,9 @@ context_terminator(pm_context_t context, pm_token_t *token) {
switch (context) {
case PM_CONTEXT_MAIN:
case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_RESCUE_MODIFIER:
return token->type == PM_TOKEN_EOF;
case PM_CONTEXT_DEFAULT_PARAMS:
return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT;
@@ -7608,8 +7634,13 @@ context_terminator(pm_context_t context, pm_token_t *token) {
case PM_CONTEXT_UNTIL:
case PM_CONTEXT_ELSE:
case PM_CONTEXT_FOR:
- case PM_CONTEXT_ENSURE:
- case PM_CONTEXT_ENSURE_DEF:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE:
return token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_FOR_INDEX:
return token->type == PM_TOKEN_KEYWORD_IN;
@@ -7629,11 +7660,21 @@ context_terminator(pm_context_t context, pm_token_t *token) {
case PM_CONTEXT_PARENS:
return token->type == PM_TOKEN_PARENTHESIS_RIGHT;
case PM_CONTEXT_BEGIN:
- case PM_CONTEXT_RESCUE:
- case PM_CONTEXT_RESCUE_DEF:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_SCLASS_RESCUE:
return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
- case PM_CONTEXT_RESCUE_ELSE:
- case PM_CONTEXT_RESCUE_ELSE_DEF:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE:
return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END;
case PM_CONTEXT_LAMBDA_BRACES:
return token->type == PM_TOKEN_BRACE_RIGHT;
@@ -7706,13 +7747,22 @@ context_def_p(const pm_parser_t *parser) {
switch (context_node->context) {
case PM_CONTEXT_DEF:
case PM_CONTEXT_DEF_PARAMS:
- case PM_CONTEXT_ENSURE_DEF:
- case PM_CONTEXT_RESCUE_DEF:
- case PM_CONTEXT_RESCUE_ELSE_DEF:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_DEF_ELSE:
return true;
case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_CLASS_ELSE:
case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_MODULE_ELSE:
case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_SCLASS_ELSE:
return false;
default:
context_node = context_node->prev;
@@ -7741,11 +7791,24 @@ context_human(pm_context_t context) {
case PM_CONTEXT_DEF: return "method definition";
case PM_CONTEXT_DEF_PARAMS: return "method parameters";
case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
- case PM_CONTEXT_ELSE: return "'else' clause";
+ case PM_CONTEXT_DEFINED: return "'defined?' expression";
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
case PM_CONTEXT_ELSIF: return "'elsif' clause";
case PM_CONTEXT_EMBEXPR: return "embedded expression";
- case PM_CONTEXT_ENSURE: return "'ensure' clause";
- case PM_CONTEXT_ENSURE_DEF: return "'ensure' clause";
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
case PM_CONTEXT_FOR: return "for loop";
case PM_CONTEXT_FOR_INDEX: return "for loop index";
case PM_CONTEXT_IF: return "if statement";
@@ -7757,11 +7820,16 @@ context_human(pm_context_t context) {
case PM_CONTEXT_POSTEXE: return "'END' block";
case PM_CONTEXT_PREDICATE: return "predicate";
case PM_CONTEXT_PREEXE: return "'BEGIN' block";
- case PM_CONTEXT_RESCUE_ELSE: return "'else' clause";
- case PM_CONTEXT_RESCUE_ELSE_DEF: return "'else' clause";
- case PM_CONTEXT_RESCUE: return "'rescue' clause";
- case PM_CONTEXT_RESCUE_DEF: return "'rescue' clause";
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
case PM_CONTEXT_SCLASS: return "singleton class definition";
+ case PM_CONTEXT_TERNARY: return "ternary expression";
case PM_CONTEXT_UNLESS: return "unless statement";
case PM_CONTEXT_UNTIL: return "until statement";
case PM_CONTEXT_WHILE: return "while statement";
@@ -13425,12 +13493,22 @@ parse_parameters(
return params;
}
+typedef enum {
+ PM_RESCUES_BEGIN = 1,
+ PM_RESCUES_BLOCK,
+ PM_RESCUES_CLASS,
+ PM_RESCUES_DEF,
+ PM_RESCUES_LAMBDA,
+ PM_RESCUES_MODULE,
+ PM_RESCUES_SCLASS
+} pm_rescues_type_t;
+
/**
* Parse any number of rescue clauses. This will form a linked list of if
* nodes pointing to each other from the top.
*/
static inline void
-parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) {
+parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, pm_rescues_type_t type) {
pm_rescue_node_t *current = NULL;
while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
@@ -13493,10 +13571,21 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) {
if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
pm_accepts_block_stack_push(parser, true);
- pm_statements_node_t *statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_DEF : PM_CONTEXT_RESCUE);
- if (statements) {
- pm_rescue_node_statements_set(rescue, statements);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
}
+
+ pm_statements_node_t *statements = parse_statements(parser, context);
+ if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
+
pm_accepts_block_stack_pop(parser);
accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
}
@@ -13529,8 +13618,21 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) {
pm_statements_node_t *else_statements = NULL;
if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
pm_accepts_block_stack_push(parser, true);
- else_statements = parse_statements(parser, def_p ? PM_CONTEXT_RESCUE_ELSE_DEF : PM_CONTEXT_RESCUE_ELSE);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
+ }
+
+ else_statements = parse_statements(parser, context);
pm_accepts_block_stack_pop(parser);
+
accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
}
@@ -13545,8 +13647,21 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) {
pm_statements_node_t *ensure_statements = NULL;
if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
pm_accepts_block_stack_push(parser, true);
- ensure_statements = parse_statements(parser, def_p ? PM_CONTEXT_ENSURE_DEF : PM_CONTEXT_ENSURE);
+ pm_context_t context;
+
+ switch (type) {
+ case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
+ case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
+ case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
+ case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
+ case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
+ case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
+ case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
+ }
+
+ ensure_statements = parse_statements(parser, context);
pm_accepts_block_stack_pop(parser);
+
accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
}
@@ -13562,13 +13677,19 @@ parse_rescues(pm_parser_t *parser, pm_begin_node_t *parent_node, bool def_p) {
}
}
-static inline pm_begin_node_t *
-parse_rescues_as_begin(pm_parser_t *parser, const uint8_t *start, pm_statements_node_t *statements, bool def_p) {
- pm_token_t no_begin_token = not_provided(parser);
- pm_begin_node_t *begin_node = pm_begin_node_create(parser, &no_begin_token, statements);
- parse_rescues(parser, begin_node, def_p);
- begin_node->base.location.start = start;
- return begin_node;
+/**
+ * Parse a set of rescue clauses with an implicit begin (for example when on a
+ * class, module, def, etc.).
+ */
+static pm_begin_node_t *
+parse_rescues_implicit_begin(pm_parser_t *parser, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type) {
+ pm_token_t begin_keyword = not_provided(parser);
+ pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements);
+
+ parse_rescues(parser, node, type);
+ node->base.location.start = start;
+
+ return node;
}
/**
@@ -13721,7 +13842,7 @@ parse_block(pm_parser_t *parser) {
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
- statements = (pm_node_t *) parse_rescues_as_begin(parser, opening.start, (pm_statements_node_t *) statements, false);
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK);
}
}
@@ -13818,6 +13939,160 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
return found;
}
+/**
+ * Check that the block exit (next, break, redo) is allowed in the current
+ * context. If it isn't, add an error to the parser.
+ */
+static void
+parse_block_exit(pm_parser_t *parser, pm_node_t *node, const char *type) {
+ pm_context_node_t *context_node = parser->current_context;
+ bool through_expression = false;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // These are the good cases. We're allowed to have a block exit
+ // in these contexts.
+ return;
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_ELSE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ // These are the bad cases. We're not allowed to have a block
+ // exit in these contexts.
+
+ if (through_expression) {
+ // If we get here, then we're about to mark this block exit
+ // as invalid. However, it could later _become_ valid if we
+ // find a trailing while/until on the expression. In this
+ // case instead of adding the error here, we'll add the
+ // block exit to the list of exits for the expression, and
+ // the node parsing will handle validating it instead.
+ assert(parser->current_block_exits != NULL);
+ pm_node_list_append(parser->current_block_exits, node);
+ } else {
+ // Otherwise, if we haven't gone through an expression
+ // context, then this is just invalid and we'll add the
+ // error here.
+ PM_PARSER_ERR_NODE_FORMAT(parser, node, PM_ERR_INVALID_BLOCK_EXIT, type);
+ }
+
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ // If we got to an expression that could be modified by a
+ // trailing while/until, then we'll track that we have gotten
+ // here because we need to know it if this block exit is later
+ // marked as invalid.
+ through_expression = true;
+ break;
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_PREDICATE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
+ * When we hit an expression that could contain block exits, we need to stash
+ * the previous set and create a new one.
+ */
+static pm_node_list_t *
+push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
+ pm_node_list_t *previous_block_exits = parser->current_block_exits;
+ parser->current_block_exits = current_block_exits;
+ return previous_block_exits;
+}
+
+/**
+ * Pop the current level of block exits from the parser, and add errors to the
+ * parser if any of them are deemed to be invalid.
+ */
+static void
+pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
+ if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
+ // If we matched a trailing while/until, then all of the block exits in
+ // the contained list are valid. In this case we do not need to do
+ // anything.
+ } else if (previous_block_exits != NULL) {
+ // If we did not matching a trailing while/until, then all of the block
+ // exits contained in the list are invalid for this specific context.
+ // However, they could still become valid in a higher level context if
+ // there is another list above this one. In this case we'll push all of
+ // the block exits up to the previous list.
+ pm_node_list_concat(previous_block_exits, parser->current_block_exits);
+ } else {
+ // If we did not match a trailing while/until and this was the last
+ // chance to do so, then all of the block exits in the list are invalid
+ // and we need to add an error for each of them.
+ pm_node_t *block_exit;
+ PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
+ const char *type;
+
+ switch (PM_NODE_TYPE(block_exit)) {
+ case PM_BREAK_NODE: type = "break"; break;
+ case PM_NEXT_NODE: type = "next"; break;
+ case PM_REDO_NODE: type = "redo"; break;
+ default: assert(false && "unreachable"); type = ""; break;
+ }
+
+ PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
+ }
+ }
+
+ parser->current_block_exits = previous_block_exits;
+}
+
static inline pm_node_t *
parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword) {
context_push(parser, PM_CONTEXT_PREDICATE);
@@ -13842,6 +14117,9 @@ parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_contex
static inline pm_node_t *
parse_conditional(pm_parser_t *parser, pm_context_t context) {
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+
pm_token_t keyword = parser->previous;
pm_token_t then_keyword = not_provided(parser);
@@ -13921,7 +14199,8 @@ parse_conditional(pm_parser_t *parser, pm_context_t context) {
break;
}
} else {
- // We should specialize this error message to refer to 'if' or 'unless' explicitly.
+ // We should specialize this error message to refer to 'if' or 'unless'
+ // explicitly.
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM);
}
@@ -13958,6 +14237,9 @@ parse_conditional(pm_parser_t *parser, pm_context_t context) {
break;
}
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return parent;
}
@@ -14597,9 +14879,8 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w
// the whitespace from a node, then we'll drop it from the list entirely.
size_t write_index = 0;
- for (size_t read_index = 0; read_index < nodes->size; read_index++) {
- pm_node_t *node = nodes->nodes[read_index];
-
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(nodes, read_index, node) {
// We're not manipulating child nodes that aren't strings. In this case
// we'll skip past it and indicate that the subsequent node should not
// be dedented.
@@ -15700,6 +15981,173 @@ pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
}
}
+/**
+ * Ensures that the current retry token is valid in the current context.
+ */
+static void
+parse_retry(pm_parser_t *parser, const pm_node_t *node) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ // These are the good cases. We're allowed to have a retry here.
+ return;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_SCLASS:
+ // These are the bad cases. We're not allowed to have a retry in
+ // these contexts.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
+ return;
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_DEF_ELSE:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ // These are also bad cases, but with a more specific error
+ // message indicating the else.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
+ return;
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ // These are also bad cases, but with a more specific error
+ // message indicating the ensure.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_PREDICATE:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
+/**
+ * Ensures that the current yield token is valid in the current context.
+ */
+static void
+parse_yield(pm_parser_t *parser, const pm_node_t *node) {
+ pm_context_node_t *context_node = parser->current_context;
+
+ while (context_node != NULL) {
+ switch (context_node->context) {
+ case PM_CONTEXT_DEF:
+ case PM_CONTEXT_DEF_PARAMS:
+ case PM_CONTEXT_DEFINED:
+ case PM_CONTEXT_DEF_ENSURE:
+ case PM_CONTEXT_DEF_RESCUE:
+ case PM_CONTEXT_DEF_ELSE:
+ // These are the good cases. We're allowed to have a block exit
+ // in these contexts.
+ return;
+ case PM_CONTEXT_CLASS:
+ case PM_CONTEXT_CLASS_ENSURE:
+ case PM_CONTEXT_CLASS_RESCUE:
+ case PM_CONTEXT_CLASS_ELSE:
+ case PM_CONTEXT_MAIN:
+ case PM_CONTEXT_MODULE:
+ case PM_CONTEXT_MODULE_ENSURE:
+ case PM_CONTEXT_MODULE_RESCUE:
+ case PM_CONTEXT_MODULE_ELSE:
+ case PM_CONTEXT_SCLASS:
+ case PM_CONTEXT_SCLASS_RESCUE:
+ case PM_CONTEXT_SCLASS_ENSURE:
+ case PM_CONTEXT_SCLASS_ELSE:
+ // These are the bad cases. We're not allowed to have a retry in
+ // these contexts.
+ pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
+ return;
+ case PM_CONTEXT_NONE:
+ // This case should never happen.
+ assert(false && "unreachable");
+ break;
+ case PM_CONTEXT_BEGIN:
+ case PM_CONTEXT_BEGIN_ELSE:
+ case PM_CONTEXT_BEGIN_ENSURE:
+ case PM_CONTEXT_BEGIN_RESCUE:
+ case PM_CONTEXT_BLOCK_BRACES:
+ case PM_CONTEXT_BLOCK_KEYWORDS:
+ case PM_CONTEXT_BLOCK_ELSE:
+ case PM_CONTEXT_BLOCK_ENSURE:
+ case PM_CONTEXT_BLOCK_RESCUE:
+ case PM_CONTEXT_CASE_IN:
+ case PM_CONTEXT_CASE_WHEN:
+ case PM_CONTEXT_DEFAULT_PARAMS:
+ case PM_CONTEXT_ELSE:
+ case PM_CONTEXT_ELSIF:
+ case PM_CONTEXT_EMBEXPR:
+ case PM_CONTEXT_FOR_INDEX:
+ case PM_CONTEXT_FOR:
+ case PM_CONTEXT_IF:
+ case PM_CONTEXT_LAMBDA_BRACES:
+ case PM_CONTEXT_LAMBDA_DO_END:
+ case PM_CONTEXT_LAMBDA_ELSE:
+ case PM_CONTEXT_LAMBDA_ENSURE:
+ case PM_CONTEXT_LAMBDA_RESCUE:
+ case PM_CONTEXT_PARENS:
+ case PM_CONTEXT_POSTEXE:
+ case PM_CONTEXT_PREDICATE:
+ case PM_CONTEXT_PREEXE:
+ case PM_CONTEXT_RESCUE_MODIFIER:
+ case PM_CONTEXT_TERNARY:
+ case PM_CONTEXT_UNLESS:
+ case PM_CONTEXT_UNTIL:
+ case PM_CONTEXT_WHILE:
+ // In these contexts we should continue walking up the list of
+ // contexts.
+ break;
+ }
+
+ context_node = context_node->prev;
+ }
+}
+
/**
* Parse an expression that begins with the previous node that we just lexed.
*/
@@ -15803,6 +16251,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
case PM_TOKEN_PARENTHESIS_LEFT:
case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
pm_token_t opening = parser->current;
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+
parser_lex(parser);
while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
@@ -15810,6 +16262,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
// we have an empty parentheses node, and we can immediately return.
if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_parentheses_node_create(parser, &opening, NULL, &parser->previous);
}
@@ -15835,9 +16291,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
lex_state_set(parser, PM_LEX_STATE_ENDARG);
}
+
parser_lex(parser);
pm_accepts_block_stack_pop(parser);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
// If we have a single statement and are ending on a right
// parenthesis, then we need to check if this is possibly a
@@ -15925,6 +16385,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_accepts_block_stack_pop(parser);
expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_parentheses_node_create(parser, &opening, (pm_node_t *) statements, &parser->previous);
}
case PM_TOKEN_BRACE_LEFT: {
@@ -16356,6 +16819,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_token_t case_keyword = parser->previous;
pm_node_t *predicate = NULL;
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+
if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
predicate = NULL;
@@ -16369,6 +16835,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
}
if (accept1(parser, PM_TOKEN_KEYWORD_END)) {
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
return (pm_node_t *) pm_case_node_create(parser, &case_keyword, predicate, &parser->previous);
}
@@ -16548,6 +17017,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_case_match_node_end_keyword_loc_set((pm_case_match_node_t *) node, &parser->previous);
}
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return node;
}
case PM_TOKEN_KEYWORD_BEGIN: {
@@ -16555,6 +17027,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_token_t begin_keyword = parser->previous;
accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
+
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
pm_statements_node_t *begin_statements = NULL;
if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
@@ -16565,7 +17040,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
}
pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
- parse_rescues(parser, begin_node, false);
+ parse_rescues(parser, begin_node, PM_RESCUES_BEGIN);
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM);
begin_node->base.location.end = parser->previous.end;
@@ -16575,6 +17050,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_parser_err_node(parser, (pm_node_t *) begin_node->else_clause, PM_ERR_BEGIN_LONELY_ELSE);
}
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) begin_node;
}
case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
@@ -16616,10 +17094,16 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
}
switch (keyword.type) {
- case PM_TOKEN_KEYWORD_BREAK:
- return (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
- case PM_TOKEN_KEYWORD_NEXT:
- return (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
+ case PM_TOKEN_KEYWORD_BREAK: {
+ pm_node_t *node = (pm_node_t *) pm_break_node_create(parser, &keyword, arguments.arguments);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "break");
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_NEXT: {
+ pm_node_t *node = (pm_node_t *) pm_next_node_create(parser, &keyword, arguments.arguments);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "next");
+ return node;
+ }
case PM_TOKEN_KEYWORD_RETURN: {
if (
(parser->current_context->context == PM_CONTEXT_CLASS) ||
@@ -16658,7 +17142,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_arguments_t arguments = { 0 };
parse_arguments_list(parser, &arguments, false, accepts_command_call);
- return (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
+ pm_node_t *node = (pm_node_t *) pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
+ if (!parser->parsing_eval) parse_yield(parser, node);
+
+ return node;
}
case PM_TOKEN_KEYWORD_CLASS: {
parser_lex(parser);
@@ -16682,7 +17169,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
- statements = (pm_node_t *) parse_rescues_as_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, false);
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS);
}
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
@@ -16695,6 +17182,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
return (pm_node_t *) pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous);
}
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+
pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_CLASS_NAME);
pm_token_t name = parser->previous;
if (name.type != PM_TOKEN_CONSTANT) {
@@ -16735,7 +17225,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
- statements = (pm_node_t *) parse_rescues_as_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, false);
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS);
}
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM);
@@ -16754,6 +17244,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
}
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous);
}
case PM_TOKEN_KEYWORD_DEF: {
@@ -16972,10 +17465,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, binding_power < PM_BINDING_POWER_COMPOSITION, PM_ERR_DEF_ENDLESS);
if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
pm_token_t rescue_keyword = parser->previous;
pm_node_t *value = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE);
- pm_rescue_modifier_node_t *rescue_node = pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value);
- statement = (pm_node_t *)rescue_node;
+ context_pop(parser);
+
+ statement = (pm_node_t *) pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value);
}
pm_statements_node_body_append((pm_statements_node_t *) statements, statement);
@@ -17004,7 +17500,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
- statements = (pm_node_t *) parse_rescues_as_begin(parser, def_keyword.start, (pm_statements_node_t *) statements, true);
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF);
}
pm_accepts_block_stack_pop(parser);
@@ -17048,6 +17544,7 @@ 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);
if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
lparen = parser->previous;
@@ -17066,6 +17563,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_DEFINED_EXPRESSION);
}
+ context_pop(parser);
return (pm_node_t *) pm_defined_node_create(
parser,
&lparen,
@@ -17223,15 +17721,21 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
parser_lex(parser);
return parse_conditional(parser, PM_CONTEXT_UNLESS);
case PM_TOKEN_KEYWORD_MODULE: {
- parser_lex(parser);
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+ parser_lex(parser);
pm_token_t module_keyword = parser->previous;
+
pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, PM_ERR_MODULE_NAME);
pm_token_t name;
// If we can recover from a syntax error that occurred while parsing
// the name of the module, then we'll handle that here.
if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) {
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
return (pm_node_t *) pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing);
}
@@ -17267,7 +17771,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
- statements = (pm_node_t *) parse_rescues_as_begin(parser, module_keyword.start, (pm_statements_node_t *) statements, false);
+ statements = (pm_node_t *) parse_rescues_implicit_begin(parser, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE);
}
pm_constant_id_list_t locals = parser->current_scope->locals;
@@ -17280,17 +17784,30 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
}
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous);
}
case PM_TOKEN_KEYWORD_NIL:
parser_lex(parser);
return (pm_node_t *) pm_nil_node_create(parser, &parser->previous);
- case PM_TOKEN_KEYWORD_REDO:
+ case PM_TOKEN_KEYWORD_REDO: {
parser_lex(parser);
- return (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
- case PM_TOKEN_KEYWORD_RETRY:
+
+ pm_node_t *node = (pm_node_t *) pm_redo_node_create(parser, &parser->previous);
+ if (!parser->parsing_eval) parse_block_exit(parser, node, "redo");
+
+ return node;
+ }
+ case PM_TOKEN_KEYWORD_RETRY: {
parser_lex(parser);
- return (pm_node_t *) pm_retry_node_create(parser, &parser->previous);
+
+ pm_node_t *node = (pm_node_t *) pm_retry_node_create(parser, &parser->previous);
+ parse_retry(parser, node);
+
+ return node;
+ }
case PM_TOKEN_KEYWORD_SELF:
parser_lex(parser);
return (pm_node_t *) pm_self_node_create(parser, &parser->previous);
@@ -18018,7 +18535,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
- body = (pm_node_t *) parse_rescues_as_begin(parser, opening.start, (pm_statements_node_t *) body, false);
+ body = (pm_node_t *) parse_rescues_implicit_begin(parser, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA);
}
expect1(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END);
@@ -18087,9 +18604,13 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_
// Contradicting binding powers, the right-hand-side value of rthe assignment allows the `rescue` modifier.
if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
pm_token_t rescue = parser->current;
parser_lex(parser);
+
pm_node_t *right = parse_expression(parser, binding_power, false, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right);
}
@@ -18121,6 +18642,8 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
// Contradicting binding powers, the right-hand-side value of the assignment
// allows the `rescue` modifier.
if ((single_value || (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1))) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
+
pm_token_t rescue = parser->current;
parser_lex(parser);
@@ -18136,6 +18659,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
}
pm_node_t *right = parse_expression(parser, binding_power, accepts_command_call_inner, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
return (pm_node_t *) pm_rescue_modifier_node_create(parser, value, &rescue, right);
}
@@ -18716,9 +19240,8 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
bool interpolated = false;
size_t total_length = 0;
- for (size_t index = 0; index < parts->size; index++) {
- pm_node_t *part = parts->nodes[index];
-
+ pm_node_t *part;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
} else {
@@ -18732,8 +19255,8 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
if (!memory) abort();
uint8_t *cursor = memory;
- for (size_t index = 0; index < parts->size; index++) {
- pm_string_t *unescaped = &((pm_string_node_t *) parts->nodes[index])->unescaped;
+ PM_NODE_LIST_FOREACH(parts, index, part) {
+ pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
size_t length = pm_string_length(unescaped);
memcpy(cursor, pm_string_source(unescaped), length);
@@ -18878,8 +19401,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
return (pm_node_t *) pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0);
}
case PM_TOKEN_QUESTION_MARK: {
+ context_push(parser, PM_CONTEXT_TERNARY);
+ pm_node_list_t current_block_exits = { 0 };
+ pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
+
pm_token_t qmark = parser->current;
parser_lex(parser);
+
pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_TRUE);
if (parser->recovering) {
@@ -18892,6 +19420,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
pm_node_t *false_expression = (pm_node_t *) pm_missing_node_create(parser, colon.start, colon.end);
+ context_pop(parser);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
}
@@ -18901,6 +19433,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
pm_token_t colon = parser->previous;
pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_TERNARY_EXPRESSION_FALSE);
+ context_pop(parser);
+ pop_block_exits(parser, previous_block_exits);
+ pm_node_list_free(¤t_block_exits);
+
return (pm_node_t *) pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
}
case PM_TOKEN_COLON_COLON: {
@@ -18976,9 +19512,12 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
}
}
case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
+ context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
parser_lex(parser);
accept1(parser, PM_TOKEN_NEWLINE);
+
pm_node_t *value = parse_expression(parser, binding_power, true, PM_ERR_RESCUE_MODIFIER_VALUE);
+ context_pop(parser);
return (pm_node_t *) pm_rescue_modifier_node_create(parser, node, &token, value);
}
@@ -19375,12 +19914,14 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
.start_line = 1,
.explicit_encoding = NULL,
.command_line = 0,
+ .parsing_eval = false,
.command_start = true,
.recovering = false,
.encoding_changed = false,
.pattern_matching_newlines = false,
.in_keyword_arg = false,
.current_param_name = 0,
+ .current_block_exits = NULL,
.semantic_token_seen = false,
.frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
.current_regular_expression_ascii_only = false
@@ -19434,6 +19975,8 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
parser->version = options->version;
// scopes option
+ parser->parsing_eval = options->scopes_count > 0;
+
for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index);
pm_parser_scope_push(parser, scope_index == 0);
@@ -20105,10 +20648,16 @@ pm_parser_errors_format(const pm_parser_t *parser, const pm_list_t *error_list,
while (column < error->column_end) {
if (column < error->column_start) {
pm_buffer_append_byte(buffer, ' ');
- } else if (colorize) {
- pm_buffer_append_string(buffer, PM_COLOR_RED "^" PM_COLOR_RESET, 11);
} else {
- pm_buffer_append_byte(buffer, '^');
+ const uint8_t caret = column == error->column_start ? '^' : '~';
+
+ if (colorize) {
+ pm_buffer_append_string(buffer, PM_COLOR_RED, 7);
+ pm_buffer_append_byte(buffer, caret);
+ pm_buffer_append_string(buffer, PM_COLOR_RESET, 3);
+ } else {
+ pm_buffer_append_byte(buffer, caret);
+ }
}
size_t char_width = encoding->char_width(start + column, parser->end - (start + column));
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
index 3d5073147a9ffb..cedd340e086e76 100644
--- a/prism/templates/src/diagnostic.c.erb
+++ b/prism/templates/src/diagnostic.c.erb
@@ -206,6 +206,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3_0] = { "`%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "'%.*s' is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INSTANCE_VARIABLE_BARE] = { "'@' without identifiers is not allowed as an instance variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_BLOCK_EXIT] = { "Invalid %s", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_SYNTAX },
@@ -218,8 +219,12 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_INVALID_MULTIBYTE_ESCAPE] = { "invalid multibyte escape: /%.*s/", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_SYNTAX }, // TODO WHAT?
+ [PM_ERR_INVALID_RETRY_AFTER_ELSE] = { "Invalid retry after else", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_RETRY_AFTER_ENSURE] = { "Invalid retry after ensure", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_RETRY_WITHOUT_RESCUE] = { "Invalid retry without rescue", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_VARIABLE_GLOBAL_3_3_0] = { "`%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_INVALID_VARIABLE_GLOBAL] = { "'%.*s' is not allowed as a global variable name", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_INVALID_YIELD] = { "Invalid yield", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_IT_NOT_ALLOWED_NUMBERED] = { "`it` is not allowed when an numbered parameter is defined", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_IT_NOT_ALLOWED_ORDINARY] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_SYNTAX },
@@ -251,7 +256,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_OPERATOR_WRITE_BLOCK] = { "unexpected operator after a call with a block", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = { "unexpected multiple `**` splat parameters", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_PARAMETER_BLOCK_MULTI] = { "multiple block parameters; only one block is allowed", PM_ERROR_LEVEL_SYNTAX },
- [PM_ERR_PARAMETER_CIRCULAR] = { "parameter default value references itself", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_PARAMETER_CIRCULAR] = { "circular argument reference - %.*s", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_PARAMETER_METHOD_NAME] = { "unexpected name for a parameter", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_PARAMETER_NAME_DUPLICATED] = { "duplicated argument name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_PARAMETER_NO_DEFAULT] = { "expected a default value for the parameter", PM_ERROR_LEVEL_SYNTAX },
@@ -294,7 +299,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_RESCUE_MODIFIER_VALUE] = { "expected a value after the `rescue` modifier", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_RESCUE_TERM] = { "expected a closing delimiter for the `rescue` clause", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_RESCUE_VARIABLE] = { "expected an exception variable after `=>` in a rescue statement", PM_ERROR_LEVEL_SYNTAX },
- [PM_ERR_RETURN_INVALID] = { "invalid `return` in a class or module body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_RETURN_INVALID] = { "Invalid return in class/module body", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_SINGLETON_FOR_LITERALS] = { "cannot define singleton method for literals", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_STATEMENT_ALIAS] = { "unexpected an `alias` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_STATEMENT_POSTEXE_END] = { "unexpected an `END` at a non-statement position", PM_ERROR_LEVEL_SYNTAX },
diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb
index 99a0c92fa94eef..e1c35f5a456f04 100644
--- a/prism/templates/src/node.c.erb
+++ b/prism/templates/src/node.c.erb
@@ -9,11 +9,9 @@ pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize);
*/
static size_t
pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) {
- size_t size = sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *));
- for (size_t index = 0; index < node_list->size; index++) {
- pm_node_memsize_node(node_list->nodes[index], memsize);
- }
- return size;
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(node_list, index, node) pm_node_memsize_node(node, memsize);
+ return sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *));
}
/**
@@ -22,13 +20,36 @@ pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) {
* the list to be twice as large as it was before. If the reallocation fails,
* this function returns false, otherwise it returns true.
*/
-bool
-pm_node_list_grow(pm_node_list_t *list) {
- if (list->size == list->capacity) {
- list->capacity = list->capacity == 0 ? 4 : list->capacity * 2;
- list->nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * list->capacity);
- return list->nodes != NULL;
+static bool
+pm_node_list_grow(pm_node_list_t *list, size_t size) {
+ size_t requested_size = list->size + size;
+
+ // If the requested size caused overflow, return false.
+ if (requested_size < list->size) return false;
+
+ // If the requested size is within the existing capacity, return true.
+ if (requested_size < list->capacity) return true;
+
+ // Otherwise, reallocate the list to be twice as large as it was before.
+ size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2;
+
+ // If multiplying by 2 caused overflow, return false.
+ if (next_capacity < list->capacity) return false;
+
+ // If we didn't get enough by doubling, keep doubling until we do.
+ while (requested_size > next_capacity) {
+ size_t double_capacity = next_capacity * 2;
+
+ // Ensure we didn't overflow by multiplying by 2.
+ if (double_capacity < next_capacity) return false;
+ next_capacity = double_capacity;
}
+
+ pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity);
+ if (nodes == NULL) return false;
+
+ list->nodes = nodes;
+ list->capacity = next_capacity;
return true;
}
@@ -37,7 +58,7 @@ pm_node_list_grow(pm_node_list_t *list) {
*/
void
pm_node_list_append(pm_node_list_t *list, pm_node_t *node) {
- if (pm_node_list_grow(list)) {
+ if (pm_node_list_grow(list, 1)) {
list->nodes[list->size++] = node;
}
}
@@ -47,13 +68,24 @@ pm_node_list_append(pm_node_list_t *list, pm_node_t *node) {
*/
void
pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) {
- if (pm_node_list_grow(list)) {
+ if (pm_node_list_grow(list, 1)) {
memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *));
list->nodes[0] = node;
list->size++;
}
}
+/**
+ * Concatenate the given node list onto the end of the other node list.
+ */
+void
+pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) {
+ if (other->size > 0 && pm_node_list_grow(list, other->size)) {
+ memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *));
+ list->size += other->size;
+ }
+}
+
/**
* Free the internal memory associated with the given node list.
*/
@@ -73,10 +105,8 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node);
*/
static void
pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) {
- for (size_t index = 0; index < list->size; index++) {
- pm_node_destroy(parser, list->nodes[index]);
- }
-
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node);
pm_node_list_free(list);
}
diff --git a/prism_compile.c b/prism_compile.c
index 5aeef191f780a7..6d049b68ae1ab4 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -4276,7 +4276,7 @@ pm_compile_constant_path(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *co
ADD_INSN1(body, &dummy_line_node, getconstant, name);
}
else {
- pm_compile_constant_path(iseq, cast->parent, prefix, body, popped, scope_node);
+ pm_compile_constant_path(iseq, cast->parent, prefix, body, false, scope_node);
ADD_INSN1(body, &dummy_line_node, putobject, Qfalse);
ADD_INSN1(body, &dummy_line_node, getconstant, name);
}
diff --git a/ruby.c b/ruby.c
index 59cbbd0360b690..b7f0f5dedff9fb 100644
--- a/ruby.c
+++ b/ruby.c
@@ -2581,7 +2581,7 @@ struct load_file_arg {
VALUE f;
};
-VALUE rb_script_lines_for(VALUE path, bool add);
+VALUE rb_script_lines_for(VALUE path);
static VALUE
load_file_internal(VALUE argp_v)
@@ -2686,7 +2686,7 @@ load_file_internal(VALUE argp_v)
rb_parser_set_options(parser, opt->do_print, opt->do_loop,
opt->do_line, opt->do_split);
- VALUE lines = rb_script_lines_for(orig_fname, true);
+ VALUE lines = rb_script_lines_for(orig_fname);
if (!NIL_P(lines)) {
rb_parser_set_script_lines(parser, lines);
}
diff --git a/ruby_parser.c b/ruby_parser.c
index b674474f8c319b..f9f5efd110e5cb 100644
--- a/ruby_parser.c
+++ b/ruby_parser.c
@@ -573,8 +573,6 @@ static const rb_parser_config_t rb_global_parser_config = {
.enc_isascii = enc_isascii,
.enc_mbc_to_codepoint = enc_mbc_to_codepoint,
- .ractor_make_shareable = rb_ractor_make_shareable,
-
.local_defined = local_defined,
.dvar_defined = dvar_defined,
@@ -996,52 +994,14 @@ rb_node_encoding_val(const NODE *node)
}
VALUE
-rb_node_const_decl_val(const NODE *node)
-{
- VALUE path;
- switch (nd_type(node)) {
- case NODE_CDECL:
- if (RNODE_CDECL(node)->nd_vid) {
- path = rb_id2str(RNODE_CDECL(node)->nd_vid);
- goto end;
- }
- else {
- node = RNODE_CDECL(node)->nd_else;
- }
- break;
- case NODE_COLON2:
- break;
- case NODE_COLON3:
- // ::Const
- path = rb_str_new_cstr("::");
- rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
- goto end;
- default:
- rb_bug("unexpected node: %s", ruby_node_name(nd_type(node)));
- UNREACHABLE_RETURN(0);
- }
-
- path = rb_ary_new();
- if (node) {
- for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) {
- rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid));
- }
- if (node && nd_type_p(node, NODE_CONST)) {
- // Const::Name
- rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid));
- }
- else if (node && nd_type_p(node, NODE_COLON3)) {
- // ::Const::Name
- rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid));
- rb_ary_push(path, rb_str_new(0, 0));
- }
- else {
- // expression::Name
- rb_ary_push(path, rb_str_new_cstr("..."));
- }
- path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
- }
- end:
- path = rb_fstring(path);
- return path;
+rb_script_lines_for(VALUE path)
+{
+ VALUE hash, lines;
+ ID script_lines;
+ CONST_ID(script_lines, "SCRIPT_LINES__");
+ if (!rb_const_defined_at(rb_cObject, script_lines)) return Qnil;
+ hash = rb_const_get_at(rb_cObject, script_lines);
+ if (!RB_TYPE_P(hash, T_HASH)) return Qnil;
+ rb_hash_aset(hash, path, lines = rb_ary_new());
+ return lines;
}
diff --git a/rubyparser.h b/rubyparser.h
index 138de47f0dd48b..7965291fe51e20 100644
--- a/rubyparser.h
+++ b/rubyparser.h
@@ -66,6 +66,13 @@ typedef struct rb_parser_string {
char *ptr;
} rb_parser_string_t;
+enum rb_parser_shareability {
+ rb_parser_shareable_none,
+ rb_parser_shareable_literal,
+ rb_parser_shareable_copy,
+ rb_parser_shareable_everything,
+};
+
/*
* AST Node
*/
@@ -129,7 +136,6 @@ enum node_type {
NODE_MATCH,
NODE_MATCH2,
NODE_MATCH3,
- NODE_LIT,
NODE_INTEGER,
NODE_FLOAT,
NODE_RATIONAL,
@@ -444,6 +450,7 @@ typedef struct RNode_CDECL {
ID nd_vid;
struct RNode *nd_value;
struct RNode *nd_else;
+ enum rb_parser_shareability shareability;
} rb_node_cdecl_t;
typedef struct RNode_CVASGN {
@@ -492,6 +499,7 @@ typedef struct RNode_OP_CDECL {
struct RNode *nd_head;
struct RNode *nd_value;
ID nd_aid;
+ enum rb_parser_shareability shareability;
} rb_node_op_cdecl_t;
typedef struct RNode_CALL {
@@ -666,12 +674,6 @@ typedef struct RNode_MATCH3 {
struct RNode *nd_value;
} rb_node_match3_t;
-typedef struct RNode_LIT {
- NODE node;
-
- VALUE nd_lit;
-} rb_node_lit_t;
-
typedef struct RNode_INTEGER {
NODE node;
@@ -1122,7 +1124,6 @@ typedef struct RNode_ERROR {
#define RNODE_MATCH(node) ((struct RNode_MATCH *)(node))
#define RNODE_MATCH2(node) ((struct RNode_MATCH2 *)(node))
#define RNODE_MATCH3(node) ((struct RNode_MATCH3 *)(node))
-#define RNODE_LIT(node) ((struct RNode_LIT *)(node))
#define RNODE_INTEGER(node) ((struct RNode_INTEGER *)(node))
#define RNODE_FLOAT(node) ((struct RNode_FLOAT *)(node))
#define RNODE_RATIONAL(node) ((struct RNode_RATIONAL *)(node))
@@ -1370,9 +1371,6 @@ typedef struct rb_parser_config_struct {
bool (*enc_isascii)(OnigCodePoint c, rb_encoding *enc);
OnigCodePoint (*enc_mbc_to_codepoint)(const char *p, const char *e, rb_encoding *enc);
- /* Ractor */
- VALUE (*ractor_make_shareable)(VALUE obj);
-
/* Compile */
// int rb_local_defined(ID id, const rb_iseq_t *iseq);
int (*local_defined)(ID, const void*);
diff --git a/signal.c b/signal.c
index 636574ba07f3aa..1c8f8c112b73ba 100644
--- a/signal.c
+++ b/signal.c
@@ -867,16 +867,19 @@ check_stack_overflow(int sig, const void *addr)
}
}
# endif
+
# ifdef _WIN32
# define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, 0)
# else
# define FAULT_ADDRESS info->si_addr
# ifdef USE_UCONTEXT_REG
-# define CHECK_STACK_OVERFLOW() (info->si_pid ? (void)0 : check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx))
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx)
# else
-# define CHECK_STACK_OVERFLOW() (info->si_pid ? (void)0 : check_stack_overflow(sig, FAULT_ADDRESS))
+# define CHECK_STACK_OVERFLOW_() check_stack_overflow(sig, FAULT_ADDRESS)
# endif
# define MESSAGE_FAULT_ADDRESS " at %p", FAULT_ADDRESS
+# define SIGNAL_FROM_USER_P() ((info)->si_code == SI_USER)
+# define CHECK_STACK_OVERFLOW() (SIGNAL_FROM_USER_P() ? (void)0 : CHECK_STACK_OVERFLOW_())
# endif
#else
# define CHECK_STACK_OVERFLOW() (void)0
@@ -1545,21 +1548,3 @@ Init_signal(void)
rb_enable_interrupt();
}
-
-#if defined(HAVE_GRANTPT)
-extern int grantpt(int);
-#else
-static int
-fake_grantfd(int masterfd)
-{
- errno = ENOSYS;
- return -1;
-}
-#define grantpt(fd) fake_grantfd(fd)
-#endif
-
-int
-rb_grantpt(int masterfd)
-{
- return grantpt(masterfd);
-}
diff --git a/spec/ruby/language/if_spec.rb b/spec/ruby/language/if_spec.rb
index 70c847d830d0a6..2d1a89f081f60a 100644
--- a/spec/ruby/language/if_spec.rb
+++ b/spec/ruby/language/if_spec.rb
@@ -309,6 +309,7 @@
it "warns when Integer literals are used instead of predicates" do
-> {
eval <<~RUBY
+ $. = 0
10.times { |i| ScratchPad << i if 4..5 }
RUBY
}.should complain(/warning: integer literal in flip-flop/, verbose: true)
diff --git a/test/.excludes-prism/TestClass.rb b/test/.excludes-prism/TestClass.rb
deleted file mode 100644
index 9681f78f4233bb..00000000000000
--- a/test/.excludes-prism/TestClass.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-exclude(:test_invalid_break_from_class_definition, "https://github.com/ruby/prism/issues/1604")
-exclude(:test_invalid_next_from_class_definition, "https://github.com/ruby/prism/issues/1604")
-exclude(:test_invalid_return_from_class_definition, "https://github.com/ruby/prism/issues/1604")
diff --git a/test/.excludes-prism/TestISeq.rb b/test/.excludes-prism/TestISeq.rb
index a40c0e49a12417..4f7655358da65a 100644
--- a/test/.excludes-prism/TestISeq.rb
+++ b/test/.excludes-prism/TestISeq.rb
@@ -4,4 +4,3 @@
exclude(:test_to_binary_class_tracepoint, "https://github.com/ruby/prism/issues/2662")
exclude(:test_to_binary_end_tracepoint, "https://github.com/ruby/prism/issues/2663")
exclude(:test_trace_points, "https://github.com/ruby/prism/issues/2660")
-exclude(:test_unreachable_syntax_error, "https://github.com/ruby/prism/issues/1604")
diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb
index 9c60ab15cf6ecc..490a18d035e191 100644
--- a/test/.excludes-prism/TestParse.rb
+++ b/test/.excludes-prism/TestParse.rb
@@ -8,9 +8,7 @@
exclude(:test_global_variable, "unknown")
exclude(:test_here_document, "unknown")
exclude(:test_heredoc_unterminated_interpolation, "unknown")
-exclude(:test_invalid_break_from_class_definition, "unknown")
exclude(:test_invalid_char, "unknown")
-exclude(:test_invalid_next_from_class_definition, "unknown")
exclude(:test_location_of_invalid_token, "unknown")
exclude(:test_no_blockarg, "unknown")
exclude(:test_op_asgn1_with_block, "unknown")
diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb
index f2f13e3161c674..7d324bca204f4c 100644
--- a/test/.excludes-prism/TestSyntax.rb
+++ b/test/.excludes-prism/TestSyntax.rb
@@ -13,10 +13,8 @@
exclude(:test_error_message_encoding, "unknown")
exclude(:test_heredoc_cr, "unknown")
exclude(:test_heredoc_no_terminator, "unknown")
-exclude(:test_invalid_break, "unknown")
exclude(:test_invalid_encoding_symbol, "unknown")
exclude(:test_invalid_literal_message, "unknown")
-exclude(:test_invalid_next, "unknown")
exclude(:test_it, "https://github.com/ruby/prism/issues/2323")
exclude(:test_keyword_invalid_name, "unknown")
exclude(:test_keyword_self_reference, "unknown")
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index dbc4a8924d8565..1d9bc0f986e2fc 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -257,6 +257,7 @@ def test_next_1_2_3
["unexpected ',', expecting end-of-input", 6..7],
["unexpected ',', ignoring it", 6..7],
["expected a matching `)`", 6..6],
+ ["Invalid next", 0..12],
["unexpected ')', expecting end-of-input", 12..13],
["unexpected ')', ignoring it", 12..13]
]
@@ -264,7 +265,8 @@ def test_next_1_2_3
def test_next_1
assert_errors expression("next 1,;"), "next 1,;", [
- ["expected an argument", 6..7]
+ ["expected an argument", 6..7],
+ ["Invalid next", 0..7]
]
end
@@ -273,6 +275,7 @@ def test_break_1_2_3
["unexpected ',', expecting end-of-input", 7..8],
["unexpected ',', ignoring it", 7..8],
["expected a matching `)`", 7..7],
+ ["Invalid break", 0..13],
["unexpected ')', expecting end-of-input", 13..14],
["unexpected ')', ignoring it", 13..14]
]
@@ -280,7 +283,8 @@ def test_break_1_2_3
def test_break_1
assert_errors expression("break 1,;"), "break 1,;", [
- ["expected an argument", 7..8]
+ ["expected an argument", 7..8],
+ ["Invalid break", 0..8]
]
end
@@ -1091,7 +1095,7 @@ def test_dont_allow_return_inside_class_body
)
assert_errors expected, "class A; return; end", [
- ["invalid `return` in a class or module body", 9..15]
+ ["Invalid return in class/module body", 9..15]
]
end
@@ -1106,7 +1110,7 @@ def test_dont_allow_return_inside_module_body
)
assert_errors expected, "module A; return; end", [
- ["invalid `return` in a class or module body", 10..16]
+ ["Invalid return in class/module body", 10..16]
]
end
@@ -1570,12 +1574,13 @@ def test_check_value_expression
1 => ^(unless 1; (return) else (return) end)
RUBY
- message = 'unexpected void value expression'
+ message = "unexpected void value expression"
assert_errors expression(source), source, [
[message, 7..13],
[message, 35..40],
[message, 51..55],
[message, 66..70],
+ ["Invalid retry without rescue", 81..86],
[message, 81..86],
[message, 97..103],
[message, 123..129],
@@ -1943,12 +1948,11 @@ def foo(bar: bar) = 42
proc { |foo: foo| }
RUBY
- message = 'parameter default value references itself'
assert_errors expression(source), source, [
- [message, 14..17],
- [message, 37..40],
- [message, 61..64],
- [message, 81..84],
+ ["circular argument reference - bar", 14..17],
+ ["circular argument reference - bar", 37..40],
+ ["circular argument reference - foo", 61..64],
+ ["circular argument reference - foo", 81..84],
]
end
@@ -2192,32 +2196,6 @@ def test_duplicate_pattern_hash_key
private
- if RUBY_ENGINE == "ruby"
- def check_syntax(source)
- $VERBOSE, previous = nil, $VERBOSE
-
- begin
- RubyVM::InstructionSequence.compile(source)
- ensure
- $VERBOSE = previous
- end
- end
-
- def assert_valid_syntax(source)
- check_syntax(source)
- end
-
- def refute_valid_syntax(source)
- assert_raise(SyntaxError) { check_syntax(source) }
- end
- else
- def assert_valid_syntax(source)
- end
-
- def refute_valid_syntax(source)
- end
- end
-
def assert_errors(expected, source, errors, check_valid_syntax: true)
refute_valid_syntax(source) if check_valid_syntax
diff --git a/test/prism/fixtures/break.txt b/test/prism/fixtures/break.txt
index 61859d8a8a3f27..82fa45bdb4fb56 100644
--- a/test/prism/fixtures/break.txt
+++ b/test/prism/fixtures/break.txt
@@ -1,24 +1,24 @@
-break
+tap { break }
-break (1), (2), (3)
+tap { break (1), (2), (3) }
-break 1
+tap { break 1 }
-break 1, 2,
-3
+tap { break 1, 2,
+3 }
-break 1, 2, 3
+tap { break 1, 2, 3 }
-break [1, 2, 3]
+tap { break [1, 2, 3] }
-break(
+tap { break(
1
2
-)
+) }
-break()
+tap { break() }
-break(1)
+tap { break(1) }
foo { break 42 } == 42
diff --git a/test/prism/fixtures/if.txt b/test/prism/fixtures/if.txt
index c5281a66930d1a..cede644a4c2149 100644
--- a/test/prism/fixtures/if.txt
+++ b/test/prism/fixtures/if.txt
@@ -7,13 +7,13 @@ if true then true elsif false then false elsif nil then nil else self end
1 if true
-break if true
+tap { break if true }
-next if true
+tap { next if true }
return if true
-if exit_loop then break 42 end
+tap { if exit_loop then break 42 end }
if foo
then bar
diff --git a/test/prism/fixtures/keywords.txt b/test/prism/fixtures/keywords.txt
index 90cdfb41c7214f..6d24a9f47656c9 100644
--- a/test/prism/fixtures/keywords.txt
+++ b/test/prism/fixtures/keywords.txt
@@ -1,6 +1,6 @@
-redo
+tap { redo }
-retry
+begin; rescue; retry; end
self
diff --git a/test/prism/fixtures/next.txt b/test/prism/fixtures/next.txt
index 476f17ffe3e3a8..2ef14c6304e929 100644
--- a/test/prism/fixtures/next.txt
+++ b/test/prism/fixtures/next.txt
@@ -1,24 +1,24 @@
-next
+tap { next }
-next (1), (2), (3)
+tap { next (1), (2), (3) }
-next 1
+tap { next 1 }
-next 1, 2,
-3
+tap { next 1, 2,
+3 }
-next 1, 2, 3
+tap { next 1, 2, 3 }
-next [1, 2, 3]
+tap { next [1, 2, 3] }
-next(
+tap { next(
1
2
-)
+) }
-next
-1
+tap { next
+1 }
-next()
+tap { next() }
-next(1)
+tap { next(1) }
diff --git a/test/prism/fixtures/rescue.txt b/test/prism/fixtures/rescue.txt
index 7cce866379a212..99170fbe0ff311 100644
--- a/test/prism/fixtures/rescue.txt
+++ b/test/prism/fixtures/rescue.txt
@@ -3,9 +3,9 @@ foo rescue nil
foo rescue
nil
-break rescue nil
+tap { break rescue nil }
-next rescue nil
+tap { next rescue nil }
return rescue nil
diff --git a/test/prism/fixtures/seattlerb/README.rdoc b/test/prism/fixtures/seattlerb/README.rdoc
index 649e4e4c2faa3b..1e5bfbdfe01682 100644
--- a/test/prism/fixtures/seattlerb/README.rdoc
+++ b/test/prism/fixtures/seattlerb/README.rdoc
@@ -65,15 +65,15 @@ You can also use Ruby19Parser, Ruby18Parser, or RubyParser.for_current_ruby:
To add a new version:
-* New parser should be generated from lib/ruby[3]_parser.yy.
-* Extend lib/ruby[3]_parser.yy with new class name.
+* New parser should be generated from lib/ruby_parser[23].yy.
+* Extend lib/ruby_parser[23].yy with new class name.
* Add new version number to V2/V3 in Rakefile for rule creation.
* Add new `ruby_parse "x.y.z"` line to Rakefile for rake compare (line ~300).
* Require generated parser in lib/ruby_parser.rb.
* Add new V## = ::Ruby##Parser; end to ruby_parser.rb (bottom of file).
* Add empty TestRubyParserShared##Plus module and TestRubyParserV## to test/test_ruby_parser.rb.
* Extend Manifest.txt with generated file names.
-* Add new version number to sexp_processor's pt_testcase.rb in all_versions
+* Add new version number to sexp_processor's pt_testcase.rb in all_versions.
Until all of these are done, you won't have a clean test run.
diff --git a/test/prism/fixtures/seattlerb/block_break.txt b/test/prism/fixtures/seattlerb/block_break.txt
deleted file mode 100644
index 35eabee187b7c2..00000000000000
--- a/test/prism/fixtures/seattlerb/block_break.txt
+++ /dev/null
@@ -1 +0,0 @@
-break foo arg do |bar| end
diff --git a/test/prism/fixtures/seattlerb/block_next.txt b/test/prism/fixtures/seattlerb/block_next.txt
deleted file mode 100644
index 760fcbf809825d..00000000000000
--- a/test/prism/fixtures/seattlerb/block_next.txt
+++ /dev/null
@@ -1 +0,0 @@
-next foo arg do |bar| end
diff --git a/test/prism/fixtures/seattlerb/dasgn_icky2.txt b/test/prism/fixtures/seattlerb/dasgn_icky2.txt
deleted file mode 100644
index 2f50d323041e65..00000000000000
--- a/test/prism/fixtures/seattlerb/dasgn_icky2.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-a do
- v = nil
- begin
- yield
- rescue Exception => v
- break
- end
-end
diff --git a/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt b/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
index 4420560d2bee83..d88e1fd21cf51e 100644
--- a/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
+++ b/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
@@ -1,2 +1,2 @@
-%q{ \
+%Q{ \
}
diff --git a/test/prism/fixtures/seattlerb/yield_arg.txt b/test/prism/fixtures/seattlerb/yield_arg.txt
deleted file mode 100644
index 7e752f62e2520e..00000000000000
--- a/test/prism/fixtures/seattlerb/yield_arg.txt
+++ /dev/null
@@ -1 +0,0 @@
-yield 42
diff --git a/test/prism/fixtures/seattlerb/yield_call_assocs.txt b/test/prism/fixtures/seattlerb/yield_call_assocs.txt
deleted file mode 100644
index 95963dfcf9b242..00000000000000
--- a/test/prism/fixtures/seattlerb/yield_call_assocs.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-yield 1, :z => 1
-
-yield 1, :z => 1, :w => 2
-
-yield y :z=>1
-
-yield y z:1
-
-yield y(z:1)
-
-yield y(z=>1)
diff --git a/test/prism/fixtures/seattlerb/yield_empty_parens.txt b/test/prism/fixtures/seattlerb/yield_empty_parens.txt
deleted file mode 100644
index cff86b243ad644..00000000000000
--- a/test/prism/fixtures/seattlerb/yield_empty_parens.txt
+++ /dev/null
@@ -1 +0,0 @@
-yield()
diff --git a/test/prism/fixtures/unless.txt b/test/prism/fixtures/unless.txt
index 410d460e6905a1..678d58991b3d92 100644
--- a/test/prism/fixtures/unless.txt
+++ b/test/prism/fixtures/unless.txt
@@ -5,9 +5,9 @@ unless true
1 unless true
-break unless true
+tap { break unless true }
-next unless true
+tap { next unless true }
return unless true
diff --git a/test/prism/fixtures/unparser/corpus/literal/control.txt b/test/prism/fixtures/unparser/corpus/literal/control.txt
deleted file mode 100644
index 648782bc9629bc..00000000000000
--- a/test/prism/fixtures/unparser/corpus/literal/control.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-next
-return
-break
-retry
-redo
-return 1
-return 1, 2
-return true ? 1 : 2
-break true ? 1 : 2
-next true ? 1 : 2
-return true, if true
- 1
-else
- 2
-end
diff --git a/test/prism/fixtures/unparser/corpus/literal/flipflop.txt b/test/prism/fixtures/unparser/corpus/literal/flipflop.txt
index 8badd39d57332c..139904a53f505d 100644
--- a/test/prism/fixtures/unparser/corpus/literal/flipflop.txt
+++ b/test/prism/fixtures/unparser/corpus/literal/flipflop.txt
@@ -4,3 +4,7 @@ end
if ((i == 4)...(i == 4))
foo
end
+if ..foo
+end
+if foo..;
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/since/32.txt b/test/prism/fixtures/unparser/corpus/literal/since/32.txt
index fa279f11cf81f3..b8e096d8fcbad4 100644
--- a/test/prism/fixtures/unparser/corpus/literal/since/32.txt
+++ b/test/prism/fixtures/unparser/corpus/literal/since/32.txt
@@ -5,3 +5,7 @@ end
def foo(argument, *)
bar(argument, *)
end
+
+def foo(**)
+ { default: 1, ** }
+end
diff --git a/test/prism/fixtures/unparser/corpus/literal/yield.txt b/test/prism/fixtures/unparser/corpus/literal/yield.txt
deleted file mode 100644
index c0b5820842435a..00000000000000
--- a/test/prism/fixtures/unparser/corpus/literal/yield.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-yield
-yield(a)
-yield(a, b)
diff --git a/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt b/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt
new file mode 100644
index 00000000000000..8b4bc5d2395ed5
--- /dev/null
+++ b/test/prism/fixtures/unparser/corpus/semantic/opasgn.txt
@@ -0,0 +1 @@
+y["#{42}\n"] += "#{42}\n"
diff --git a/test/prism/fixtures/until.txt b/test/prism/fixtures/until.txt
index 7dcb5d495dbf14..652fc8c5a7c802 100644
--- a/test/prism/fixtures/until.txt
+++ b/test/prism/fixtures/until.txt
@@ -2,9 +2,9 @@ until true; 1; end
1 until true
-break until true
+tap { break until true }
-next until true
+tap { next until true }
return until true
diff --git a/test/prism/fixtures/while.txt b/test/prism/fixtures/while.txt
index eecfbfdddd0c96..b776f755eeb798 100644
--- a/test/prism/fixtures/while.txt
+++ b/test/prism/fixtures/while.txt
@@ -2,21 +2,21 @@ while true; 1; end
1 while true
-break while true
+tap { break while true }
-next while true
+tap { next while true }
return while true
foo :a, :b while bar?
-while def self.foo a = tap do end; end; break; end
+tap { while def self.foo a = tap do end; end; break; end }
-while class Foo a = tap do end; end; break; end
+tap { while class Foo a = tap do end; end; break; end }
-while class << self; tap do end; end; break; end
+tap { while class << self; tap do end; end; break; end }
-while class << self; a = tap do end; end; break; end
+tap { while class << self; a = tap do end; end; break; end }
while def foo = bar do end; end
diff --git a/test/prism/fixtures/whitequark/args_assocs.txt b/test/prism/fixtures/whitequark/args_assocs.txt
deleted file mode 100644
index b33e1311818776..00000000000000
--- a/test/prism/fixtures/whitequark/args_assocs.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-fun(:foo => 1)
-
-fun(:foo => 1, &baz)
-
-self.[]= foo, :a => 1
-
-self[:bar => 1]
-
-super(:foo => 42)
-
-yield(:foo => 42)
diff --git a/test/prism/fixtures/whitequark/args_assocs_legacy.txt b/test/prism/fixtures/whitequark/args_assocs_legacy.txt
deleted file mode 100644
index b33e1311818776..00000000000000
--- a/test/prism/fixtures/whitequark/args_assocs_legacy.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-fun(:foo => 1)
-
-fun(:foo => 1, &baz)
-
-self.[]= foo, :a => 1
-
-self[:bar => 1]
-
-super(:foo => 42)
-
-yield(:foo => 42)
diff --git a/test/prism/fixtures/whitequark/break.txt b/test/prism/fixtures/whitequark/break.txt
deleted file mode 100644
index da51ec7c31efbc..00000000000000
--- a/test/prism/fixtures/whitequark/break.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-break
-
-break foo
-
-break()
-
-break(foo)
diff --git a/test/prism/fixtures/whitequark/break_block.txt b/test/prism/fixtures/whitequark/break_block.txt
deleted file mode 100644
index 4b58c58d5eaa69..00000000000000
--- a/test/prism/fixtures/whitequark/break_block.txt
+++ /dev/null
@@ -1 +0,0 @@
-break fun foo do end
diff --git a/test/prism/fixtures/whitequark/class_definition_in_while_cond.txt b/test/prism/fixtures/whitequark/class_definition_in_while_cond.txt
deleted file mode 100644
index 10427314b592ed..00000000000000
--- a/test/prism/fixtures/whitequark/class_definition_in_while_cond.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-while class << self; a = tap do end; end; break; end
-
-while class << self; tap do end; end; break; end
-
-while class Foo a = tap do end; end; break; end
-
-while class Foo; tap do end; end; break; end
diff --git a/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt b/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt
new file mode 100644
index 00000000000000..757863b12bf56c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_eflipflop_with_beginless_range.txt
@@ -0,0 +1 @@
+if ...bar; end
diff --git a/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt b/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt
new file mode 100644
index 00000000000000..8a3b815ec9c9fc
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_eflipflop_with_endless_range.txt
@@ -0,0 +1 @@
+if foo...; end
diff --git a/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt b/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt
new file mode 100644
index 00000000000000..4ff5b09690055c
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_iflipflop_with_beginless_range.txt
@@ -0,0 +1 @@
+if ..bar; end
diff --git a/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt b/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt
new file mode 100644
index 00000000000000..2739ad0e2a31a7
--- /dev/null
+++ b/test/prism/fixtures/whitequark/cond_iflipflop_with_endless_range.txt
@@ -0,0 +1 @@
+if foo..; end
diff --git a/test/prism/fixtures/whitequark/if_while_after_class__since_32.txt b/test/prism/fixtures/whitequark/if_while_after_class__since_32.txt
deleted file mode 100644
index 1552494d28ce77..00000000000000
--- a/test/prism/fixtures/whitequark/if_while_after_class__since_32.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-class if true; Object end::Kernel; end
-
-class while true; break Object end::Kernel; end
-
-module if true; Object end::Kernel; end
-
-module while true; break Object end::Kernel; end
diff --git a/test/prism/fixtures/whitequark/method_definition_in_while_cond.txt b/test/prism/fixtures/whitequark/method_definition_in_while_cond.txt
deleted file mode 100644
index 6ec38906a1e929..00000000000000
--- a/test/prism/fixtures/whitequark/method_definition_in_while_cond.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-while def foo a = tap do end; end; break; end
-
-while def foo; tap do end; end; break; end
-
-while def self.foo a = tap do end; end; break; end
-
-while def self.foo; tap do end; end; break; end
diff --git a/test/prism/fixtures/whitequark/next.txt b/test/prism/fixtures/whitequark/next.txt
deleted file mode 100644
index 7556ac2b74b9b0..00000000000000
--- a/test/prism/fixtures/whitequark/next.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-next
-
-next foo
-
-next()
-
-next(foo)
diff --git a/test/prism/fixtures/whitequark/next_block.txt b/test/prism/fixtures/whitequark/next_block.txt
deleted file mode 100644
index d3b51dfa2186d6..00000000000000
--- a/test/prism/fixtures/whitequark/next_block.txt
+++ /dev/null
@@ -1 +0,0 @@
-next fun foo do end
diff --git a/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt b/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt
new file mode 100644
index 00000000000000..f9ffd4329aa508
--- /dev/null
+++ b/test/prism/fixtures/whitequark/numparam_ruby_bug_19025.txt
@@ -0,0 +1 @@
+p { [_1 **2] }
diff --git a/test/prism/fixtures/whitequark/parser_bug_989.txt b/test/prism/fixtures/whitequark/parser_bug_989.txt
new file mode 100644
index 00000000000000..b7b99612b4e25f
--- /dev/null
+++ b/test/prism/fixtures/whitequark/parser_bug_989.txt
@@ -0,0 +1,3 @@
+ <<-HERE
+ content
+ HERE
diff --git a/test/prism/fixtures/whitequark/redo.txt b/test/prism/fixtures/whitequark/redo.txt
deleted file mode 100644
index f49789cbab8747..00000000000000
--- a/test/prism/fixtures/whitequark/redo.txt
+++ /dev/null
@@ -1 +0,0 @@
-redo
diff --git a/test/prism/fixtures/whitequark/retry.txt b/test/prism/fixtures/whitequark/retry.txt
deleted file mode 100644
index 77428f7b73e572..00000000000000
--- a/test/prism/fixtures/whitequark/retry.txt
+++ /dev/null
@@ -1 +0,0 @@
-retry
diff --git a/test/prism/fixtures/whitequark/yield.txt b/test/prism/fixtures/whitequark/yield.txt
deleted file mode 100644
index 0ecf6395891989..00000000000000
--- a/test/prism/fixtures/whitequark/yield.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-yield
-
-yield foo
-
-yield()
-
-yield(foo)
diff --git a/test/prism/fixtures/yield.txt b/test/prism/fixtures/yield.txt
index d75ab57a187eda..752ba27a2e67f4 100644
--- a/test/prism/fixtures/yield.txt
+++ b/test/prism/fixtures/yield.txt
@@ -1,7 +1,7 @@
-yield
+def foo; yield; end
-yield()
+def foo; yield(); end
-yield(1)
+def foo; yield(1); end
-yield(1, 2, 3)
+def foo; yield(1, 2, 3); end
diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb
index b7b9a754cae781..81417fbcb3c09e 100644
--- a/test/prism/location_test.rb
+++ b/test/prism/location_test.rb
@@ -98,10 +98,10 @@ def test_BlockParametersNode
end
def test_BreakNode
- assert_location(BreakNode, "break")
- assert_location(BreakNode, "break foo")
- assert_location(BreakNode, "break foo, bar")
- assert_location(BreakNode, "break(foo)")
+ assert_location(BreakNode, "tap { break }", 6...11) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break foo }", 6...15) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break foo, bar }", 6...20) { |node| node.block.body.body.first }
+ assert_location(BreakNode, "tap { break(foo) }", 6...16) { |node| node.block.body.body.first }
end
def test_CallNode
@@ -637,10 +637,10 @@ def test_MultiWriteNode
end
def test_NextNode
- assert_location(NextNode, "next")
- assert_location(NextNode, "next foo")
- assert_location(NextNode, "next foo, bar")
- assert_location(NextNode, "next(foo)")
+ assert_location(NextNode, "tap { next }", 6...10) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next foo }", 6...14) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next foo, bar }", 6...19) { |node| node.block.body.body.first }
+ assert_location(NextNode, "tap { next(foo) }", 6...15) { |node| node.block.body.body.first }
end
def test_NilNode
@@ -726,7 +726,7 @@ def test_RationalNode
end
def test_RedoNode
- assert_location(RedoNode, "redo")
+ assert_location(RedoNode, "tap { redo }", 6...10) { |node| node.block.body.body.first }
end
def test_RegularExpressionNode
@@ -769,7 +769,7 @@ def test_RestParameterNode
end
def test_RetryNode
- assert_location(RetryNode, "retry")
+ assert_location(RetryNode, "begin; rescue; retry; end", 15...20) { |node| node.rescue_clause.statements.body.first }
end
def test_ReturnNode
@@ -910,10 +910,10 @@ def test_XStringNode
end
def test_YieldNode
- assert_location(YieldNode, "yield")
- assert_location(YieldNode, "yield foo")
- assert_location(YieldNode, "yield foo, bar")
- assert_location(YieldNode, "yield(foo)")
+ assert_location(YieldNode, "def test; yield; end", 10...15) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield foo; end", 10...19) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield foo, bar; end", 10...24) { |node| node.body.body.first }
+ assert_location(YieldNode, "def test; yield(foo); end", 10...20) { |node| node.body.body.first }
end
def test_all_tested
diff --git a/test/prism/magic_comment_test.rb b/test/prism/magic_comment_test.rb
index 4c02af732cf993..9e2e92af92777e 100644
--- a/test/prism/magic_comment_test.rb
+++ b/test/prism/magic_comment_test.rb
@@ -2,6 +2,8 @@
require_relative "test_helper"
+return if RUBY_ENGINE != "ruby"
+
module Prism
class MagicCommentTest < TestCase
examples = [
@@ -22,16 +24,10 @@ class MagicCommentTest < TestCase
examples.each.with_index(1) do |example, index|
define_method(:"test_magic_comment_#{index}") do
- assert_magic_comment(example)
+ expected = RubyVM::InstructionSequence.compile(%Q{#{example}\n""}).eval.encoding
+ actual = Prism.parse(example).encoding
+ assert_equal expected, actual
end
end
-
- private
-
- def assert_magic_comment(example)
- expected = Ripper.new(example).tap(&:parse).encoding
- actual = Prism.parse(example).encoding
- assert_equal expected, actual
- end
end
end
diff --git a/test/prism/parse_test.rb b/test/prism/parse_test.rb
index 4255553f516afd..afb53e06685c11 100644
--- a/test/prism/parse_test.rb
+++ b/test/prism/parse_test.rb
@@ -188,52 +188,43 @@ def test_parse_file_comments
directory = File.dirname(snapshot)
FileUtils.mkdir_p(directory) unless File.directory?(directory)
- ripper_should_parse = ripper_should_match = ripper_enabled
-
- # This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
- # we're on an earlier version.
- ripper_should_match = false if relative == "seattlerb/pct_w_heredoc_interp_nested.txt" && RUBY_VERSION < "3.3.0"
-
- # It seems like there are some oddities with nested heredocs and ripper.
- # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
- ripper_should_match = false if relative == "seattlerb/heredoc_nested.txt"
-
- # Ripper seems to have a bug that the regex portions before and after the heredoc are combined
- # into a single token. See https://bugs.ruby-lang.org/issues/19838.
- #
- # Additionally, Ripper cannot parse the %w[] fixture in this file, so set ripper_should_parse to false.
- ripper_should_parse = false if relative == "spanning_heredoc.txt"
-
- # Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace characters in the heredoc start.
- # Example: <<~' EOF' or <<-' EOF'
- # https://bugs.ruby-lang.org/issues/19539
- ripper_should_parse = false if relative == "heredocs_leading_whitespace.txt" && RUBY_VERSION < "3.3.0"
+ ripper_should_match = ripper_enabled
+ check_valid_syntax = RUBY_VERSION >= "3.2.0"
+
+ case relative
+ when "seattlerb/pct_w_heredoc_interp_nested.txt"
+ # This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
+ # we're on an earlier version.
+ ripper_should_match = false if RUBY_VERSION < "3.3.0"
+ when "seattlerb/heredoc_nested.txt", "whitequark/dedenting_heredoc.txt"
+ # It seems like there are some oddities with nested heredocs and ripper.
+ # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
+ ripper_should_match = false
+ when "spanning_heredoc.txt", "spanning_heredoc_newlines.txt"
+ # Ripper seems to have a bug that the regex portions before and after
+ # the heredoc are combined into a single token. See
+ # https://bugs.ruby-lang.org/issues/19838.
+ ripper_should_match = false
+ when "heredocs_leading_whitespace.txt"
+ # Ruby < 3.3.0 cannot parse heredocs where there are leading whitespace
+ # characters in the heredoc start.
+ # Example: <<~' EOF' or <<-' EOF'
+ # https://bugs.ruby-lang.org/issues/19539
+ if RUBY_VERSION < "3.3.0"
+ ripper_should_match = false
+ check_valid_syntax = false
+ end
+ end
define_method "test_filepath_#{relative}" do
- # First, read the source from the filepath. Use binmode to avoid converting CRLF on Windows,
- # and explicitly set the external encoding to UTF-8 to override the binmode default.
+ # First, read the source from the filepath. Use binmode to avoid
+ # converting CRLF on Windows, and explicitly set the external encoding
+ # to UTF-8 to override the binmode default.
source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
- if ripper_should_parse
- src = source
-
- case relative
- when /break|next|redo|if|unless|rescue|control|keywords|retry/
- # Uncaught syntax errors: Invalid break, Invalid next
- src = "->do\nrescue\n#{src}\nend"
- ripper_should_match = false
- end
- case src
- when /^ *yield/
- # Uncaught syntax errors: Invalid yield
- src = "def __invalid_yield__\n#{src}\nend"
- ripper_should_match = false
- end
-
- # Make sure that it can be correctly parsed by Ripper. If it can't, then we have a fixture
- # that is invalid Ruby.
- refute_nil(Ripper.sexp_raw(src), "Ripper failed to parse")
- end
+ # Make sure that the given source is valid syntax, otherwise we have an
+ # invalid fixture.
+ assert_valid_syntax(source) if check_valid_syntax
# Next, assert that there were no errors during parsing.
result = Prism.parse(source, filepath: relative)
@@ -276,7 +267,7 @@ def test_parse_file_comments
source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
assert_equal expected_newlines, Debug.newlines(source)
- if ripper_should_parse && ripper_should_match
+ if ripper_should_match
# Finally, assert that we can lex the source and get the same tokens as
# Ripper.
lex_result = Prism.lex_compat(source)
diff --git a/test/prism/ruby_parser_test.rb b/test/prism/ruby_parser_test.rb
index c9883574a68b31..8edeac4b4f8cbc 100644
--- a/test/prism/ruby_parser_test.rb
+++ b/test/prism/ruby_parser_test.rb
@@ -84,6 +84,7 @@ class RubyParserTest < TestCase
whitequark/if_while_after_class__since_32.txt
whitequark/lvar_injecting_match.txt
whitequark/not.txt
+ whitequark/numparam_ruby_bug_19025.txt
whitequark/op_asgn_cmd.txt
whitequark/parser_bug_640.txt
whitequark/parser_slash_slash_n_escaping_in_literals.txt
diff --git a/test/prism/snapshots/break.txt b/test/prism/snapshots/break.txt
index 984b9f7f227272..c15a9e4675f57f 100644
--- a/test/prism/snapshots/break.txt
+++ b/test/prism/snapshots/break.txt
@@ -3,144 +3,306 @@
└── statements:
@ StatementsNode (location: (1,0)-(25,23))
└── body: (length: 11)
- ├── @ BreakNode (location: (1,0)-(1,5))
+ ├── @ CallNode (location: (1,0)-(1,13))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
│ ├── arguments: ∅
- │ └── keyword_loc: (1,0)-(1,5) = "break"
- ├── @ BreakNode (location: (3,0)-(3,19))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,6)-(3,19))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ ParenthesesNode (location: (3,6)-(3,9))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (3,7)-(3,8))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (3,7)-(3,8))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ ├── opening_loc: (3,6)-(3,7) = "("
- │ │ │ └── closing_loc: (3,8)-(3,9) = ")"
- │ │ ├── @ ParenthesesNode (location: (3,11)-(3,14))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (3,12)-(3,13))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (3,12)-(3,13))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ ├── opening_loc: (3,11)-(3,12) = "("
- │ │ │ └── closing_loc: (3,13)-(3,14) = ")"
- │ │ └── @ ParenthesesNode (location: (3,16)-(3,19))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (3,17)-(3,18))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (3,17)-(3,18))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 3
- │ │ ├── opening_loc: (3,16)-(3,17) = "("
- │ │ └── closing_loc: (3,18)-(3,19) = ")"
- │ └── keyword_loc: (3,0)-(3,5) = "break"
- ├── @ BreakNode (location: (5,0)-(5,7))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,6)-(5,7))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ IntegerNode (location: (5,6)-(5,7))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ └── keyword_loc: (5,0)-(5,5) = "break"
- ├── @ BreakNode (location: (7,0)-(8,1))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (7,6)-(8,1))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ IntegerNode (location: (7,6)-(7,7))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── @ IntegerNode (location: (7,9)-(7,10))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── @ IntegerNode (location: (8,0)-(8,1))
- │ │ ├── flags: decimal
- │ │ └── value: 3
- │ └── keyword_loc: (7,0)-(7,5) = "break"
- ├── @ BreakNode (location: (10,0)-(10,13))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (10,6)-(10,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ IntegerNode (location: (10,6)-(10,7))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── @ IntegerNode (location: (10,9)-(10,10))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── @ IntegerNode (location: (10,12)-(10,13))
- │ │ ├── flags: decimal
- │ │ └── value: 3
- │ └── keyword_loc: (10,0)-(10,5) = "break"
- ├── @ BreakNode (location: (12,0)-(12,15))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (12,6)-(12,15))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ArrayNode (location: (12,6)-(12,15))
- │ │ ├── flags: ∅
- │ │ ├── elements: (length: 3)
- │ │ │ ├── @ IntegerNode (location: (12,7)-(12,8))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ ├── @ IntegerNode (location: (12,10)-(12,11))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ └── @ IntegerNode (location: (12,13)-(12,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 3
- │ │ ├── opening_loc: (12,6)-(12,7) = "["
- │ │ └── closing_loc: (12,14)-(12,15) = "]"
- │ └── keyword_loc: (12,0)-(12,5) = "break"
- ├── @ BreakNode (location: (14,0)-(17,1))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (14,5)-(17,1))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (14,5)-(17,1))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (15,2)-(16,3))
- │ │ │ └── body: (length: 2)
- │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ ├── opening_loc: (14,5)-(14,6) = "("
- │ │ └── closing_loc: (17,0)-(17,1) = ")"
- │ └── keyword_loc: (14,0)-(14,5) = "break"
- ├── @ BreakNode (location: (19,0)-(19,7))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (19,5)-(19,7))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (19,5)-(19,7))
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (19,5)-(19,6) = "("
- │ │ └── closing_loc: (19,6)-(19,7) = ")"
- │ └── keyword_loc: (19,0)-(19,5) = "break"
- ├── @ BreakNode (location: (21,0)-(21,8))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (21,5)-(21,8))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (21,5)-(21,8))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (21,6)-(21,7))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (21,6)-(21,7))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── opening_loc: (21,5)-(21,6) = "("
- │ │ └── closing_loc: (21,7)-(21,8) = ")"
- │ └── keyword_loc: (21,0)-(21,5) = "break"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,13))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (1,6)-(1,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (1,6)-(1,11) = "break"
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,12)-(1,13) = "}"
+ ├── @ CallNode (location: (3,0)-(3,27))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (3,0)-(3,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(3,27))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,6)-(3,25))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (3,6)-(3,25))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,12)-(3,25))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ ParenthesesNode (location: (3,12)-(3,15))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,13)-(3,14))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,13)-(3,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (3,12)-(3,13) = "("
+ │ │ │ │ └── closing_loc: (3,14)-(3,15) = ")"
+ │ │ │ ├── @ ParenthesesNode (location: (3,17)-(3,20))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,18)-(3,19))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,18)-(3,19))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── opening_loc: (3,17)-(3,18) = "("
+ │ │ │ │ └── closing_loc: (3,19)-(3,20) = ")"
+ │ │ │ └── @ ParenthesesNode (location: (3,22)-(3,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (3,23)-(3,24))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,23)-(3,24))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (3,22)-(3,23) = "("
+ │ │ │ └── closing_loc: (3,24)-(3,25) = ")"
+ │ │ └── keyword_loc: (3,6)-(3,11) = "break"
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (3,26)-(3,27) = "}"
+ ├── @ CallNode (location: (5,0)-(5,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,15))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,13))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,12)-(5,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,12)-(5,13))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,14)-(5,15) = "}"
+ ├── @ CallNode (location: (7,0)-(8,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(8,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (7,6)-(8,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,12)-(8,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (7,12)-(7,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (8,0)-(8,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (7,6)-(7,11) = "break"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (8,2)-(8,3) = "}"
+ ├── @ CallNode (location: (10,0)-(10,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (10,6)-(10,19))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,12)-(10,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (10,12)-(10,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (10,15)-(10,16))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (10,18)-(10,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (10,6)-(10,11) = "break"
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,20)-(10,21) = "}"
+ ├── @ CallNode (location: (12,0)-(12,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (12,6)-(12,21))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (12,12)-(12,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ArrayNode (location: (12,12)-(12,21))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 3)
+ │ │ │ │ ├── @ IntegerNode (location: (12,13)-(12,14))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── @ IntegerNode (location: (12,16)-(12,17))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── @ IntegerNode (location: (12,19)-(12,20))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (12,12)-(12,13) = "["
+ │ │ │ └── closing_loc: (12,20)-(12,21) = "]"
+ │ │ └── keyword_loc: (12,6)-(12,11) = "break"
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,22)-(12,23) = "}"
+ ├── @ CallNode (location: (14,0)-(17,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (14,0)-(14,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (14,4)-(17,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,6)-(17,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (14,6)-(17,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (14,11)-(17,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (14,11)-(17,1))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,2)-(16,3))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: (14,11)-(14,12) = "("
+ │ │ │ └── closing_loc: (17,0)-(17,1) = ")"
+ │ │ └── keyword_loc: (14,6)-(14,11) = "break"
+ │ ├── opening_loc: (14,4)-(14,5) = "{"
+ │ └── closing_loc: (17,2)-(17,3) = "}"
+ ├── @ CallNode (location: (19,0)-(19,15))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(19,15))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(19,13))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (19,6)-(19,13))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (19,11)-(19,13))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (19,11)-(19,13))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (19,11)-(19,12) = "("
+ │ │ │ └── closing_loc: (19,12)-(19,13) = ")"
+ │ │ └── keyword_loc: (19,6)-(19,11) = "break"
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (19,14)-(19,15) = "}"
+ ├── @ CallNode (location: (21,0)-(21,16))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (21,0)-(21,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (21,4)-(21,16))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (21,6)-(21,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (21,6)-(21,14))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (21,11)-(21,14))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (21,11)-(21,14))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (21,12)-(21,13))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (21,12)-(21,13))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── opening_loc: (21,11)-(21,12) = "("
+ │ │ │ └── closing_loc: (21,13)-(21,14) = ")"
+ │ │ └── keyword_loc: (21,6)-(21,11) = "break"
+ │ ├── opening_loc: (21,4)-(21,5) = "{"
+ │ └── closing_loc: (21,15)-(21,16) = "}"
├── @ CallNode (location: (23,0)-(23,22))
│ ├── flags: ∅
│ ├── receiver:
diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt
index f879ed38857a8e..b618659756a215 100644
--- a/test/prism/snapshots/if.txt
+++ b/test/prism/snapshots/if.txt
@@ -91,32 +91,68 @@
│ │ └── value: 1
│ ├── consequent: ∅
│ └── end_keyword_loc: ∅
- ├── @ IfNode (location: (10,0)-(10,13))
- │ ├── if_keyword_loc: (10,6)-(10,8) = "if"
- │ ├── predicate:
- │ │ @ TrueNode (location: (10,9)-(10,13))
- │ ├── then_keyword_loc: ∅
- │ ├── statements:
- │ │ @ StatementsNode (location: (10,0)-(10,5))
- │ │ └── body: (length: 1)
- │ │ └── @ BreakNode (location: (10,0)-(10,5))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (10,0)-(10,5) = "break"
- │ ├── consequent: ∅
- │ └── end_keyword_loc: ∅
- ├── @ IfNode (location: (12,0)-(12,12))
- │ ├── if_keyword_loc: (12,5)-(12,7) = "if"
- │ ├── predicate:
- │ │ @ TrueNode (location: (12,8)-(12,12))
- │ ├── then_keyword_loc: ∅
- │ ├── statements:
- │ │ @ StatementsNode (location: (12,0)-(12,4))
- │ │ └── body: (length: 1)
- │ │ └── @ NextNode (location: (12,0)-(12,4))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (12,0)-(12,4) = "next"
- │ ├── consequent: ∅
- │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (10,0)-(10,21))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,21))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,19))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (10,6)-(10,19))
+ │ │ ├── if_keyword_loc: (10,12)-(10,14) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (10,15)-(10,19))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,6)-(10,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (10,6)-(10,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (10,6)-(10,11) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,20)-(10,21) = "}"
+ ├── @ CallNode (location: (12,0)-(12,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (12,6)-(12,18))
+ │ │ ├── if_keyword_loc: (12,11)-(12,13) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (12,14)-(12,18))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (12,6)-(12,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NextNode (location: (12,6)-(12,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (12,6)-(12,10) = "next"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,19)-(12,20) = "}"
├── @ IfNode (location: (14,0)-(14,14))
│ ├── if_keyword_loc: (14,7)-(14,9) = "if"
│ ├── predicate:
@@ -130,34 +166,52 @@
│ │ └── arguments: ∅
│ ├── consequent: ∅
│ └── end_keyword_loc: ∅
- ├── @ IfNode (location: (16,0)-(16,30))
- │ ├── if_keyword_loc: (16,0)-(16,2) = "if"
- │ ├── predicate:
- │ │ @ CallNode (location: (16,3)-(16,12))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :exit_loop
- │ │ ├── message_loc: (16,3)-(16,12) = "exit_loop"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── then_keyword_loc: (16,13)-(16,17) = "then"
- │ ├── statements:
- │ │ @ StatementsNode (location: (16,18)-(16,26))
- │ │ └── body: (length: 1)
- │ │ └── @ BreakNode (location: (16,18)-(16,26))
- │ │ ├── arguments:
- │ │ │ @ ArgumentsNode (location: (16,24)-(16,26))
- │ │ │ ├── flags: ∅
- │ │ │ └── arguments: (length: 1)
- │ │ │ └── @ IntegerNode (location: (16,24)-(16,26))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 42
- │ │ └── keyword_loc: (16,18)-(16,23) = "break"
- │ ├── consequent: ∅
- │ └── end_keyword_loc: (16,27)-(16,30) = "end"
+ ├── @ CallNode (location: (16,0)-(16,38))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (16,0)-(16,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (16,4)-(16,38))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (16,6)-(16,36))
+ │ │ └── body: (length: 1)
+ │ │ └── @ IfNode (location: (16,6)-(16,36))
+ │ │ ├── if_keyword_loc: (16,6)-(16,8) = "if"
+ │ │ ├── predicate:
+ │ │ │ @ CallNode (location: (16,9)-(16,18))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :exit_loop
+ │ │ │ ├── message_loc: (16,9)-(16,18) = "exit_loop"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── then_keyword_loc: (16,19)-(16,23) = "then"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (16,24)-(16,32))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (16,24)-(16,32))
+ │ │ │ ├── arguments:
+ │ │ │ │ @ ArgumentsNode (location: (16,30)-(16,32))
+ │ │ │ │ ├── flags: ∅
+ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (16,30)-(16,32))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── keyword_loc: (16,24)-(16,29) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: (16,33)-(16,36) = "end"
+ │ ├── opening_loc: (16,4)-(16,5) = "{"
+ │ └── closing_loc: (16,37)-(16,38) = "}"
├── @ IfNode (location: (18,0)-(20,3))
│ ├── if_keyword_loc: (18,0)-(18,2) = "if"
│ ├── predicate:
diff --git a/test/prism/snapshots/keywords.txt b/test/prism/snapshots/keywords.txt
index 8a93c38bfd3de2..b3d5c5e1c382c1 100644
--- a/test/prism/snapshots/keywords.txt
+++ b/test/prism/snapshots/keywords.txt
@@ -3,8 +3,42 @@
└── statements:
@ StatementsNode (location: (1,0)-(11,8))
└── body: (length: 6)
- ├── @ RedoNode (location: (1,0)-(1,4))
- ├── @ RetryNode (location: (3,0)-(3,5))
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RedoNode (location: (1,6)-(1,10))
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,11)-(1,12) = "}"
+ ├── @ BeginNode (location: (3,0)-(3,25))
+ │ ├── begin_keyword_loc: (3,0)-(3,5) = "begin"
+ │ ├── statements: ∅
+ │ ├── rescue_clause:
+ │ │ @ RescueNode (location: (3,7)-(3,20))
+ │ │ ├── keyword_loc: (3,7)-(3,13) = "rescue"
+ │ │ ├── exceptions: (length: 0)
+ │ │ ├── operator_loc: ∅
+ │ │ ├── reference: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (3,15)-(3,20))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ RetryNode (location: (3,15)-(3,20))
+ │ │ └── consequent: ∅
+ │ ├── else_clause: ∅
+ │ ├── ensure_clause: ∅
+ │ └── end_keyword_loc: (3,22)-(3,25) = "end"
├── @ SelfNode (location: (5,0)-(5,4))
├── @ SourceEncodingNode (location: (7,0)-(7,12))
├── @ SourceFileNode (location: (9,0)-(9,8))
diff --git a/test/prism/snapshots/next.txt b/test/prism/snapshots/next.txt
index 64ec3ebc91a2c0..ce2e497de93003 100644
--- a/test/prism/snapshots/next.txt
+++ b/test/prism/snapshots/next.txt
@@ -1,149 +1,329 @@
-@ ProgramNode (location: (1,0)-(24,7))
+@ ProgramNode (location: (1,0)-(24,15))
├── locals: []
└── statements:
- @ StatementsNode (location: (1,0)-(24,7))
- └── body: (length: 11)
- ├── @ NextNode (location: (1,0)-(1,4))
+ @ StatementsNode (location: (1,0)-(24,15))
+ └── body: (length: 10)
+ ├── @ CallNode (location: (1,0)-(1,12))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (1,0)-(1,3) = "tap"
+ │ ├── opening_loc: ∅
│ ├── arguments: ∅
- │ └── keyword_loc: (1,0)-(1,4) = "next"
- ├── @ NextNode (location: (3,0)-(3,18))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,5)-(3,18))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ ParenthesesNode (location: (3,5)-(3,8))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (3,6)-(3,7))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (3,6)-(3,7))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ ├── opening_loc: (3,5)-(3,6) = "("
- │ │ │ └── closing_loc: (3,7)-(3,8) = ")"
- │ │ ├── @ ParenthesesNode (location: (3,10)-(3,13))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (3,11)-(3,12))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (3,11)-(3,12))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ ├── opening_loc: (3,10)-(3,11) = "("
- │ │ │ └── closing_loc: (3,12)-(3,13) = ")"
- │ │ └── @ ParenthesesNode (location: (3,15)-(3,18))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (3,16)-(3,17))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (3,16)-(3,17))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 3
- │ │ ├── opening_loc: (3,15)-(3,16) = "("
- │ │ └── closing_loc: (3,17)-(3,18) = ")"
- │ └── keyword_loc: (3,0)-(3,4) = "next"
- ├── @ NextNode (location: (5,0)-(5,6))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,5)-(5,6))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ IntegerNode (location: (5,5)-(5,6))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ └── keyword_loc: (5,0)-(5,4) = "next"
- ├── @ NextNode (location: (7,0)-(8,1))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (7,5)-(8,1))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ IntegerNode (location: (7,5)-(7,6))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── @ IntegerNode (location: (7,8)-(7,9))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── @ IntegerNode (location: (8,0)-(8,1))
- │ │ ├── flags: decimal
- │ │ └── value: 3
- │ └── keyword_loc: (7,0)-(7,4) = "next"
- ├── @ NextNode (location: (10,0)-(10,12))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (10,5)-(10,12))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 3)
- │ │ ├── @ IntegerNode (location: (10,5)-(10,6))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── @ IntegerNode (location: (10,8)-(10,9))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── @ IntegerNode (location: (10,11)-(10,12))
- │ │ ├── flags: decimal
- │ │ └── value: 3
- │ └── keyword_loc: (10,0)-(10,4) = "next"
- ├── @ NextNode (location: (12,0)-(12,14))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (12,5)-(12,14))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ArrayNode (location: (12,5)-(12,14))
- │ │ ├── flags: ∅
- │ │ ├── elements: (length: 3)
- │ │ │ ├── @ IntegerNode (location: (12,6)-(12,7))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ ├── @ IntegerNode (location: (12,9)-(12,10))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ └── @ IntegerNode (location: (12,12)-(12,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 3
- │ │ ├── opening_loc: (12,5)-(12,6) = "["
- │ │ └── closing_loc: (12,13)-(12,14) = "]"
- │ └── keyword_loc: (12,0)-(12,4) = "next"
- ├── @ NextNode (location: (14,0)-(17,1))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (14,4)-(17,1))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (14,4)-(17,1))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (15,2)-(16,3))
- │ │ │ └── body: (length: 2)
- │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ ├── opening_loc: (14,4)-(14,5) = "("
- │ │ └── closing_loc: (17,0)-(17,1) = ")"
- │ └── keyword_loc: (14,0)-(14,4) = "next"
- ├── @ NextNode (location: (19,0)-(19,4))
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (1,4)-(1,12))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,6)-(1,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (1,6)-(1,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (1,6)-(1,10) = "next"
+ │ ├── opening_loc: (1,4)-(1,5) = "{"
+ │ └── closing_loc: (1,11)-(1,12) = "}"
+ ├── @ CallNode (location: (3,0)-(3,26))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (3,0)-(3,3) = "tap"
+ │ ├── opening_loc: ∅
│ ├── arguments: ∅
- │ └── keyword_loc: (19,0)-(19,4) = "next"
- ├── @ IntegerNode (location: (20,0)-(20,1))
- │ ├── flags: decimal
- │ └── value: 1
- ├── @ NextNode (location: (22,0)-(22,6))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (22,4)-(22,6))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (22,4)-(22,6))
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (22,4)-(22,5) = "("
- │ │ └── closing_loc: (22,5)-(22,6) = ")"
- │ └── keyword_loc: (22,0)-(22,4) = "next"
- └── @ NextNode (location: (24,0)-(24,7))
- ├── arguments:
- │ @ ArgumentsNode (location: (24,4)-(24,7))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ ParenthesesNode (location: (24,4)-(24,7))
- │ ├── body:
- │ │ @ StatementsNode (location: (24,5)-(24,6))
- │ │ └── body: (length: 1)
- │ │ └── @ IntegerNode (location: (24,5)-(24,6))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ ├── opening_loc: (24,4)-(24,5) = "("
- │ └── closing_loc: (24,6)-(24,7) = ")"
- └── keyword_loc: (24,0)-(24,4) = "next"
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (3,4)-(3,26))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,6)-(3,24))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (3,6)-(3,24))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (3,11)-(3,24))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ ParenthesesNode (location: (3,11)-(3,14))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,12)-(3,13))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,12)-(3,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── opening_loc: (3,11)-(3,12) = "("
+ │ │ │ │ └── closing_loc: (3,13)-(3,14) = ")"
+ │ │ │ ├── @ ParenthesesNode (location: (3,16)-(3,19))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (3,17)-(3,18))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ IntegerNode (location: (3,17)-(3,18))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ ├── opening_loc: (3,16)-(3,17) = "("
+ │ │ │ │ └── closing_loc: (3,18)-(3,19) = ")"
+ │ │ │ └── @ ParenthesesNode (location: (3,21)-(3,24))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (3,22)-(3,23))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (3,22)-(3,23))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (3,21)-(3,22) = "("
+ │ │ │ └── closing_loc: (3,23)-(3,24) = ")"
+ │ │ └── keyword_loc: (3,6)-(3,10) = "next"
+ │ ├── opening_loc: (3,4)-(3,5) = "{"
+ │ └── closing_loc: (3,25)-(3,26) = "}"
+ ├── @ CallNode (location: (5,0)-(5,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (5,6)-(5,12))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,11)-(5,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,11)-(5,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── keyword_loc: (5,6)-(5,10) = "next"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,13)-(5,14) = "}"
+ ├── @ CallNode (location: (7,0)-(8,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(8,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(8,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(8,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (7,11)-(8,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (7,11)-(7,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (7,14)-(7,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (8,0)-(8,1))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (8,2)-(8,3) = "}"
+ ├── @ CallNode (location: (10,0)-(10,20))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,20))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (10,6)-(10,18))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (10,11)-(10,18))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 3)
+ │ │ │ ├── @ IntegerNode (location: (10,11)-(10,12))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ ├── @ IntegerNode (location: (10,14)-(10,15))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ └── @ IntegerNode (location: (10,17)-(10,18))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 3
+ │ │ └── keyword_loc: (10,6)-(10,10) = "next"
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,19)-(10,20) = "}"
+ ├── @ CallNode (location: (12,0)-(12,22))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (12,0)-(12,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (12,4)-(12,22))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (12,6)-(12,20))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (12,6)-(12,20))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (12,11)-(12,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ArrayNode (location: (12,11)-(12,20))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── elements: (length: 3)
+ │ │ │ │ ├── @ IntegerNode (location: (12,12)-(12,13))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ ├── @ IntegerNode (location: (12,15)-(12,16))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 2
+ │ │ │ │ └── @ IntegerNode (location: (12,18)-(12,19))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 3
+ │ │ │ ├── opening_loc: (12,11)-(12,12) = "["
+ │ │ │ └── closing_loc: (12,19)-(12,20) = "]"
+ │ │ └── keyword_loc: (12,6)-(12,10) = "next"
+ │ ├── opening_loc: (12,4)-(12,5) = "{"
+ │ └── closing_loc: (12,21)-(12,22) = "}"
+ ├── @ CallNode (location: (14,0)-(17,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (14,0)-(14,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (14,4)-(17,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (14,6)-(17,1))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (14,6)-(17,1))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (14,10)-(17,1))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (14,10)-(17,1))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,2)-(16,3))
+ │ │ │ │ └── body: (length: 2)
+ │ │ │ │ ├── @ IntegerNode (location: (15,2)-(15,3))
+ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ └── value: 1
+ │ │ │ │ └── @ IntegerNode (location: (16,2)-(16,3))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 2
+ │ │ │ ├── opening_loc: (14,10)-(14,11) = "("
+ │ │ │ └── closing_loc: (17,0)-(17,1) = ")"
+ │ │ └── keyword_loc: (14,6)-(14,10) = "next"
+ │ ├── opening_loc: (14,4)-(14,5) = "{"
+ │ └── closing_loc: (17,2)-(17,3) = "}"
+ ├── @ CallNode (location: (19,0)-(20,3))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(20,3))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(20,1))
+ │ │ └── body: (length: 2)
+ │ │ ├── @ NextNode (location: (19,6)-(19,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (19,6)-(19,10) = "next"
+ │ │ └── @ IntegerNode (location: (20,0)-(20,1))
+ │ │ ├── flags: decimal
+ │ │ └── value: 1
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (20,2)-(20,3) = "}"
+ ├── @ CallNode (location: (22,0)-(22,14))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (22,0)-(22,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (22,4)-(22,14))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (22,6)-(22,12))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (22,6)-(22,12))
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (22,10)-(22,12))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ ParenthesesNode (location: (22,10)-(22,12))
+ │ │ │ ├── body: ∅
+ │ │ │ ├── opening_loc: (22,10)-(22,11) = "("
+ │ │ │ └── closing_loc: (22,11)-(22,12) = ")"
+ │ │ └── keyword_loc: (22,6)-(22,10) = "next"
+ │ ├── opening_loc: (22,4)-(22,5) = "{"
+ │ └── closing_loc: (22,13)-(22,14) = "}"
+ └── @ CallNode (location: (24,0)-(24,15))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :tap
+ ├── message_loc: (24,0)-(24,3) = "tap"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (24,4)-(24,15))
+ ├── locals: []
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (24,6)-(24,13))
+ │ └── body: (length: 1)
+ │ └── @ NextNode (location: (24,6)-(24,13))
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (24,10)-(24,13))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ ParenthesesNode (location: (24,10)-(24,13))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (24,11)-(24,12))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (24,11)-(24,12))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── opening_loc: (24,10)-(24,11) = "("
+ │ │ └── closing_loc: (24,12)-(24,13) = ")"
+ │ └── keyword_loc: (24,6)-(24,10) = "next"
+ ├── opening_loc: (24,4)-(24,5) = "{"
+ └── closing_loc: (24,14)-(24,15) = "}"
diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt
index d987c49617384c..57cafde5a6333b 100644
--- a/test/prism/snapshots/rescue.txt
+++ b/test/prism/snapshots/rescue.txt
@@ -33,22 +33,58 @@
│ ├── keyword_loc: (3,4)-(3,10) = "rescue"
│ └── rescue_expression:
│ @ NilNode (location: (4,0)-(4,3))
- ├── @ RescueModifierNode (location: (6,0)-(6,16))
- │ ├── expression:
- │ │ @ BreakNode (location: (6,0)-(6,5))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (6,0)-(6,5) = "break"
- │ ├── keyword_loc: (6,6)-(6,12) = "rescue"
- │ └── rescue_expression:
- │ @ NilNode (location: (6,13)-(6,16))
- ├── @ RescueModifierNode (location: (8,0)-(8,15))
- │ ├── expression:
- │ │ @ NextNode (location: (8,0)-(8,4))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (8,0)-(8,4) = "next"
- │ ├── keyword_loc: (8,5)-(8,11) = "rescue"
- │ └── rescue_expression:
- │ @ NilNode (location: (8,12)-(8,15))
+ ├── @ CallNode (location: (6,0)-(6,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (6,0)-(6,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (6,4)-(6,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (6,6)-(6,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (6,6)-(6,22))
+ │ │ ├── expression:
+ │ │ │ @ BreakNode (location: (6,6)-(6,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (6,6)-(6,11) = "break"
+ │ │ ├── keyword_loc: (6,12)-(6,18) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (6,19)-(6,22))
+ │ ├── opening_loc: (6,4)-(6,5) = "{"
+ │ └── closing_loc: (6,23)-(6,24) = "}"
+ ├── @ CallNode (location: (8,0)-(8,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (8,0)-(8,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (8,4)-(8,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,6)-(8,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ RescueModifierNode (location: (8,6)-(8,21))
+ │ │ ├── expression:
+ │ │ │ @ NextNode (location: (8,6)-(8,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (8,6)-(8,10) = "next"
+ │ │ ├── keyword_loc: (8,11)-(8,17) = "rescue"
+ │ │ └── rescue_expression:
+ │ │ @ NilNode (location: (8,18)-(8,21))
+ │ ├── opening_loc: (8,4)-(8,5) = "{"
+ │ └── closing_loc: (8,22)-(8,23) = "}"
├── @ RescueModifierNode (location: (10,0)-(10,17))
│ ├── expression:
│ │ @ ReturnNode (location: (10,0)-(10,6))
diff --git a/test/prism/snapshots/seattlerb/block_break.txt b/test/prism/snapshots/seattlerb/block_break.txt
deleted file mode 100644
index ce61b2f0d12161..00000000000000
--- a/test/prism/snapshots/seattlerb/block_break.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,26))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,26))
- └── body: (length: 1)
- └── @ BreakNode (location: (1,0)-(1,26))
- ├── arguments:
- │ @ ArgumentsNode (location: (1,6)-(1,26))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ CallNode (location: (1,6)-(1,26))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :foo
- │ ├── message_loc: (1,6)-(1,9) = "foo"
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,10)-(1,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (1,10)-(1,13))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :arg
- │ │ ├── message_loc: (1,10)-(1,13) = "arg"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── closing_loc: ∅
- │ └── block:
- │ @ BlockNode (location: (1,14)-(1,26))
- │ ├── locals: [:bar]
- │ ├── parameters:
- │ │ @ BlockParametersNode (location: (1,17)-(1,22))
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (1,18)-(1,21))
- │ │ │ ├── requireds: (length: 1)
- │ │ │ │ └── @ RequiredParameterNode (location: (1,18)-(1,21))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ └── name: :bar
- │ │ │ ├── optionals: (length: 0)
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── locals: (length: 0)
- │ │ ├── opening_loc: (1,17)-(1,18) = "|"
- │ │ └── closing_loc: (1,21)-(1,22) = "|"
- │ ├── body: ∅
- │ ├── opening_loc: (1,14)-(1,16) = "do"
- │ └── closing_loc: (1,23)-(1,26) = "end"
- └── keyword_loc: (1,0)-(1,5) = "break"
diff --git a/test/prism/snapshots/seattlerb/block_next.txt b/test/prism/snapshots/seattlerb/block_next.txt
deleted file mode 100644
index 71f2deb2ddf8d5..00000000000000
--- a/test/prism/snapshots/seattlerb/block_next.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,25))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,25))
- └── body: (length: 1)
- └── @ NextNode (location: (1,0)-(1,25))
- ├── arguments:
- │ @ ArgumentsNode (location: (1,5)-(1,25))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ CallNode (location: (1,5)-(1,25))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :foo
- │ ├── message_loc: (1,5)-(1,8) = "foo"
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,9)-(1,12))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (1,9)-(1,12))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :arg
- │ │ ├── message_loc: (1,9)-(1,12) = "arg"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── closing_loc: ∅
- │ └── block:
- │ @ BlockNode (location: (1,13)-(1,25))
- │ ├── locals: [:bar]
- │ ├── parameters:
- │ │ @ BlockParametersNode (location: (1,16)-(1,21))
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (1,17)-(1,20))
- │ │ │ ├── requireds: (length: 1)
- │ │ │ │ └── @ RequiredParameterNode (location: (1,17)-(1,20))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ └── name: :bar
- │ │ │ ├── optionals: (length: 0)
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── locals: (length: 0)
- │ │ ├── opening_loc: (1,16)-(1,17) = "|"
- │ │ └── closing_loc: (1,20)-(1,21) = "|"
- │ ├── body: ∅
- │ ├── opening_loc: (1,13)-(1,15) = "do"
- │ └── closing_loc: (1,22)-(1,25) = "end"
- └── keyword_loc: (1,0)-(1,4) = "next"
diff --git a/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt b/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
index 31cc0941ee25eb..c8a990c67255a6 100644
--- a/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
+++ b/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
@@ -5,7 +5,7 @@
└── body: (length: 1)
└── @ StringNode (location: (1,0)-(2,1))
├── flags: ∅
- ├── opening_loc: (1,0)-(1,3) = "%q{"
+ ├── opening_loc: (1,0)-(1,3) = "%Q{"
├── content_loc: (1,3)-(2,0) = " \\\n"
├── closing_loc: (2,0)-(2,1) = "}"
- └── unescaped: " \\\n"
+ └── unescaped: " "
diff --git a/test/prism/snapshots/unless.txt b/test/prism/snapshots/unless.txt
index df16f90fd8efa0..6611ffe63d8775 100644
--- a/test/prism/snapshots/unless.txt
+++ b/test/prism/snapshots/unless.txt
@@ -51,32 +51,68 @@
│ │ └── value: 1
│ ├── consequent: ∅
│ └── end_keyword_loc: ∅
- ├── @ UnlessNode (location: (8,0)-(8,17))
- │ ├── keyword_loc: (8,6)-(8,12) = "unless"
- │ ├── predicate:
- │ │ @ TrueNode (location: (8,13)-(8,17))
- │ ├── then_keyword_loc: ∅
- │ ├── statements:
- │ │ @ StatementsNode (location: (8,0)-(8,5))
- │ │ └── body: (length: 1)
- │ │ └── @ BreakNode (location: (8,0)-(8,5))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (8,0)-(8,5) = "break"
- │ ├── consequent: ∅
- │ └── end_keyword_loc: ∅
- ├── @ UnlessNode (location: (10,0)-(10,16))
- │ ├── keyword_loc: (10,5)-(10,11) = "unless"
- │ ├── predicate:
- │ │ @ TrueNode (location: (10,12)-(10,16))
- │ ├── then_keyword_loc: ∅
- │ ├── statements:
- │ │ @ StatementsNode (location: (10,0)-(10,4))
- │ │ └── body: (length: 1)
- │ │ └── @ NextNode (location: (10,0)-(10,4))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (10,0)-(10,4) = "next"
- │ ├── consequent: ∅
- │ └── end_keyword_loc: ∅
+ ├── @ CallNode (location: (8,0)-(8,25))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (8,0)-(8,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (8,4)-(8,25))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (8,6)-(8,23))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UnlessNode (location: (8,6)-(8,23))
+ │ │ ├── keyword_loc: (8,12)-(8,18) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (8,19)-(8,23))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (8,6)-(8,11))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (8,6)-(8,11))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (8,6)-(8,11) = "break"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (8,4)-(8,5) = "{"
+ │ └── closing_loc: (8,24)-(8,25) = "}"
+ ├── @ CallNode (location: (10,0)-(10,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (10,0)-(10,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (10,4)-(10,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (10,6)-(10,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UnlessNode (location: (10,6)-(10,22))
+ │ │ ├── keyword_loc: (10,11)-(10,17) = "unless"
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (10,18)-(10,22))
+ │ │ ├── then_keyword_loc: ∅
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (10,6)-(10,10))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ NextNode (location: (10,6)-(10,10))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (10,6)-(10,10) = "next"
+ │ │ ├── consequent: ∅
+ │ │ └── end_keyword_loc: ∅
+ │ ├── opening_loc: (10,4)-(10,5) = "{"
+ │ └── closing_loc: (10,23)-(10,24) = "}"
├── @ UnlessNode (location: (12,0)-(12,18))
│ ├── keyword_loc: (12,7)-(12,13) = "unless"
│ ├── predicate:
diff --git a/test/prism/snapshots/unparser/corpus/literal/control.txt b/test/prism/snapshots/unparser/corpus/literal/control.txt
deleted file mode 100644
index 9bb303fed42ca9..00000000000000
--- a/test/prism/snapshots/unparser/corpus/literal/control.txt
+++ /dev/null
@@ -1,150 +0,0 @@
-@ ProgramNode (location: (1,0)-(15,3))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(15,3))
- └── body: (length: 11)
- ├── @ NextNode (location: (1,0)-(1,4))
- │ ├── arguments: ∅
- │ └── keyword_loc: (1,0)-(1,4) = "next"
- ├── @ ReturnNode (location: (2,0)-(2,6))
- │ ├── keyword_loc: (2,0)-(2,6) = "return"
- │ └── arguments: ∅
- ├── @ BreakNode (location: (3,0)-(3,5))
- │ ├── arguments: ∅
- │ └── keyword_loc: (3,0)-(3,5) = "break"
- ├── @ RetryNode (location: (4,0)-(4,5))
- ├── @ RedoNode (location: (5,0)-(5,4))
- ├── @ ReturnNode (location: (6,0)-(6,8))
- │ ├── keyword_loc: (6,0)-(6,6) = "return"
- │ └── arguments:
- │ @ ArgumentsNode (location: (6,7)-(6,8))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ IntegerNode (location: (6,7)-(6,8))
- │ ├── flags: decimal
- │ └── value: 1
- ├── @ ReturnNode (location: (7,0)-(7,11))
- │ ├── keyword_loc: (7,0)-(7,6) = "return"
- │ └── arguments:
- │ @ ArgumentsNode (location: (7,7)-(7,11))
- │ ├── flags: ∅
- │ └── arguments: (length: 2)
- │ ├── @ IntegerNode (location: (7,7)-(7,8))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ └── @ IntegerNode (location: (7,10)-(7,11))
- │ ├── flags: decimal
- │ └── value: 2
- ├── @ ReturnNode (location: (8,0)-(8,19))
- │ ├── keyword_loc: (8,0)-(8,6) = "return"
- │ └── arguments:
- │ @ ArgumentsNode (location: (8,7)-(8,19))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ IfNode (location: (8,7)-(8,19))
- │ ├── if_keyword_loc: ∅
- │ ├── predicate:
- │ │ @ TrueNode (location: (8,7)-(8,11))
- │ ├── then_keyword_loc: (8,12)-(8,13) = "?"
- │ ├── statements:
- │ │ @ StatementsNode (location: (8,14)-(8,15))
- │ │ └── body: (length: 1)
- │ │ └── @ IntegerNode (location: (8,14)-(8,15))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ ├── consequent:
- │ │ @ ElseNode (location: (8,16)-(8,19))
- │ │ ├── else_keyword_loc: (8,16)-(8,17) = ":"
- │ │ ├── statements:
- │ │ │ @ StatementsNode (location: (8,18)-(8,19))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (8,18)-(8,19))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── end_keyword_loc: ∅
- │ └── end_keyword_loc: ∅
- ├── @ BreakNode (location: (9,0)-(9,18))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (9,6)-(9,18))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ IfNode (location: (9,6)-(9,18))
- │ │ ├── if_keyword_loc: ∅
- │ │ ├── predicate:
- │ │ │ @ TrueNode (location: (9,6)-(9,10))
- │ │ ├── then_keyword_loc: (9,11)-(9,12) = "?"
- │ │ ├── statements:
- │ │ │ @ StatementsNode (location: (9,13)-(9,14))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (9,13)-(9,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── consequent:
- │ │ │ @ ElseNode (location: (9,15)-(9,18))
- │ │ │ ├── else_keyword_loc: (9,15)-(9,16) = ":"
- │ │ │ ├── statements:
- │ │ │ │ @ StatementsNode (location: (9,17)-(9,18))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (9,17)-(9,18))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ └── end_keyword_loc: ∅
- │ │ └── end_keyword_loc: ∅
- │ └── keyword_loc: (9,0)-(9,5) = "break"
- ├── @ NextNode (location: (10,0)-(10,17))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (10,5)-(10,17))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ IfNode (location: (10,5)-(10,17))
- │ │ ├── if_keyword_loc: ∅
- │ │ ├── predicate:
- │ │ │ @ TrueNode (location: (10,5)-(10,9))
- │ │ ├── then_keyword_loc: (10,10)-(10,11) = "?"
- │ │ ├── statements:
- │ │ │ @ StatementsNode (location: (10,12)-(10,13))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ IntegerNode (location: (10,12)-(10,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ ├── consequent:
- │ │ │ @ ElseNode (location: (10,14)-(10,17))
- │ │ │ ├── else_keyword_loc: (10,14)-(10,15) = ":"
- │ │ │ ├── statements:
- │ │ │ │ @ StatementsNode (location: (10,16)-(10,17))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ IntegerNode (location: (10,16)-(10,17))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 2
- │ │ │ └── end_keyword_loc: ∅
- │ │ └── end_keyword_loc: ∅
- │ └── keyword_loc: (10,0)-(10,4) = "next"
- └── @ ReturnNode (location: (11,0)-(15,3))
- ├── keyword_loc: (11,0)-(11,6) = "return"
- └── arguments:
- @ ArgumentsNode (location: (11,7)-(15,3))
- ├── flags: ∅
- └── arguments: (length: 2)
- ├── @ TrueNode (location: (11,7)-(11,11))
- └── @ IfNode (location: (11,13)-(15,3))
- ├── if_keyword_loc: (11,13)-(11,15) = "if"
- ├── predicate:
- │ @ TrueNode (location: (11,16)-(11,20))
- ├── then_keyword_loc: ∅
- ├── statements:
- │ @ StatementsNode (location: (12,2)-(12,3))
- │ └── body: (length: 1)
- │ └── @ IntegerNode (location: (12,2)-(12,3))
- │ ├── flags: decimal
- │ └── value: 1
- ├── consequent:
- │ @ ElseNode (location: (13,0)-(15,3))
- │ ├── else_keyword_loc: (13,0)-(13,4) = "else"
- │ ├── statements:
- │ │ @ StatementsNode (location: (14,2)-(14,3))
- │ │ └── body: (length: 1)
- │ │ └── @ IntegerNode (location: (14,2)-(14,3))
- │ │ ├── flags: decimal
- │ │ └── value: 2
- │ └── end_keyword_loc: (15,0)-(15,3) = "end"
- └── end_keyword_loc: (15,0)-(15,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/flipflop.txt b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt
index 2794e0534f22a8..2d9f669e6f14dc 100644
--- a/test/prism/snapshots/unparser/corpus/literal/flipflop.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/flipflop.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (1,0)-(6,3))
+@ ProgramNode (location: (1,0)-(10,3))
├── locals: []
└── statements:
- @ StatementsNode (location: (1,0)-(6,3))
- └── body: (length: 2)
+ @ StatementsNode (location: (1,0)-(10,3))
+ └── body: (length: 4)
├── @ IfNode (location: (1,0)-(3,3))
│ ├── if_keyword_loc: (1,0)-(1,2) = "if"
│ ├── predicate:
@@ -97,97 +97,141 @@
│ │ └── block: ∅
│ ├── consequent: ∅
│ └── end_keyword_loc: (3,0)-(3,3) = "end"
- └── @ IfNode (location: (4,0)-(6,3))
- ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ ├── @ IfNode (location: (4,0)-(6,3))
+ │ ├── if_keyword_loc: (4,0)-(4,2) = "if"
+ │ ├── predicate:
+ │ │ @ ParenthesesNode (location: (4,3)-(4,24))
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (4,4)-(4,23))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ FlipFlopNode (location: (4,4)-(4,23))
+ │ │ │ ├── flags: exclude_end
+ │ │ │ ├── left:
+ │ │ │ │ @ ParenthesesNode (location: (4,4)-(4,12))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (4,5)-(4,11))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (4,5)-(4,11))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (4,5)-(4,6))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (4,5)-(4,6) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (4,7)-(4,9) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (4,10)-(4,11))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (4,10)-(4,11))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (4,4)-(4,5) = "("
+ │ │ │ │ └── closing_loc: (4,11)-(4,12) = ")"
+ │ │ │ ├── right:
+ │ │ │ │ @ ParenthesesNode (location: (4,15)-(4,23))
+ │ │ │ │ ├── body:
+ │ │ │ │ │ @ StatementsNode (location: (4,16)-(4,22))
+ │ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ │ └── @ CallNode (location: (4,16)-(4,22))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── receiver:
+ │ │ │ │ │ │ @ CallNode (location: (4,16)-(4,17))
+ │ │ │ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ │ ├── name: :i
+ │ │ │ │ │ │ ├── message_loc: (4,16)-(4,17) = "i"
+ │ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ │ └── block: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :==
+ │ │ │ │ │ ├── message_loc: (4,18)-(4,20) = "=="
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments:
+ │ │ │ │ │ │ @ ArgumentsNode (location: (4,21)-(4,22))
+ │ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ │ └── arguments: (length: 1)
+ │ │ │ │ │ │ └── @ IntegerNode (location: (4,21)-(4,22))
+ │ │ │ │ │ │ ├── flags: decimal
+ │ │ │ │ │ │ └── value: 4
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block: ∅
+ │ │ │ │ ├── opening_loc: (4,15)-(4,16) = "("
+ │ │ │ │ └── closing_loc: (4,22)-(4,23) = ")"
+ │ │ │ └── operator_loc: (4,12)-(4,15) = "..."
+ │ │ ├── opening_loc: (4,3)-(4,4) = "("
+ │ │ └── closing_loc: (4,23)-(4,24) = ")"
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements:
+ │ │ @ StatementsNode (location: (5,2)-(5,5))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (5,2)-(5,5))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (5,2)-(5,5) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (6,0)-(6,3) = "end"
+ ├── @ IfNode (location: (7,0)-(8,3))
+ │ ├── if_keyword_loc: (7,0)-(7,2) = "if"
+ │ ├── predicate:
+ │ │ @ FlipFlopNode (location: (7,3)-(7,8))
+ │ │ ├── flags: ∅
+ │ │ ├── left: ∅
+ │ │ ├── right:
+ │ │ │ @ CallNode (location: (7,5)-(7,8))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :foo
+ │ │ │ ├── message_loc: (7,5)-(7,8) = "foo"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ └── operator_loc: (7,3)-(7,5) = ".."
+ │ ├── then_keyword_loc: ∅
+ │ ├── statements: ∅
+ │ ├── consequent: ∅
+ │ └── end_keyword_loc: (8,0)-(8,3) = "end"
+ └── @ IfNode (location: (9,0)-(10,3))
+ ├── if_keyword_loc: (9,0)-(9,2) = "if"
├── predicate:
- │ @ ParenthesesNode (location: (4,3)-(4,24))
- │ ├── body:
- │ │ @ StatementsNode (location: (4,4)-(4,23))
- │ │ └── body: (length: 1)
- │ │ └── @ FlipFlopNode (location: (4,4)-(4,23))
- │ │ ├── flags: exclude_end
- │ │ ├── left:
- │ │ │ @ ParenthesesNode (location: (4,4)-(4,12))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (4,5)-(4,11))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ CallNode (location: (4,5)-(4,11))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ ├── receiver:
- │ │ │ │ │ @ CallNode (location: (4,5)-(4,6))
- │ │ │ │ │ ├── flags: variable_call, ignore_visibility
- │ │ │ │ │ ├── receiver: ∅
- │ │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ │ ├── name: :i
- │ │ │ │ │ ├── message_loc: (4,5)-(4,6) = "i"
- │ │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ │ ├── arguments: ∅
- │ │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ │ └── block: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :==
- │ │ │ │ ├── message_loc: (4,7)-(4,9) = "=="
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments:
- │ │ │ │ │ @ ArgumentsNode (location: (4,10)-(4,11))
- │ │ │ │ │ ├── flags: ∅
- │ │ │ │ │ └── arguments: (length: 1)
- │ │ │ │ │ └── @ IntegerNode (location: (4,10)-(4,11))
- │ │ │ │ │ ├── flags: decimal
- │ │ │ │ │ └── value: 4
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block: ∅
- │ │ │ ├── opening_loc: (4,4)-(4,5) = "("
- │ │ │ └── closing_loc: (4,11)-(4,12) = ")"
- │ │ ├── right:
- │ │ │ @ ParenthesesNode (location: (4,15)-(4,23))
- │ │ │ ├── body:
- │ │ │ │ @ StatementsNode (location: (4,16)-(4,22))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ CallNode (location: (4,16)-(4,22))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ ├── receiver:
- │ │ │ │ │ @ CallNode (location: (4,16)-(4,17))
- │ │ │ │ │ ├── flags: variable_call, ignore_visibility
- │ │ │ │ │ ├── receiver: ∅
- │ │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ │ ├── name: :i
- │ │ │ │ │ ├── message_loc: (4,16)-(4,17) = "i"
- │ │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ │ ├── arguments: ∅
- │ │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ │ └── block: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :==
- │ │ │ │ ├── message_loc: (4,18)-(4,20) = "=="
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments:
- │ │ │ │ │ @ ArgumentsNode (location: (4,21)-(4,22))
- │ │ │ │ │ ├── flags: ∅
- │ │ │ │ │ └── arguments: (length: 1)
- │ │ │ │ │ └── @ IntegerNode (location: (4,21)-(4,22))
- │ │ │ │ │ ├── flags: decimal
- │ │ │ │ │ └── value: 4
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block: ∅
- │ │ │ ├── opening_loc: (4,15)-(4,16) = "("
- │ │ │ └── closing_loc: (4,22)-(4,23) = ")"
- │ │ └── operator_loc: (4,12)-(4,15) = "..."
- │ ├── opening_loc: (4,3)-(4,4) = "("
- │ └── closing_loc: (4,23)-(4,24) = ")"
+ │ @ FlipFlopNode (location: (9,3)-(9,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (9,3)-(9,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (9,3)-(9,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (9,6)-(9,8) = ".."
├── then_keyword_loc: ∅
- ├── statements:
- │ @ StatementsNode (location: (5,2)-(5,5))
- │ └── body: (length: 1)
- │ └── @ CallNode (location: (5,2)-(5,5))
- │ ├── flags: variable_call, ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :foo
- │ ├── message_loc: (5,2)-(5,5) = "foo"
- │ ├── opening_loc: ∅
- │ ├── arguments: ∅
- │ ├── closing_loc: ∅
- │ └── block: ∅
+ ├── statements: ∅
├── consequent: ∅
- └── end_keyword_loc: (6,0)-(6,3) = "end"
+ └── end_keyword_loc: (10,0)-(10,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
index ee8dd7f53d1920..e72be6d8b71208 100644
--- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (1,0)-(7,3))
+@ ProgramNode (location: (1,0)-(11,3))
├── locals: []
└── statements:
- @ StatementsNode (location: (1,0)-(7,3))
- └── body: (length: 2)
+ @ StatementsNode (location: (1,0)-(11,3))
+ └── body: (length: 3)
├── @ DefNode (location: (1,0)-(3,3))
│ ├── name: :foo
│ ├── name_loc: (1,4)-(1,7) = "foo"
@@ -56,53 +56,101 @@
│ ├── rparen_loc: (1,20)-(1,21) = ")"
│ ├── equal_loc: ∅
│ └── end_keyword_loc: (3,0)-(3,3) = "end"
- └── @ DefNode (location: (5,0)-(7,3))
+ ├── @ DefNode (location: (5,0)-(7,3))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,4)-(5,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters:
+ │ │ @ ParametersNode (location: (5,8)-(5,19))
+ │ │ ├── requireds: (length: 1)
+ │ │ │ └── @ RequiredParameterNode (location: (5,8)-(5,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── name: :argument
+ │ │ ├── optionals: (length: 0)
+ │ │ ├── rest:
+ │ │ │ @ RestParameterNode (location: (5,18)-(5,19))
+ │ │ │ ├── flags: ∅
+ │ │ │ ├── name: ∅
+ │ │ │ ├── name_loc: ∅
+ │ │ │ └── operator_loc: (5,18)-(5,19) = "*"
+ │ │ ├── posts: (length: 0)
+ │ │ ├── keywords: (length: 0)
+ │ │ ├── keyword_rest: ∅
+ │ │ └── block: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (6,2)-(6,18))
+ │ │ └── body: (length: 1)
+ │ │ └── @ CallNode (location: (6,2)-(6,18))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (6,2)-(6,5) = "bar"
+ │ │ ├── opening_loc: (6,5)-(6,6) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (6,6)-(6,17))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 2)
+ │ │ │ ├── @ LocalVariableReadNode (location: (6,6)-(6,14))
+ │ │ │ │ ├── name: :argument
+ │ │ │ │ └── depth: 0
+ │ │ │ └── @ SplatNode (location: (6,16)-(6,17))
+ │ │ │ ├── operator_loc: (6,16)-(6,17) = "*"
+ │ │ │ └── expression: ∅
+ │ │ ├── closing_loc: (6,17)-(6,18) = ")"
+ │ │ └── block: ∅
+ │ ├── locals: [:argument]
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: (5,7)-(5,8) = "("
+ │ ├── rparen_loc: (5,19)-(5,20) = ")"
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (7,0)-(7,3) = "end"
+ └── @ DefNode (location: (9,0)-(11,3))
├── name: :foo
- ├── name_loc: (5,4)-(5,7) = "foo"
+ ├── name_loc: (9,4)-(9,7) = "foo"
├── receiver: ∅
├── parameters:
- │ @ ParametersNode (location: (5,8)-(5,19))
- │ ├── requireds: (length: 1)
- │ │ └── @ RequiredParameterNode (location: (5,8)-(5,16))
- │ │ ├── flags: ∅
- │ │ └── name: :argument
+ │ @ ParametersNode (location: (9,8)-(9,10))
+ │ ├── requireds: (length: 0)
│ ├── optionals: (length: 0)
- │ ├── rest:
- │ │ @ RestParameterNode (location: (5,18)-(5,19))
+ │ ├── rest: ∅
+ │ ├── posts: (length: 0)
+ │ ├── keywords: (length: 0)
+ │ ├── keyword_rest:
+ │ │ @ KeywordRestParameterNode (location: (9,8)-(9,10))
│ │ ├── flags: ∅
│ │ ├── name: ∅
│ │ ├── name_loc: ∅
- │ │ └── operator_loc: (5,18)-(5,19) = "*"
- │ ├── posts: (length: 0)
- │ ├── keywords: (length: 0)
- │ ├── keyword_rest: ∅
+ │ │ └── operator_loc: (9,8)-(9,10) = "**"
│ └── block: ∅
├── body:
- │ @ StatementsNode (location: (6,2)-(6,18))
+ │ @ StatementsNode (location: (10,2)-(10,20))
│ └── body: (length: 1)
- │ └── @ CallNode (location: (6,2)-(6,18))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :bar
- │ ├── message_loc: (6,2)-(6,5) = "bar"
- │ ├── opening_loc: (6,5)-(6,6) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (6,6)-(6,17))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 2)
- │ │ ├── @ LocalVariableReadNode (location: (6,6)-(6,14))
- │ │ │ ├── name: :argument
- │ │ │ └── depth: 0
- │ │ └── @ SplatNode (location: (6,16)-(6,17))
- │ │ ├── operator_loc: (6,16)-(6,17) = "*"
- │ │ └── expression: ∅
- │ ├── closing_loc: (6,17)-(6,18) = ")"
- │ └── block: ∅
- ├── locals: [:argument]
- ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ └── @ HashNode (location: (10,2)-(10,20))
+ │ ├── opening_loc: (10,2)-(10,3) = "{"
+ │ ├── elements: (length: 2)
+ │ │ ├── @ AssocNode (location: (10,4)-(10,14))
+ │ │ │ ├── key:
+ │ │ │ │ @ SymbolNode (location: (10,4)-(10,12))
+ │ │ │ │ ├── flags: forced_us_ascii_encoding
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── value_loc: (10,4)-(10,11) = "default"
+ │ │ │ │ ├── closing_loc: (10,11)-(10,12) = ":"
+ │ │ │ │ └── unescaped: "default"
+ │ │ │ ├── value:
+ │ │ │ │ @ IntegerNode (location: (10,13)-(10,14))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 1
+ │ │ │ └── operator_loc: ∅
+ │ │ └── @ AssocSplatNode (location: (10,16)-(10,18))
+ │ │ ├── value: ∅
+ │ │ └── operator_loc: (10,16)-(10,18) = "**"
+ │ └── closing_loc: (10,19)-(10,20) = "}"
+ ├── locals: []
+ ├── def_keyword_loc: (9,0)-(9,3) = "def"
├── operator_loc: ∅
- ├── lparen_loc: (5,7)-(5,8) = "("
- ├── rparen_loc: (5,19)-(5,20) = ")"
+ ├── lparen_loc: (9,7)-(9,8) = "("
+ ├── rparen_loc: (9,10)-(9,11) = ")"
├── equal_loc: ∅
- └── end_keyword_loc: (7,0)-(7,3) = "end"
+ └── end_keyword_loc: (11,0)-(11,3) = "end"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
new file mode 100644
index 00000000000000..e100dd8ecb7fe3
--- /dev/null
+++ b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
@@ -0,0 +1,69 @@
+@ ProgramNode (location: (1,0)-(1,25))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,25))
+ └── body: (length: 1)
+ └── @ IndexOperatorWriteNode (location: (1,0)-(1,25))
+ ├── flags: ∅
+ ├── receiver:
+ │ @ CallNode (location: (1,0)-(1,1))
+ │ ├── flags: variable_call, ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :y
+ │ ├── message_loc: (1,0)-(1,1) = "y"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── call_operator_loc: ∅
+ ├── opening_loc: (1,1)-(1,2) = "["
+ ├── arguments:
+ │ @ ArgumentsNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ └── arguments: (length: 1)
+ │ └── @ InterpolatedStringNode (location: (1,2)-(1,11))
+ │ ├── flags: ∅
+ │ ├── opening_loc: (1,2)-(1,3) = "\""
+ │ ├── parts: (length: 2)
+ │ │ ├── @ EmbeddedStatementsNode (location: (1,3)-(1,8))
+ │ │ │ ├── opening_loc: (1,3)-(1,5) = "\#{"
+ │ │ │ ├── statements:
+ │ │ │ │ @ StatementsNode (location: (1,5)-(1,7))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ IntegerNode (location: (1,5)-(1,7))
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ └── value: 42
+ │ │ │ └── closing_loc: (1,7)-(1,8) = "}"
+ │ │ └── @ StringNode (location: (1,8)-(1,10))
+ │ │ ├── flags: frozen
+ │ │ ├── opening_loc: ∅
+ │ │ ├── content_loc: (1,8)-(1,10) = "\\n"
+ │ │ ├── closing_loc: ∅
+ │ │ └── unescaped: "\n"
+ │ └── closing_loc: (1,10)-(1,11) = "\""
+ ├── closing_loc: (1,11)-(1,12) = "]"
+ ├── block: ∅
+ ├── operator: :+
+ ├── operator_loc: (1,13)-(1,15) = "+="
+ └── value:
+ @ InterpolatedStringNode (location: (1,16)-(1,25))
+ ├── flags: ∅
+ ├── opening_loc: (1,16)-(1,17) = "\""
+ ├── parts: (length: 2)
+ │ ├── @ EmbeddedStatementsNode (location: (1,17)-(1,22))
+ │ │ ├── opening_loc: (1,17)-(1,19) = "\#{"
+ │ │ ├── statements:
+ │ │ │ @ StatementsNode (location: (1,19)-(1,21))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,19)-(1,21))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 42
+ │ │ └── closing_loc: (1,21)-(1,22) = "}"
+ │ └── @ StringNode (location: (1,22)-(1,24))
+ │ ├── flags: frozen
+ │ ├── opening_loc: ∅
+ │ ├── content_loc: (1,22)-(1,24) = "\\n"
+ │ ├── closing_loc: ∅
+ │ └── unescaped: "\n"
+ └── closing_loc: (1,24)-(1,25) = "\""
diff --git a/test/prism/snapshots/until.txt b/test/prism/snapshots/until.txt
index fc7cb92c91a0be..d45e39644bb02e 100644
--- a/test/prism/snapshots/until.txt
+++ b/test/prism/snapshots/until.txt
@@ -27,30 +27,66 @@
│ └── @ IntegerNode (location: (3,0)-(3,1))
│ ├── flags: decimal
│ └── value: 1
- ├── @ UntilNode (location: (5,0)-(5,16))
- │ ├── flags: ∅
- │ ├── keyword_loc: (5,6)-(5,11) = "until"
+ ├── @ CallNode (location: (5,0)-(5,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
│ ├── closing_loc: ∅
- │ ├── predicate:
- │ │ @ TrueNode (location: (5,12)-(5,16))
- │ └── statements:
- │ @ StatementsNode (location: (5,0)-(5,5))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (5,0)-(5,5))
- │ ├── arguments: ∅
- │ └── keyword_loc: (5,0)-(5,5) = "break"
- ├── @ UntilNode (location: (7,0)-(7,15))
- │ ├── flags: ∅
- │ ├── keyword_loc: (7,5)-(7,10) = "until"
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UntilNode (location: (5,6)-(5,22))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (5,12)-(5,17) = "until"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (5,18)-(5,22))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,6)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,23)-(5,24) = "}"
+ ├── @ CallNode (location: (7,0)-(7,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
│ ├── closing_loc: ∅
- │ ├── predicate:
- │ │ @ TrueNode (location: (7,11)-(7,15))
- │ └── statements:
- │ @ StatementsNode (location: (7,0)-(7,4))
- │ └── body: (length: 1)
- │ └── @ NextNode (location: (7,0)-(7,4))
- │ ├── arguments: ∅
- │ └── keyword_loc: (7,0)-(7,4) = "next"
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(7,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(7,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ UntilNode (location: (7,6)-(7,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (7,11)-(7,16) = "until"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (7,17)-(7,21))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (7,6)-(7,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(7,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (7,22)-(7,23) = "}"
├── @ UntilNode (location: (9,0)-(9,17))
│ ├── flags: ∅
│ ├── keyword_loc: (9,7)-(9,12) = "until"
diff --git a/test/prism/snapshots/while.txt b/test/prism/snapshots/while.txt
index 9f191647984458..36fab4996024b3 100644
--- a/test/prism/snapshots/while.txt
+++ b/test/prism/snapshots/while.txt
@@ -27,30 +27,66 @@
│ └── @ IntegerNode (location: (3,0)-(3,1))
│ ├── flags: decimal
│ └── value: 1
- ├── @ WhileNode (location: (5,0)-(5,16))
- │ ├── flags: ∅
- │ ├── keyword_loc: (5,6)-(5,11) = "while"
+ ├── @ CallNode (location: (5,0)-(5,24))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (5,0)-(5,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
│ ├── closing_loc: ∅
- │ ├── predicate:
- │ │ @ TrueNode (location: (5,12)-(5,16))
- │ └── statements:
- │ @ StatementsNode (location: (5,0)-(5,5))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (5,0)-(5,5))
- │ ├── arguments: ∅
- │ └── keyword_loc: (5,0)-(5,5) = "break"
- ├── @ WhileNode (location: (7,0)-(7,15))
- │ ├── flags: ∅
- │ ├── keyword_loc: (7,5)-(7,10) = "while"
+ │ └── block:
+ │ @ BlockNode (location: (5,4)-(5,24))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,6)-(5,22))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (5,6)-(5,22))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (5,12)-(5,17) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (5,18)-(5,22))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (5,6)-(5,11))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (5,6)-(5,11))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (5,6)-(5,11) = "break"
+ │ ├── opening_loc: (5,4)-(5,5) = "{"
+ │ └── closing_loc: (5,23)-(5,24) = "}"
+ ├── @ CallNode (location: (7,0)-(7,23))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (7,0)-(7,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
│ ├── closing_loc: ∅
- │ ├── predicate:
- │ │ @ TrueNode (location: (7,11)-(7,15))
- │ └── statements:
- │ @ StatementsNode (location: (7,0)-(7,4))
- │ └── body: (length: 1)
- │ └── @ NextNode (location: (7,0)-(7,4))
- │ ├── arguments: ∅
- │ └── keyword_loc: (7,0)-(7,4) = "next"
+ │ └── block:
+ │ @ BlockNode (location: (7,4)-(7,23))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (7,6)-(7,21))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (7,6)-(7,21))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (7,11)-(7,16) = "while"
+ │ │ ├── closing_loc: ∅
+ │ │ ├── predicate:
+ │ │ │ @ TrueNode (location: (7,17)-(7,21))
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (7,6)-(7,10))
+ │ │ └── body: (length: 1)
+ │ │ └── @ NextNode (location: (7,6)-(7,10))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (7,6)-(7,10) = "next"
+ │ ├── opening_loc: (7,4)-(7,5) = "{"
+ │ └── closing_loc: (7,22)-(7,23) = "}"
├── @ WhileNode (location: (9,0)-(9,17))
│ ├── flags: ∅
│ ├── keyword_loc: (9,7)-(9,12) = "while"
@@ -106,187 +142,259 @@
│ │ └── unescaped: "b"
│ ├── closing_loc: ∅
│ └── block: ∅
- ├── @ WhileNode (location: (13,0)-(13,50))
- │ ├── flags: ∅
- │ ├── keyword_loc: (13,0)-(13,5) = "while"
- │ ├── closing_loc: (13,47)-(13,50) = "end"
- │ ├── predicate:
- │ │ @ DefNode (location: (13,6)-(13,38))
- │ │ ├── name: :foo
- │ │ ├── name_loc: (13,15)-(13,18) = "foo"
- │ │ ├── receiver:
- │ │ │ @ SelfNode (location: (13,10)-(13,14))
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (13,19)-(13,33))
- │ │ │ ├── requireds: (length: 0)
- │ │ │ ├── optionals: (length: 1)
- │ │ │ │ └── @ OptionalParameterNode (location: (13,19)-(13,33))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ ├── name: :a
- │ │ │ │ ├── name_loc: (13,19)-(13,20) = "a"
- │ │ │ │ ├── operator_loc: (13,21)-(13,22) = "="
- │ │ │ │ └── value:
- │ │ │ │ @ CallNode (location: (13,23)-(13,33))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (13,23)-(13,26) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (13,27)-(13,33))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (13,27)-(13,29) = "do"
- │ │ │ │ └── closing_loc: (13,30)-(13,33) = "end"
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── body: ∅
- │ │ ├── locals: [:a]
- │ │ ├── def_keyword_loc: (13,6)-(13,9) = "def"
- │ │ ├── operator_loc: (13,14)-(13,15) = "."
- │ │ ├── lparen_loc: ∅
- │ │ ├── rparen_loc: ∅
- │ │ ├── equal_loc: ∅
- │ │ └── end_keyword_loc: (13,35)-(13,38) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (13,40)-(13,45))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (13,40)-(13,45))
- │ ├── arguments: ∅
- │ └── keyword_loc: (13,40)-(13,45) = "break"
- ├── @ WhileNode (location: (15,0)-(15,47))
- │ ├── flags: ∅
- │ ├── keyword_loc: (15,0)-(15,5) = "while"
- │ ├── closing_loc: (15,44)-(15,47) = "end"
- │ ├── predicate:
- │ │ @ ClassNode (location: (15,6)-(15,35))
- │ │ ├── locals: [:a]
- │ │ ├── class_keyword_loc: (15,6)-(15,11) = "class"
- │ │ ├── constant_path:
- │ │ │ @ ConstantReadNode (location: (15,12)-(15,15))
- │ │ │ └── name: :Foo
- │ │ ├── inheritance_operator_loc: ∅
- │ │ ├── superclass: ∅
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (15,16)-(15,30))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ LocalVariableWriteNode (location: (15,16)-(15,30))
- │ │ │ ├── name: :a
- │ │ │ ├── depth: 0
- │ │ │ ├── name_loc: (15,16)-(15,17) = "a"
- │ │ │ ├── value:
- │ │ │ │ @ CallNode (location: (15,20)-(15,30))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (15,20)-(15,23) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (15,24)-(15,30))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (15,24)-(15,26) = "do"
- │ │ │ │ └── closing_loc: (15,27)-(15,30) = "end"
- │ │ │ └── operator_loc: (15,18)-(15,19) = "="
- │ │ ├── end_keyword_loc: (15,32)-(15,35) = "end"
- │ │ └── name: :Foo
- │ └── statements:
- │ @ StatementsNode (location: (15,37)-(15,42))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (15,37)-(15,42))
- │ ├── arguments: ∅
- │ └── keyword_loc: (15,37)-(15,42) = "break"
- ├── @ WhileNode (location: (17,0)-(17,48))
- │ ├── flags: ∅
- │ ├── keyword_loc: (17,0)-(17,5) = "while"
- │ ├── closing_loc: (17,45)-(17,48) = "end"
- │ ├── predicate:
- │ │ @ SingletonClassNode (location: (17,6)-(17,36))
- │ │ ├── locals: []
- │ │ ├── class_keyword_loc: (17,6)-(17,11) = "class"
- │ │ ├── operator_loc: (17,12)-(17,14) = "<<"
- │ │ ├── expression:
- │ │ │ @ SelfNode (location: (17,15)-(17,19))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (17,21)-(17,31))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ CallNode (location: (17,21)-(17,31))
- │ │ │ ├── flags: ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :tap
- │ │ │ ├── message_loc: (17,21)-(17,24) = "tap"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block:
- │ │ │ @ BlockNode (location: (17,25)-(17,31))
- │ │ │ ├── locals: []
- │ │ │ ├── parameters: ∅
- │ │ │ ├── body: ∅
- │ │ │ ├── opening_loc: (17,25)-(17,27) = "do"
- │ │ │ └── closing_loc: (17,28)-(17,31) = "end"
- │ │ └── end_keyword_loc: (17,33)-(17,36) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (17,38)-(17,43))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (17,38)-(17,43))
- │ ├── arguments: ∅
- │ └── keyword_loc: (17,38)-(17,43) = "break"
- ├── @ WhileNode (location: (19,0)-(19,52))
- │ ├── flags: ∅
- │ ├── keyword_loc: (19,0)-(19,5) = "while"
- │ ├── closing_loc: (19,49)-(19,52) = "end"
- │ ├── predicate:
- │ │ @ SingletonClassNode (location: (19,6)-(19,40))
- │ │ ├── locals: [:a]
- │ │ ├── class_keyword_loc: (19,6)-(19,11) = "class"
- │ │ ├── operator_loc: (19,12)-(19,14) = "<<"
- │ │ ├── expression:
- │ │ │ @ SelfNode (location: (19,15)-(19,19))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (19,21)-(19,35))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ LocalVariableWriteNode (location: (19,21)-(19,35))
- │ │ │ ├── name: :a
- │ │ │ ├── depth: 0
- │ │ │ ├── name_loc: (19,21)-(19,22) = "a"
- │ │ │ ├── value:
- │ │ │ │ @ CallNode (location: (19,25)-(19,35))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (19,25)-(19,28) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (19,29)-(19,35))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (19,29)-(19,31) = "do"
- │ │ │ │ └── closing_loc: (19,32)-(19,35) = "end"
- │ │ │ └── operator_loc: (19,23)-(19,24) = "="
- │ │ └── end_keyword_loc: (19,37)-(19,40) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (19,42)-(19,47))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (19,42)-(19,47))
- │ ├── arguments: ∅
- │ └── keyword_loc: (19,42)-(19,47) = "break"
+ ├── @ CallNode (location: (13,0)-(13,58))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (13,0)-(13,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (13,4)-(13,58))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (13,6)-(13,56))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (13,6)-(13,56))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (13,6)-(13,11) = "while"
+ │ │ ├── closing_loc: (13,53)-(13,56) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ DefNode (location: (13,12)-(13,44))
+ │ │ │ ├── name: :foo
+ │ │ │ ├── name_loc: (13,21)-(13,24) = "foo"
+ │ │ │ ├── receiver:
+ │ │ │ │ @ SelfNode (location: (13,16)-(13,20))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (13,25)-(13,39))
+ │ │ │ │ ├── requireds: (length: 0)
+ │ │ │ │ ├── optionals: (length: 1)
+ │ │ │ │ │ └── @ OptionalParameterNode (location: (13,25)-(13,39))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ ├── name: :a
+ │ │ │ │ │ ├── name_loc: (13,25)-(13,26) = "a"
+ │ │ │ │ │ ├── operator_loc: (13,27)-(13,28) = "="
+ │ │ │ │ │ └── value:
+ │ │ │ │ │ @ CallNode (location: (13,29)-(13,39))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (13,29)-(13,32) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (13,33)-(13,39))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (13,33)-(13,35) = "do"
+ │ │ │ │ │ └── closing_loc: (13,36)-(13,39) = "end"
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── body: ∅
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── def_keyword_loc: (13,12)-(13,15) = "def"
+ │ │ │ ├── operator_loc: (13,20)-(13,21) = "."
+ │ │ │ ├── lparen_loc: ∅
+ │ │ │ ├── rparen_loc: ∅
+ │ │ │ ├── equal_loc: ∅
+ │ │ │ └── end_keyword_loc: (13,41)-(13,44) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (13,46)-(13,51))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (13,46)-(13,51))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (13,46)-(13,51) = "break"
+ │ ├── opening_loc: (13,4)-(13,5) = "{"
+ │ └── closing_loc: (13,57)-(13,58) = "}"
+ ├── @ CallNode (location: (15,0)-(15,55))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (15,0)-(15,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (15,4)-(15,55))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (15,6)-(15,53))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (15,6)-(15,53))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (15,6)-(15,11) = "while"
+ │ │ ├── closing_loc: (15,50)-(15,53) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ ClassNode (location: (15,12)-(15,41))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── class_keyword_loc: (15,12)-(15,17) = "class"
+ │ │ │ ├── constant_path:
+ │ │ │ │ @ ConstantReadNode (location: (15,18)-(15,21))
+ │ │ │ │ └── name: :Foo
+ │ │ │ ├── inheritance_operator_loc: ∅
+ │ │ │ ├── superclass: ∅
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (15,22)-(15,36))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableWriteNode (location: (15,22)-(15,36))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── depth: 0
+ │ │ │ │ ├── name_loc: (15,22)-(15,23) = "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (15,26)-(15,36))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (15,26)-(15,29) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (15,30)-(15,36))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (15,30)-(15,32) = "do"
+ │ │ │ │ │ └── closing_loc: (15,33)-(15,36) = "end"
+ │ │ │ │ └── operator_loc: (15,24)-(15,25) = "="
+ │ │ │ ├── end_keyword_loc: (15,38)-(15,41) = "end"
+ │ │ │ └── name: :Foo
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (15,43)-(15,48))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (15,43)-(15,48))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (15,43)-(15,48) = "break"
+ │ ├── opening_loc: (15,4)-(15,5) = "{"
+ │ └── closing_loc: (15,54)-(15,55) = "}"
+ ├── @ CallNode (location: (17,0)-(17,56))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (17,0)-(17,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (17,4)-(17,56))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (17,6)-(17,54))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (17,6)-(17,54))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (17,6)-(17,11) = "while"
+ │ │ ├── closing_loc: (17,51)-(17,54) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ SingletonClassNode (location: (17,12)-(17,42))
+ │ │ │ ├── locals: []
+ │ │ │ ├── class_keyword_loc: (17,12)-(17,17) = "class"
+ │ │ │ ├── operator_loc: (17,18)-(17,20) = "<<"
+ │ │ │ ├── expression:
+ │ │ │ │ @ SelfNode (location: (17,21)-(17,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (17,27)-(17,37))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ CallNode (location: (17,27)-(17,37))
+ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ ├── name: :tap
+ │ │ │ │ ├── message_loc: (17,27)-(17,30) = "tap"
+ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ └── block:
+ │ │ │ │ @ BlockNode (location: (17,31)-(17,37))
+ │ │ │ │ ├── locals: []
+ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ ├── body: ∅
+ │ │ │ │ ├── opening_loc: (17,31)-(17,33) = "do"
+ │ │ │ │ └── closing_loc: (17,34)-(17,37) = "end"
+ │ │ │ └── end_keyword_loc: (17,39)-(17,42) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (17,44)-(17,49))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (17,44)-(17,49))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (17,44)-(17,49) = "break"
+ │ ├── opening_loc: (17,4)-(17,5) = "{"
+ │ └── closing_loc: (17,55)-(17,56) = "}"
+ ├── @ CallNode (location: (19,0)-(19,60))
+ │ ├── flags: ignore_visibility
+ │ ├── receiver: ∅
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :tap
+ │ ├── message_loc: (19,0)-(19,3) = "tap"
+ │ ├── opening_loc: ∅
+ │ ├── arguments: ∅
+ │ ├── closing_loc: ∅
+ │ └── block:
+ │ @ BlockNode (location: (19,4)-(19,60))
+ │ ├── locals: []
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (19,6)-(19,58))
+ │ │ └── body: (length: 1)
+ │ │ └── @ WhileNode (location: (19,6)-(19,58))
+ │ │ ├── flags: ∅
+ │ │ ├── keyword_loc: (19,6)-(19,11) = "while"
+ │ │ ├── closing_loc: (19,55)-(19,58) = "end"
+ │ │ ├── predicate:
+ │ │ │ @ SingletonClassNode (location: (19,12)-(19,46))
+ │ │ │ ├── locals: [:a]
+ │ │ │ ├── class_keyword_loc: (19,12)-(19,17) = "class"
+ │ │ │ ├── operator_loc: (19,18)-(19,20) = "<<"
+ │ │ │ ├── expression:
+ │ │ │ │ @ SelfNode (location: (19,21)-(19,25))
+ │ │ │ ├── body:
+ │ │ │ │ @ StatementsNode (location: (19,27)-(19,41))
+ │ │ │ │ └── body: (length: 1)
+ │ │ │ │ └── @ LocalVariableWriteNode (location: (19,27)-(19,41))
+ │ │ │ │ ├── name: :a
+ │ │ │ │ ├── depth: 0
+ │ │ │ │ ├── name_loc: (19,27)-(19,28) = "a"
+ │ │ │ │ ├── value:
+ │ │ │ │ │ @ CallNode (location: (19,31)-(19,41))
+ │ │ │ │ │ ├── flags: ignore_visibility
+ │ │ │ │ │ ├── receiver: ∅
+ │ │ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ │ │ ├── name: :tap
+ │ │ │ │ │ ├── message_loc: (19,31)-(19,34) = "tap"
+ │ │ │ │ │ ├── opening_loc: ∅
+ │ │ │ │ │ ├── arguments: ∅
+ │ │ │ │ │ ├── closing_loc: ∅
+ │ │ │ │ │ └── block:
+ │ │ │ │ │ @ BlockNode (location: (19,35)-(19,41))
+ │ │ │ │ │ ├── locals: []
+ │ │ │ │ │ ├── parameters: ∅
+ │ │ │ │ │ ├── body: ∅
+ │ │ │ │ │ ├── opening_loc: (19,35)-(19,37) = "do"
+ │ │ │ │ │ └── closing_loc: (19,38)-(19,41) = "end"
+ │ │ │ │ └── operator_loc: (19,29)-(19,30) = "="
+ │ │ │ └── end_keyword_loc: (19,43)-(19,46) = "end"
+ │ │ └── statements:
+ │ │ @ StatementsNode (location: (19,48)-(19,53))
+ │ │ └── body: (length: 1)
+ │ │ └── @ BreakNode (location: (19,48)-(19,53))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (19,48)-(19,53) = "break"
+ │ ├── opening_loc: (19,4)-(19,5) = "{"
+ │ └── closing_loc: (19,59)-(19,60) = "}"
├── @ WhileNode (location: (21,0)-(21,31))
│ ├── flags: ∅
│ ├── keyword_loc: (21,0)-(21,5) = "while"
diff --git a/test/prism/snapshots/whitequark/break.txt b/test/prism/snapshots/whitequark/break.txt
deleted file mode 100644
index 7ce2bfa1367db4..00000000000000
--- a/test/prism/snapshots/whitequark/break.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,10))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,10))
- └── body: (length: 4)
- ├── @ BreakNode (location: (1,0)-(1,5))
- │ ├── arguments: ∅
- │ └── keyword_loc: (1,0)-(1,5) = "break"
- ├── @ BreakNode (location: (3,0)-(3,9))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,6)-(3,9))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (3,6)-(3,9))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (3,6)-(3,9) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── keyword_loc: (3,0)-(3,5) = "break"
- ├── @ BreakNode (location: (5,0)-(5,7))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,5)-(5,7))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (5,5)-(5,7))
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (5,5)-(5,6) = "("
- │ │ └── closing_loc: (5,6)-(5,7) = ")"
- │ └── keyword_loc: (5,0)-(5,5) = "break"
- └── @ BreakNode (location: (7,0)-(7,10))
- ├── arguments:
- │ @ ArgumentsNode (location: (7,5)-(7,10))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ ParenthesesNode (location: (7,5)-(7,10))
- │ ├── body:
- │ │ @ StatementsNode (location: (7,6)-(7,9))
- │ │ └── body: (length: 1)
- │ │ └── @ CallNode (location: (7,6)-(7,9))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (7,6)-(7,9) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── opening_loc: (7,5)-(7,6) = "("
- │ └── closing_loc: (7,9)-(7,10) = ")"
- └── keyword_loc: (7,0)-(7,5) = "break"
diff --git a/test/prism/snapshots/whitequark/break_block.txt b/test/prism/snapshots/whitequark/break_block.txt
deleted file mode 100644
index 9322868944569d..00000000000000
--- a/test/prism/snapshots/whitequark/break_block.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,20))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,20))
- └── body: (length: 1)
- └── @ BreakNode (location: (1,0)-(1,20))
- ├── arguments:
- │ @ ArgumentsNode (location: (1,6)-(1,20))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ CallNode (location: (1,6)-(1,20))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (1,6)-(1,9) = "fun"
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,10)-(1,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (1,10)-(1,13))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (1,10)-(1,13) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── closing_loc: ∅
- │ └── block:
- │ @ BlockNode (location: (1,14)-(1,20))
- │ ├── locals: []
- │ ├── parameters: ∅
- │ ├── body: ∅
- │ ├── opening_loc: (1,14)-(1,16) = "do"
- │ └── closing_loc: (1,17)-(1,20) = "end"
- └── keyword_loc: (1,0)-(1,5) = "break"
diff --git a/test/prism/snapshots/whitequark/class_definition_in_while_cond.txt b/test/prism/snapshots/whitequark/class_definition_in_while_cond.txt
deleted file mode 100644
index c02c88e79bf5e8..00000000000000
--- a/test/prism/snapshots/whitequark/class_definition_in_while_cond.txt
+++ /dev/null
@@ -1,171 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,44))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,44))
- └── body: (length: 4)
- ├── @ WhileNode (location: (1,0)-(1,52))
- │ ├── flags: ∅
- │ ├── keyword_loc: (1,0)-(1,5) = "while"
- │ ├── closing_loc: (1,49)-(1,52) = "end"
- │ ├── predicate:
- │ │ @ SingletonClassNode (location: (1,6)-(1,40))
- │ │ ├── locals: [:a]
- │ │ ├── class_keyword_loc: (1,6)-(1,11) = "class"
- │ │ ├── operator_loc: (1,12)-(1,14) = "<<"
- │ │ ├── expression:
- │ │ │ @ SelfNode (location: (1,15)-(1,19))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (1,21)-(1,35))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ LocalVariableWriteNode (location: (1,21)-(1,35))
- │ │ │ ├── name: :a
- │ │ │ ├── depth: 0
- │ │ │ ├── name_loc: (1,21)-(1,22) = "a"
- │ │ │ ├── value:
- │ │ │ │ @ CallNode (location: (1,25)-(1,35))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (1,25)-(1,28) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (1,29)-(1,35))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (1,29)-(1,31) = "do"
- │ │ │ │ └── closing_loc: (1,32)-(1,35) = "end"
- │ │ │ └── operator_loc: (1,23)-(1,24) = "="
- │ │ └── end_keyword_loc: (1,37)-(1,40) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (1,42)-(1,47))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (1,42)-(1,47))
- │ ├── arguments: ∅
- │ └── keyword_loc: (1,42)-(1,47) = "break"
- ├── @ WhileNode (location: (3,0)-(3,48))
- │ ├── flags: ∅
- │ ├── keyword_loc: (3,0)-(3,5) = "while"
- │ ├── closing_loc: (3,45)-(3,48) = "end"
- │ ├── predicate:
- │ │ @ SingletonClassNode (location: (3,6)-(3,36))
- │ │ ├── locals: []
- │ │ ├── class_keyword_loc: (3,6)-(3,11) = "class"
- │ │ ├── operator_loc: (3,12)-(3,14) = "<<"
- │ │ ├── expression:
- │ │ │ @ SelfNode (location: (3,15)-(3,19))
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (3,21)-(3,31))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ CallNode (location: (3,21)-(3,31))
- │ │ │ ├── flags: ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :tap
- │ │ │ ├── message_loc: (3,21)-(3,24) = "tap"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block:
- │ │ │ @ BlockNode (location: (3,25)-(3,31))
- │ │ │ ├── locals: []
- │ │ │ ├── parameters: ∅
- │ │ │ ├── body: ∅
- │ │ │ ├── opening_loc: (3,25)-(3,27) = "do"
- │ │ │ └── closing_loc: (3,28)-(3,31) = "end"
- │ │ └── end_keyword_loc: (3,33)-(3,36) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (3,38)-(3,43))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (3,38)-(3,43))
- │ ├── arguments: ∅
- │ └── keyword_loc: (3,38)-(3,43) = "break"
- ├── @ WhileNode (location: (5,0)-(5,47))
- │ ├── flags: ∅
- │ ├── keyword_loc: (5,0)-(5,5) = "while"
- │ ├── closing_loc: (5,44)-(5,47) = "end"
- │ ├── predicate:
- │ │ @ ClassNode (location: (5,6)-(5,35))
- │ │ ├── locals: [:a]
- │ │ ├── class_keyword_loc: (5,6)-(5,11) = "class"
- │ │ ├── constant_path:
- │ │ │ @ ConstantReadNode (location: (5,12)-(5,15))
- │ │ │ └── name: :Foo
- │ │ ├── inheritance_operator_loc: ∅
- │ │ ├── superclass: ∅
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (5,16)-(5,30))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ LocalVariableWriteNode (location: (5,16)-(5,30))
- │ │ │ ├── name: :a
- │ │ │ ├── depth: 0
- │ │ │ ├── name_loc: (5,16)-(5,17) = "a"
- │ │ │ ├── value:
- │ │ │ │ @ CallNode (location: (5,20)-(5,30))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (5,20)-(5,23) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (5,24)-(5,30))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (5,24)-(5,26) = "do"
- │ │ │ │ └── closing_loc: (5,27)-(5,30) = "end"
- │ │ │ └── operator_loc: (5,18)-(5,19) = "="
- │ │ ├── end_keyword_loc: (5,32)-(5,35) = "end"
- │ │ └── name: :Foo
- │ └── statements:
- │ @ StatementsNode (location: (5,37)-(5,42))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (5,37)-(5,42))
- │ ├── arguments: ∅
- │ └── keyword_loc: (5,37)-(5,42) = "break"
- └── @ WhileNode (location: (7,0)-(7,44))
- ├── flags: ∅
- ├── keyword_loc: (7,0)-(7,5) = "while"
- ├── closing_loc: (7,41)-(7,44) = "end"
- ├── predicate:
- │ @ ClassNode (location: (7,6)-(7,32))
- │ ├── locals: []
- │ ├── class_keyword_loc: (7,6)-(7,11) = "class"
- │ ├── constant_path:
- │ │ @ ConstantReadNode (location: (7,12)-(7,15))
- │ │ └── name: :Foo
- │ ├── inheritance_operator_loc: ∅
- │ ├── superclass: ∅
- │ ├── body:
- │ │ @ StatementsNode (location: (7,17)-(7,27))
- │ │ └── body: (length: 1)
- │ │ └── @ CallNode (location: (7,17)-(7,27))
- │ │ ├── flags: ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :tap
- │ │ ├── message_loc: (7,17)-(7,20) = "tap"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block:
- │ │ @ BlockNode (location: (7,21)-(7,27))
- │ │ ├── locals: []
- │ │ ├── parameters: ∅
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (7,21)-(7,23) = "do"
- │ │ └── closing_loc: (7,24)-(7,27) = "end"
- │ ├── end_keyword_loc: (7,29)-(7,32) = "end"
- │ └── name: :Foo
- └── statements:
- @ StatementsNode (location: (7,34)-(7,39))
- └── body: (length: 1)
- └── @ BreakNode (location: (7,34)-(7,39))
- ├── arguments: ∅
- └── keyword_loc: (7,34)-(7,39) = "break"
diff --git a/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt b/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt
new file mode 100644
index 00000000000000..05972521e88230
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_eflipflop_with_beginless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,14))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,9))
+ │ ├── flags: exclude_end
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,6)-(1,9))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,6)-(1,9) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,3)-(1,6) = "..."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt b/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt
new file mode 100644
index 00000000000000..c85ff292a5511f
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_eflipflop_with_endless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,14))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,9))
+ │ ├── flags: exclude_end
+ │ ├── left:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (1,6)-(1,9) = "..."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,11)-(1,14) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt b/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt
new file mode 100644
index 00000000000000..63b87ffd49c531
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_iflipflop_with_beginless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,13))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,8))
+ │ ├── flags: ∅
+ │ ├── left: ∅
+ │ ├── right:
+ │ │ @ CallNode (location: (1,5)-(1,8))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :bar
+ │ │ ├── message_loc: (1,5)-(1,8) = "bar"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ └── operator_loc: (1,3)-(1,5) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt b/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt
new file mode 100644
index 00000000000000..328a2da15362c0
--- /dev/null
+++ b/test/prism/snapshots/whitequark/cond_iflipflop_with_endless_range.txt
@@ -0,0 +1,27 @@
+@ ProgramNode (location: (1,0)-(1,13))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,13))
+ └── body: (length: 1)
+ └── @ IfNode (location: (1,0)-(1,13))
+ ├── if_keyword_loc: (1,0)-(1,2) = "if"
+ ├── predicate:
+ │ @ FlipFlopNode (location: (1,3)-(1,8))
+ │ ├── flags: ∅
+ │ ├── left:
+ │ │ @ CallNode (location: (1,3)-(1,6))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (1,3)-(1,6) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right: ∅
+ │ └── operator_loc: (1,6)-(1,8) = ".."
+ ├── then_keyword_loc: ∅
+ ├── statements: ∅
+ ├── consequent: ∅
+ └── end_keyword_loc: (1,10)-(1,13) = "end"
diff --git a/test/prism/snapshots/whitequark/if_while_after_class__since_32.txt b/test/prism/snapshots/whitequark/if_while_after_class__since_32.txt
deleted file mode 100644
index 0a2982ed353f48..00000000000000
--- a/test/prism/snapshots/whitequark/if_while_after_class__since_32.txt
+++ /dev/null
@@ -1,119 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,48))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,48))
- └── body: (length: 4)
- ├── @ ClassNode (location: (1,0)-(1,38))
- │ ├── locals: []
- │ ├── class_keyword_loc: (1,0)-(1,5) = "class"
- │ ├── constant_path:
- │ │ @ ConstantPathNode (location: (1,6)-(1,33))
- │ │ ├── parent:
- │ │ │ @ IfNode (location: (1,6)-(1,25))
- │ │ │ ├── if_keyword_loc: (1,6)-(1,8) = "if"
- │ │ │ ├── predicate:
- │ │ │ │ @ TrueNode (location: (1,9)-(1,13))
- │ │ │ ├── then_keyword_loc: ∅
- │ │ │ ├── statements:
- │ │ │ │ @ StatementsNode (location: (1,15)-(1,21))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ ConstantReadNode (location: (1,15)-(1,21))
- │ │ │ │ └── name: :Object
- │ │ │ ├── consequent: ∅
- │ │ │ └── end_keyword_loc: (1,22)-(1,25) = "end"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (1,27)-(1,33))
- │ │ │ └── name: :Kernel
- │ │ └── delimiter_loc: (1,25)-(1,27) = "::"
- │ ├── inheritance_operator_loc: ∅
- │ ├── superclass: ∅
- │ ├── body: ∅
- │ ├── end_keyword_loc: (1,35)-(1,38) = "end"
- │ └── name: :Kernel
- ├── @ ClassNode (location: (3,0)-(3,47))
- │ ├── locals: []
- │ ├── class_keyword_loc: (3,0)-(3,5) = "class"
- │ ├── constant_path:
- │ │ @ ConstantPathNode (location: (3,6)-(3,42))
- │ │ ├── parent:
- │ │ │ @ WhileNode (location: (3,6)-(3,34))
- │ │ │ ├── flags: ∅
- │ │ │ ├── keyword_loc: (3,6)-(3,11) = "while"
- │ │ │ ├── closing_loc: (3,31)-(3,34) = "end"
- │ │ │ ├── predicate:
- │ │ │ │ @ TrueNode (location: (3,12)-(3,16))
- │ │ │ └── statements:
- │ │ │ @ StatementsNode (location: (3,18)-(3,30))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ BreakNode (location: (3,18)-(3,30))
- │ │ │ ├── arguments:
- │ │ │ │ @ ArgumentsNode (location: (3,24)-(3,30))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ └── arguments: (length: 1)
- │ │ │ │ └── @ ConstantReadNode (location: (3,24)-(3,30))
- │ │ │ │ └── name: :Object
- │ │ │ └── keyword_loc: (3,18)-(3,23) = "break"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (3,36)-(3,42))
- │ │ │ └── name: :Kernel
- │ │ └── delimiter_loc: (3,34)-(3,36) = "::"
- │ ├── inheritance_operator_loc: ∅
- │ ├── superclass: ∅
- │ ├── body: ∅
- │ ├── end_keyword_loc: (3,44)-(3,47) = "end"
- │ └── name: :Kernel
- ├── @ ModuleNode (location: (5,0)-(5,39))
- │ ├── locals: []
- │ ├── module_keyword_loc: (5,0)-(5,6) = "module"
- │ ├── constant_path:
- │ │ @ ConstantPathNode (location: (5,7)-(5,34))
- │ │ ├── parent:
- │ │ │ @ IfNode (location: (5,7)-(5,26))
- │ │ │ ├── if_keyword_loc: (5,7)-(5,9) = "if"
- │ │ │ ├── predicate:
- │ │ │ │ @ TrueNode (location: (5,10)-(5,14))
- │ │ │ ├── then_keyword_loc: ∅
- │ │ │ ├── statements:
- │ │ │ │ @ StatementsNode (location: (5,16)-(5,22))
- │ │ │ │ └── body: (length: 1)
- │ │ │ │ └── @ ConstantReadNode (location: (5,16)-(5,22))
- │ │ │ │ └── name: :Object
- │ │ │ ├── consequent: ∅
- │ │ │ └── end_keyword_loc: (5,23)-(5,26) = "end"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (5,28)-(5,34))
- │ │ │ └── name: :Kernel
- │ │ └── delimiter_loc: (5,26)-(5,28) = "::"
- │ ├── body: ∅
- │ ├── end_keyword_loc: (5,36)-(5,39) = "end"
- │ └── name: :Kernel
- └── @ ModuleNode (location: (7,0)-(7,48))
- ├── locals: []
- ├── module_keyword_loc: (7,0)-(7,6) = "module"
- ├── constant_path:
- │ @ ConstantPathNode (location: (7,7)-(7,43))
- │ ├── parent:
- │ │ @ WhileNode (location: (7,7)-(7,35))
- │ │ ├── flags: ∅
- │ │ ├── keyword_loc: (7,7)-(7,12) = "while"
- │ │ ├── closing_loc: (7,32)-(7,35) = "end"
- │ │ ├── predicate:
- │ │ │ @ TrueNode (location: (7,13)-(7,17))
- │ │ └── statements:
- │ │ @ StatementsNode (location: (7,19)-(7,31))
- │ │ └── body: (length: 1)
- │ │ └── @ BreakNode (location: (7,19)-(7,31))
- │ │ ├── arguments:
- │ │ │ @ ArgumentsNode (location: (7,25)-(7,31))
- │ │ │ ├── flags: ∅
- │ │ │ └── arguments: (length: 1)
- │ │ │ └── @ ConstantReadNode (location: (7,25)-(7,31))
- │ │ │ └── name: :Object
- │ │ └── keyword_loc: (7,19)-(7,24) = "break"
- │ ├── child:
- │ │ @ ConstantReadNode (location: (7,37)-(7,43))
- │ │ └── name: :Kernel
- │ └── delimiter_loc: (7,35)-(7,37) = "::"
- ├── body: ∅
- ├── end_keyword_loc: (7,45)-(7,48) = "end"
- └── name: :Kernel
diff --git a/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt b/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt
deleted file mode 100644
index 963686b73ec038..00000000000000
--- a/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt
+++ /dev/null
@@ -1,199 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,47))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,47))
- └── body: (length: 4)
- ├── @ WhileNode (location: (1,0)-(1,45))
- │ ├── flags: ∅
- │ ├── keyword_loc: (1,0)-(1,5) = "while"
- │ ├── closing_loc: (1,42)-(1,45) = "end"
- │ ├── predicate:
- │ │ @ DefNode (location: (1,6)-(1,33))
- │ │ ├── name: :foo
- │ │ ├── name_loc: (1,10)-(1,13) = "foo"
- │ │ ├── receiver: ∅
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (1,14)-(1,28))
- │ │ │ ├── requireds: (length: 0)
- │ │ │ ├── optionals: (length: 1)
- │ │ │ │ └── @ OptionalParameterNode (location: (1,14)-(1,28))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ ├── name: :a
- │ │ │ │ ├── name_loc: (1,14)-(1,15) = "a"
- │ │ │ │ ├── operator_loc: (1,16)-(1,17) = "="
- │ │ │ │ └── value:
- │ │ │ │ @ CallNode (location: (1,18)-(1,28))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (1,18)-(1,21) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (1,22)-(1,28))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (1,22)-(1,24) = "do"
- │ │ │ │ └── closing_loc: (1,25)-(1,28) = "end"
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── body: ∅
- │ │ ├── locals: [:a]
- │ │ ├── def_keyword_loc: (1,6)-(1,9) = "def"
- │ │ ├── operator_loc: ∅
- │ │ ├── lparen_loc: ∅
- │ │ ├── rparen_loc: ∅
- │ │ ├── equal_loc: ∅
- │ │ └── end_keyword_loc: (1,30)-(1,33) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (1,35)-(1,40))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (1,35)-(1,40))
- │ ├── arguments: ∅
- │ └── keyword_loc: (1,35)-(1,40) = "break"
- ├── @ WhileNode (location: (3,0)-(3,42))
- │ ├── flags: ∅
- │ ├── keyword_loc: (3,0)-(3,5) = "while"
- │ ├── closing_loc: (3,39)-(3,42) = "end"
- │ ├── predicate:
- │ │ @ DefNode (location: (3,6)-(3,30))
- │ │ ├── name: :foo
- │ │ ├── name_loc: (3,10)-(3,13) = "foo"
- │ │ ├── receiver: ∅
- │ │ ├── parameters: ∅
- │ │ ├── body:
- │ │ │ @ StatementsNode (location: (3,15)-(3,25))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ CallNode (location: (3,15)-(3,25))
- │ │ │ ├── flags: ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :tap
- │ │ │ ├── message_loc: (3,15)-(3,18) = "tap"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block:
- │ │ │ @ BlockNode (location: (3,19)-(3,25))
- │ │ │ ├── locals: []
- │ │ │ ├── parameters: ∅
- │ │ │ ├── body: ∅
- │ │ │ ├── opening_loc: (3,19)-(3,21) = "do"
- │ │ │ └── closing_loc: (3,22)-(3,25) = "end"
- │ │ ├── locals: []
- │ │ ├── def_keyword_loc: (3,6)-(3,9) = "def"
- │ │ ├── operator_loc: ∅
- │ │ ├── lparen_loc: ∅
- │ │ ├── rparen_loc: ∅
- │ │ ├── equal_loc: ∅
- │ │ └── end_keyword_loc: (3,27)-(3,30) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (3,32)-(3,37))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (3,32)-(3,37))
- │ ├── arguments: ∅
- │ └── keyword_loc: (3,32)-(3,37) = "break"
- ├── @ WhileNode (location: (5,0)-(5,50))
- │ ├── flags: ∅
- │ ├── keyword_loc: (5,0)-(5,5) = "while"
- │ ├── closing_loc: (5,47)-(5,50) = "end"
- │ ├── predicate:
- │ │ @ DefNode (location: (5,6)-(5,38))
- │ │ ├── name: :foo
- │ │ ├── name_loc: (5,15)-(5,18) = "foo"
- │ │ ├── receiver:
- │ │ │ @ SelfNode (location: (5,10)-(5,14))
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (5,19)-(5,33))
- │ │ │ ├── requireds: (length: 0)
- │ │ │ ├── optionals: (length: 1)
- │ │ │ │ └── @ OptionalParameterNode (location: (5,19)-(5,33))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ ├── name: :a
- │ │ │ │ ├── name_loc: (5,19)-(5,20) = "a"
- │ │ │ │ ├── operator_loc: (5,21)-(5,22) = "="
- │ │ │ │ └── value:
- │ │ │ │ @ CallNode (location: (5,23)-(5,33))
- │ │ │ │ ├── flags: ignore_visibility
- │ │ │ │ ├── receiver: ∅
- │ │ │ │ ├── call_operator_loc: ∅
- │ │ │ │ ├── name: :tap
- │ │ │ │ ├── message_loc: (5,23)-(5,26) = "tap"
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── arguments: ∅
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── block:
- │ │ │ │ @ BlockNode (location: (5,27)-(5,33))
- │ │ │ │ ├── locals: []
- │ │ │ │ ├── parameters: ∅
- │ │ │ │ ├── body: ∅
- │ │ │ │ ├── opening_loc: (5,27)-(5,29) = "do"
- │ │ │ │ └── closing_loc: (5,30)-(5,33) = "end"
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── body: ∅
- │ │ ├── locals: [:a]
- │ │ ├── def_keyword_loc: (5,6)-(5,9) = "def"
- │ │ ├── operator_loc: (5,14)-(5,15) = "."
- │ │ ├── lparen_loc: ∅
- │ │ ├── rparen_loc: ∅
- │ │ ├── equal_loc: ∅
- │ │ └── end_keyword_loc: (5,35)-(5,38) = "end"
- │ └── statements:
- │ @ StatementsNode (location: (5,40)-(5,45))
- │ └── body: (length: 1)
- │ └── @ BreakNode (location: (5,40)-(5,45))
- │ ├── arguments: ∅
- │ └── keyword_loc: (5,40)-(5,45) = "break"
- └── @ WhileNode (location: (7,0)-(7,47))
- ├── flags: ∅
- ├── keyword_loc: (7,0)-(7,5) = "while"
- ├── closing_loc: (7,44)-(7,47) = "end"
- ├── predicate:
- │ @ DefNode (location: (7,6)-(7,35))
- │ ├── name: :foo
- │ ├── name_loc: (7,15)-(7,18) = "foo"
- │ ├── receiver:
- │ │ @ SelfNode (location: (7,10)-(7,14))
- │ ├── parameters: ∅
- │ ├── body:
- │ │ @ StatementsNode (location: (7,20)-(7,30))
- │ │ └── body: (length: 1)
- │ │ └── @ CallNode (location: (7,20)-(7,30))
- │ │ ├── flags: ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :tap
- │ │ ├── message_loc: (7,20)-(7,23) = "tap"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block:
- │ │ @ BlockNode (location: (7,24)-(7,30))
- │ │ ├── locals: []
- │ │ ├── parameters: ∅
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (7,24)-(7,26) = "do"
- │ │ └── closing_loc: (7,27)-(7,30) = "end"
- │ ├── locals: []
- │ ├── def_keyword_loc: (7,6)-(7,9) = "def"
- │ ├── operator_loc: (7,14)-(7,15) = "."
- │ ├── lparen_loc: ∅
- │ ├── rparen_loc: ∅
- │ ├── equal_loc: ∅
- │ └── end_keyword_loc: (7,32)-(7,35) = "end"
- └── statements:
- @ StatementsNode (location: (7,37)-(7,42))
- └── body: (length: 1)
- └── @ BreakNode (location: (7,37)-(7,42))
- ├── arguments: ∅
- └── keyword_loc: (7,37)-(7,42) = "break"
diff --git a/test/prism/snapshots/whitequark/next.txt b/test/prism/snapshots/whitequark/next.txt
deleted file mode 100644
index b58c2de4fb4eb2..00000000000000
--- a/test/prism/snapshots/whitequark/next.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,9))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,9))
- └── body: (length: 4)
- ├── @ NextNode (location: (1,0)-(1,4))
- │ ├── arguments: ∅
- │ └── keyword_loc: (1,0)-(1,4) = "next"
- ├── @ NextNode (location: (3,0)-(3,8))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,5)-(3,8))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (3,5)-(3,8))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (3,5)-(3,8) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── keyword_loc: (3,0)-(3,4) = "next"
- ├── @ NextNode (location: (5,0)-(5,6))
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,4)-(5,6))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ ParenthesesNode (location: (5,4)-(5,6))
- │ │ ├── body: ∅
- │ │ ├── opening_loc: (5,4)-(5,5) = "("
- │ │ └── closing_loc: (5,5)-(5,6) = ")"
- │ └── keyword_loc: (5,0)-(5,4) = "next"
- └── @ NextNode (location: (7,0)-(7,9))
- ├── arguments:
- │ @ ArgumentsNode (location: (7,4)-(7,9))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ ParenthesesNode (location: (7,4)-(7,9))
- │ ├── body:
- │ │ @ StatementsNode (location: (7,5)-(7,8))
- │ │ └── body: (length: 1)
- │ │ └── @ CallNode (location: (7,5)-(7,8))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (7,5)-(7,8) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── opening_loc: (7,4)-(7,5) = "("
- │ └── closing_loc: (7,8)-(7,9) = ")"
- └── keyword_loc: (7,0)-(7,4) = "next"
diff --git a/test/prism/snapshots/whitequark/next_block.txt b/test/prism/snapshots/whitequark/next_block.txt
deleted file mode 100644
index f1d65a44ed0318..00000000000000
--- a/test/prism/snapshots/whitequark/next_block.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,19))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,19))
- └── body: (length: 1)
- └── @ NextNode (location: (1,0)-(1,19))
- ├── arguments:
- │ @ ArgumentsNode (location: (1,5)-(1,19))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ CallNode (location: (1,5)-(1,19))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (1,5)-(1,8) = "fun"
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,9)-(1,12))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (1,9)-(1,12))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :foo
- │ │ ├── message_loc: (1,9)-(1,12) = "foo"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ ├── closing_loc: ∅
- │ └── block:
- │ @ BlockNode (location: (1,13)-(1,19))
- │ ├── locals: []
- │ ├── parameters: ∅
- │ ├── body: ∅
- │ ├── opening_loc: (1,13)-(1,15) = "do"
- │ └── closing_loc: (1,16)-(1,19) = "end"
- └── keyword_loc: (1,0)-(1,4) = "next"
diff --git a/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt b/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt
new file mode 100644
index 00000000000000..396238cbbfea09
--- /dev/null
+++ b/test/prism/snapshots/whitequark/numparam_ruby_bug_19025.txt
@@ -0,0 +1,49 @@
+@ ProgramNode (location: (1,0)-(1,14))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,0)-(1,14))
+ └── body: (length: 1)
+ └── @ CallNode (location: (1,0)-(1,14))
+ ├── flags: ignore_visibility
+ ├── receiver: ∅
+ ├── call_operator_loc: ∅
+ ├── name: :p
+ ├── message_loc: (1,0)-(1,1) = "p"
+ ├── opening_loc: ∅
+ ├── arguments: ∅
+ ├── closing_loc: ∅
+ └── block:
+ @ BlockNode (location: (1,2)-(1,14))
+ ├── locals: [:_1]
+ ├── parameters:
+ │ @ NumberedParametersNode (location: (1,2)-(1,14))
+ │ └── maximum: 1
+ ├── body:
+ │ @ StatementsNode (location: (1,4)-(1,12))
+ │ └── body: (length: 1)
+ │ └── @ ArrayNode (location: (1,4)-(1,12))
+ │ ├── flags: ∅
+ │ ├── elements: (length: 1)
+ │ │ └── @ CallNode (location: (1,5)-(1,11))
+ │ │ ├── flags: ∅
+ │ │ ├── receiver:
+ │ │ │ @ LocalVariableReadNode (location: (1,5)-(1,7))
+ │ │ │ ├── name: :_1
+ │ │ │ └── depth: 0
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :**
+ │ │ ├── message_loc: (1,8)-(1,10) = "**"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (1,10)-(1,11))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── opening_loc: (1,4)-(1,5) = "["
+ │ └── closing_loc: (1,11)-(1,12) = "]"
+ ├── opening_loc: (1,2)-(1,3) = "{"
+ └── closing_loc: (1,13)-(1,14) = "}"
diff --git a/test/prism/snapshots/whitequark/parser_bug_989.txt b/test/prism/snapshots/whitequark/parser_bug_989.txt
new file mode 100644
index 00000000000000..c241d6127f1e5c
--- /dev/null
+++ b/test/prism/snapshots/whitequark/parser_bug_989.txt
@@ -0,0 +1,11 @@
+@ ProgramNode (location: (1,1)-(1,8))
+├── locals: []
+└── statements:
+ @ StatementsNode (location: (1,1)-(1,8))
+ └── body: (length: 1)
+ └── @ StringNode (location: (1,1)-(1,8))
+ ├── flags: ∅
+ ├── opening_loc: (1,1)-(1,8) = "<<-HERE"
+ ├── content_loc: (2,0)-(3,0) = "\t\tcontent\n"
+ ├── closing_loc: (3,0)-(4,0) = "\tHERE\n"
+ └── unescaped: "\t\tcontent\n"
diff --git a/test/prism/snapshots/whitequark/redo.txt b/test/prism/snapshots/whitequark/redo.txt
deleted file mode 100644
index 48d3da9d52d181..00000000000000
--- a/test/prism/snapshots/whitequark/redo.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,4))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,4))
- └── body: (length: 1)
- └── @ RedoNode (location: (1,0)-(1,4))
diff --git a/test/prism/snapshots/whitequark/retry.txt b/test/prism/snapshots/whitequark/retry.txt
deleted file mode 100644
index 671c36951469ce..00000000000000
--- a/test/prism/snapshots/whitequark/retry.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,5))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,5))
- └── body: (length: 1)
- └── @ RetryNode (location: (1,0)-(1,5))
diff --git a/test/prism/snapshots/yield.txt b/test/prism/snapshots/yield.txt
index 896dbac13f05ea..e9680c3b2d9799 100644
--- a/test/prism/snapshots/yield.txt
+++ b/test/prism/snapshots/yield.txt
@@ -1,43 +1,103 @@
-@ ProgramNode (location: (1,0)-(7,14))
+@ ProgramNode (location: (1,0)-(7,28))
├── locals: []
└── statements:
- @ StatementsNode (location: (1,0)-(7,14))
+ @ StatementsNode (location: (1,0)-(7,28))
└── body: (length: 4)
- ├── @ YieldNode (location: (1,0)-(1,5))
- │ ├── keyword_loc: (1,0)-(1,5) = "yield"
+ ├── @ DefNode (location: (1,0)-(1,19))
+ │ ├── name: :foo
+ │ ├── name_loc: (1,4)-(1,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (1,9)-(1,14))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (1,9)-(1,14))
+ │ │ ├── keyword_loc: (1,9)-(1,14) = "yield"
+ │ │ ├── lparen_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ └── rparen_loc: ∅
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (1,0)-(1,3) = "def"
+ │ ├── operator_loc: ∅
│ ├── lparen_loc: ∅
- │ ├── arguments: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (3,0)-(3,7))
- │ ├── keyword_loc: (3,0)-(3,5) = "yield"
- │ ├── lparen_loc: (3,5)-(3,6) = "("
- │ ├── arguments: ∅
- │ └── rparen_loc: (3,6)-(3,7) = ")"
- ├── @ YieldNode (location: (5,0)-(5,8))
- │ ├── keyword_loc: (5,0)-(5,5) = "yield"
- │ ├── lparen_loc: (5,5)-(5,6) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,6)-(5,7))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ IntegerNode (location: (5,6)-(5,7))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ └── rparen_loc: (5,7)-(5,8) = ")"
- └── @ YieldNode (location: (7,0)-(7,14))
- ├── keyword_loc: (7,0)-(7,5) = "yield"
- ├── lparen_loc: (7,5)-(7,6) = "("
- ├── arguments:
- │ @ ArgumentsNode (location: (7,6)-(7,13))
- │ ├── flags: ∅
- │ └── arguments: (length: 3)
- │ ├── @ IntegerNode (location: (7,6)-(7,7))
- │ │ ├── flags: decimal
- │ │ └── value: 1
- │ ├── @ IntegerNode (location: (7,9)-(7,10))
- │ │ ├── flags: decimal
- │ │ └── value: 2
- │ └── @ IntegerNode (location: (7,12)-(7,13))
- │ ├── flags: decimal
- │ └── value: 3
- └── rparen_loc: (7,13)-(7,14) = ")"
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (1,16)-(1,19) = "end"
+ ├── @ DefNode (location: (3,0)-(3,21))
+ │ ├── name: :foo
+ │ ├── name_loc: (3,4)-(3,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (3,9)-(3,16))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (3,9)-(3,16))
+ │ │ ├── keyword_loc: (3,9)-(3,14) = "yield"
+ │ │ ├── lparen_loc: (3,14)-(3,15) = "("
+ │ │ ├── arguments: ∅
+ │ │ └── rparen_loc: (3,15)-(3,16) = ")"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (3,0)-(3,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (3,18)-(3,21) = "end"
+ ├── @ DefNode (location: (5,0)-(5,22))
+ │ ├── name: :foo
+ │ ├── name_loc: (5,4)-(5,7) = "foo"
+ │ ├── receiver: ∅
+ │ ├── parameters: ∅
+ │ ├── body:
+ │ │ @ StatementsNode (location: (5,9)-(5,17))
+ │ │ └── body: (length: 1)
+ │ │ └── @ YieldNode (location: (5,9)-(5,17))
+ │ │ ├── keyword_loc: (5,9)-(5,14) = "yield"
+ │ │ ├── lparen_loc: (5,14)-(5,15) = "("
+ │ │ ├── arguments:
+ │ │ │ @ ArgumentsNode (location: (5,15)-(5,16))
+ │ │ │ ├── flags: ∅
+ │ │ │ └── arguments: (length: 1)
+ │ │ │ └── @ IntegerNode (location: (5,15)-(5,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ └── rparen_loc: (5,16)-(5,17) = ")"
+ │ ├── locals: []
+ │ ├── def_keyword_loc: (5,0)-(5,3) = "def"
+ │ ├── operator_loc: ∅
+ │ ├── lparen_loc: ∅
+ │ ├── rparen_loc: ∅
+ │ ├── equal_loc: ∅
+ │ └── end_keyword_loc: (5,19)-(5,22) = "end"
+ └── @ DefNode (location: (7,0)-(7,28))
+ ├── name: :foo
+ ├── name_loc: (7,4)-(7,7) = "foo"
+ ├── receiver: ∅
+ ├── parameters: ∅
+ ├── body:
+ │ @ StatementsNode (location: (7,9)-(7,23))
+ │ └── body: (length: 1)
+ │ └── @ YieldNode (location: (7,9)-(7,23))
+ │ ├── keyword_loc: (7,9)-(7,14) = "yield"
+ │ ├── lparen_loc: (7,14)-(7,15) = "("
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (7,15)-(7,22))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 3)
+ │ │ ├── @ IntegerNode (location: (7,15)-(7,16))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 1
+ │ │ ├── @ IntegerNode (location: (7,18)-(7,19))
+ │ │ │ ├── flags: decimal
+ │ │ │ └── value: 2
+ │ │ └── @ IntegerNode (location: (7,21)-(7,22))
+ │ │ ├── flags: decimal
+ │ │ └── value: 3
+ │ └── rparen_loc: (7,22)-(7,23) = ")"
+ ├── locals: []
+ ├── def_keyword_loc: (7,0)-(7,3) = "def"
+ ├── operator_loc: ∅
+ ├── lparen_loc: ∅
+ ├── rparen_loc: ∅
+ ├── equal_loc: ∅
+ └── end_keyword_loc: (7,25)-(7,28) = "end"
diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb
index 28b0725d6f0dba..77af7e7b459bf4 100644
--- a/test/prism/test_helper.rb
+++ b/test/prism/test_helper.rb
@@ -19,6 +19,38 @@ module Prism
class TestCase < ::Test::Unit::TestCase
private
+ if RUBY_ENGINE == "ruby"
+ # Check that the given source is valid syntax by compiling it with RubyVM.
+ def check_syntax(source)
+ $VERBOSE, previous = nil, $VERBOSE
+
+ begin
+ RubyVM::InstructionSequence.compile(source)
+ ensure
+ $VERBOSE = previous
+ end
+ end
+
+ # Assert that the given source is valid Ruby syntax by attempting to
+ # compile it, and then implicitly checking that it does not raise an
+ # syntax errors.
+ def assert_valid_syntax(source)
+ check_syntax(source)
+ end
+
+ # Refute that the given source is invalid Ruby syntax by attempting to
+ # compile it and asserting that it raises a SyntaxError.
+ def refute_valid_syntax(source)
+ assert_raise(SyntaxError) { check_syntax(source) }
+ end
+ else
+ def assert_valid_syntax(source)
+ end
+
+ def refute_valid_syntax(source)
+ end
+ end
+
def assert_raises(*args, &block)
raise "Use assert_raise instead"
end
diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb
index 2311af0f5aad26..0dff275f6ec4ee 100644
--- a/test/reline/test_key_actor_emacs.rb
+++ b/test/reline/test_key_actor_emacs.rb
@@ -255,18 +255,18 @@ def test_em_delete_ends_editing
end
def test_ed_clear_screen
- refute(@line_editor.instance_variable_get(:@cleared))
+ @line_editor.instance_variable_get(:@rendered_screen).lines = [[]]
input_keys("\C-l", false)
- assert(@line_editor.instance_variable_get(:@cleared))
+ assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines)
end
def test_ed_clear_screen_with_inputed
input_keys('abc')
input_keys("\C-b", false)
- refute(@line_editor.instance_variable_get(:@cleared))
+ @line_editor.instance_variable_get(:@rendered_screen).lines = [[]]
assert_line_around_cursor('ab', 'c')
input_keys("\C-l", false)
- assert(@line_editor.instance_variable_get(:@cleared))
+ assert_empty(@line_editor.instance_variable_get(:@rendered_screen).lines)
assert_line_around_cursor('ab', 'c')
end
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 53c842a04a41c7..3409b8905c53a8 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -831,6 +831,20 @@ def test_suppress_auto_indent_for_adding_newlines_in_pasting
EOC
end
+ def test_auto_indent_with_various_spaces
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --auto-indent}, startup_message: 'Multiline REPL.')
+ write "(\n\C-v"
+ write "\C-k\n\C-v"
+ write "\C-k)"
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> (
+ prompt> ^K
+ prompt> )
+ EOC
+ end
+
def test_autowrap_in_the_middle_of_a_line
start_terminal(5, 20, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write("def abcdefg; end\C-b\C-b\C-b\C-b\C-b")
diff --git a/test/ruby/enc/test_case_options.rb b/test/ruby/enc/test_case_options.rb
index 40a85ee97072d0..e9c81d804eaa38 100644
--- a/test/ruby/enc/test_case_options.rb
+++ b/test/ruby/enc/test_case_options.rb
@@ -19,7 +19,7 @@ def assert_raise_bang_operations(arg, *options)
def assert_raise_both_types(*options)
assert_raise_functional_operations 'a', *options
- assert_raise_bang_operations +'a', *options
+ assert_raise_bang_operations(+'a', *options)
assert_raise_functional_operations :a, *options
end
@@ -51,7 +51,7 @@ def assert_okay_bang_operations(arg, *options)
def assert_okay_both_types(*options)
assert_okay_functional_operations 'a', *options
- assert_okay_bang_operations +'a', *options
+ assert_okay_bang_operations(+'a', *options)
assert_okay_functional_operations :a, *options
end
diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb
index 39c5e88893a587..55a06296aa7275 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -9,39 +9,23 @@ class TestArgf < Test::Unit::TestCase
def setup
@tmpdir = Dir.mktmpdir
@tmp_count = 0
- @t1 = make_tempfile0("argf-foo")
- @t1.binmode
- @t1.puts "1"
- @t1.puts "2"
- @t1.close
- @t2 = make_tempfile0("argf-bar")
- @t2.binmode
- @t2.puts "3"
- @t2.puts "4"
- @t2.close
- @t3 = make_tempfile0("argf-baz")
- @t3.binmode
- @t3.puts "5"
- @t3.puts "6"
- @t3.close
+ @t1 = make_tempfile("argf-foo", %w"1 2", binmode: true)
+ @t2 = make_tempfile("argf-bar", %w"3 4", binmode: true)
+ @t3 = make_tempfile("argf-baz", %w"5 6", binmode: true)
end
def teardown
FileUtils.rmtree(@tmpdir)
end
- def make_tempfile0(basename)
+ def make_tempfile(basename = "argf-qux", data = %w[foo bar baz], binmode: false)
@tmp_count += 1
- open("#{@tmpdir}/#{basename}-#{@tmp_count}", "w")
- end
-
- def make_tempfile(basename = "argf-qux")
- t = make_tempfile0(basename)
- t.puts "foo"
- t.puts "bar"
- t.puts "baz"
- t.close
- t
+ path = "#{@tmpdir}/#{basename}-#{@tmp_count}"
+ File.open(path, "w") do |f|
+ f.binmode if binmode
+ f.puts(*data)
+ f
+ end
end
def ruby(*args, external_encoding: Encoding::UTF_8)
@@ -571,15 +555,11 @@ def test_eof
end
end
- t1 = open("#{@tmpdir}/argf-hoge", "w")
- t1.binmode
- t1.puts "foo"
- t1.close
- t2 = open("#{@tmpdir}/argf-moge", "w")
- t2.binmode
- t2.puts "bar"
- t2.close
- ruby('-e', 'STDERR.reopen(STDOUT); ARGF.gets; ARGF.skip; p ARGF.eof?', t1.path, t2.path) do |f|
+ t1 = "#{@tmpdir}/argf-hoge"
+ t2 = "#{@tmpdir}/argf-moge"
+ File.binwrite(t1, "foo\n")
+ File.binwrite(t2, "bar\n")
+ ruby('-e', 'STDERR.reopen(STDOUT); ARGF.gets; ARGF.skip; p ARGF.eof?', t1, t2) do |f|
assert_equal(%w(false), f.read.split(/\n/))
end
end
@@ -854,7 +834,7 @@ def test_file
def test_binmode
bug5268 = '[ruby-core:39234]'
- open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ File.binwrite(@t3.path, "5\r\n6\r\n")
ruby('-e', "ARGF.binmode; STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
f.binmode
assert_equal("1\n2\n3\n4\n5\r\n6\r\n", f.read, bug5268)
@@ -863,7 +843,7 @@ def test_binmode
def test_textmode
bug5268 = '[ruby-core:39234]'
- open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ File.binwrite(@t3.path, "5\r\n6\r\n")
ruby('-e', "STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
f.binmode
assert_equal("1\n2\n3\n4\n5\n6\n", f.read, bug5268)
@@ -1142,36 +1122,30 @@ def test_sized_read
end
def test_putc
- t = make_tempfile0("argf-#{__method__}")
- t.puts 'bar'
- t.close
+ t = make_tempfile("argf-#{__method__}", 'bar')
ruby('-pi-', '-e', "print ARGF.putc('x')", t.path) do |f|
end
assert_equal("xxbar\n", File.read(t.path))
end
def test_puts
- t = make_tempfile0("argf-#{__method__}")
- t.puts 'bar'
- t.close
- ruby('-pi-', '-e', "print ARGF.puts('foo')", t.path) do |f|
+ t = make_tempfile("argf-#{__method__}", 'bar')
+ err = "#{@tmpdir}/errout"
+ ruby('-pi-', '-W2', '-e', "print ARGF.puts('foo')", t.path, {err: err}) do |f|
end
assert_equal("foo\nbar\n", File.read(t.path))
+ assert_empty File.read(err)
end
def test_print
- t = make_tempfile0("argf-#{__method__}")
- t.puts 'bar'
- t.close
+ t = make_tempfile("argf-#{__method__}", 'bar')
ruby('-pi-', '-e', "print ARGF.print('foo')", t.path) do |f|
end
assert_equal("foobar\n", File.read(t.path))
end
def test_printf
- t = make_tempfile0("argf-#{__method__}")
- t.puts 'bar'
- t.close
+ t = make_tempfile("argf-#{__method__}", 'bar')
ruby('-pi-', '-e', "print ARGF.printf('%s', 'foo')", t.path) do |f|
end
assert_equal("foobar\n", File.read(t.path))
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index d19bda118f1097..90d19c3d68eb74 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -746,6 +746,19 @@ def test_keep_script_lines_for_of
assert_equal("def test_keep_script_lines_for_of\n", node_method.source.lines.first)
end
+ def test_keep_script_lines_for_of_with_existing_SCRIPT_LINES__that_has__FILE__as_a_key
+ # This test confirms that the bug that previously occurred because of
+ # `AbstractSyntaxTree.of`s unnecessary dependence on SCRIPT_LINES__ does not reproduce.
+ # The bug occurred only if SCRIPT_LINES__ included __FILE__ as a key.
+ lines = [
+ "SCRIPT_LINES__ = {__FILE__ => []}",
+ "puts RubyVM::AbstractSyntaxTree.of(->{ 1 + 2 }, keep_script_lines: true).script_lines",
+ "p SCRIPT_LINES__"
+ ]
+ test_stdout = lines + ['{"-e"=>[]}']
+ assert_in_out_err(["-e", lines.join("\n")], "", test_stdout, [])
+ end
+
def test_source_with_multibyte_characters
ast = RubyVM::AbstractSyntaxTree.parse(%{a("\u00a7");b("\u00a9")}, keep_script_lines: true)
a_fcall, b_fcall = ast.children[2].children
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index d5fe9ce320d7a8..a52b75c2672dc3 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -133,6 +133,9 @@ def []=(*a, **b, &c) @set = [a, b, c] end
kw = {}
b = lambda{}
+ # Prevent "assigned but unused variable" warnings
+ _ = [h, a, kw, b]
+
message = /keyword arg given in index/
# +=, without block, non-popped
@@ -294,6 +297,9 @@ def []=(*a, **b) @set = [a, b] end
a = []
kw = {}
+ # Prevent "assigned but unused variable" warnings
+ _ = [h, a, kw]
+
assert_syntax_error(%q{h[*a, 2, b: 5, **kw] += 1}, message)
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index e365c39def583b..fe649cddb90f61 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -498,6 +498,10 @@ def test_op_asgn1_with_block
t = Object.new
a = []
blk = proc {|x| a << x }
+
+ # Prevent an "assigned but unused variable" warning
+ _ = blk
+
def t.[](_)
yield(:aref)
nil
@@ -1103,9 +1107,13 @@ def test_unused_variable
def test_parsing_begin_statement_inside_method_definition
assert_equal :bug_20234, eval("def (begin;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
assert_equal :bug_20234, eval("def (begin;rescue;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
assert_equal :bug_20234, eval("def (begin;ensure;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
assert_equal :bug_20234, eval("def (begin;rescue;else;end).bug_20234; end")
+ NilClass.remove_method(:bug_20234)
assert_raise(SyntaxError) { eval("def (begin;else;end).bug_20234; end") }
assert_raise(SyntaxError) { eval("def (begin;ensure;else;end).bug_20234; end") }
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index fd5092aaf0956d..4f636fa1ba0a5a 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -979,7 +979,7 @@ def test_resolve_feature_path_with_missing_feature
def test_require_with_public_method_missing
# [Bug #19793]
- assert_separately(["-W0", "-rtempfile"], __FILE__, __LINE__, <<~RUBY)
+ assert_separately(["-W0", "-rtempfile"], __FILE__, __LINE__, <<~RUBY, timeout: 60)
GC.stress = true
class Object
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 0dae1c5a8e49ea..9ff3791e7de986 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -3620,11 +3620,11 @@ def test_chilled_string
assert_predicate chilled_string.clone, :frozen?
# @+ treat the original string as frozen
- assert_not_predicate +chilled_string, :frozen?
+ assert_not_predicate(+chilled_string, :frozen?)
assert_not_same chilled_string, +chilled_string
# @- the original string as mutable
- assert_predicate -chilled_string, :frozen?
+ assert_predicate(-chilled_string, :frozen?)
assert_not_same chilled_string, -chilled_string
end
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index e8c76ab49b8094..a055f248be7526 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -487,6 +487,16 @@ def test_ignore_invalid_config_file
end
end
+ def test_accept_string_key
+ File.open @temp_conf, "w" do |fp|
+ fp.puts "verbose: false"
+ end
+
+ util_config_file
+
+ assert_equal false, @cfg.verbose
+ end
+
def test_load_ssl_verify_mode_from_config
File.open @temp_conf, "w" do |fp|
fp.puts ":ssl_verify_mode: 1"
diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb
index a748090b2645ca..d758b5fb026fa1 100644
--- a/tool/lib/test/unit.rb
+++ b/tool/lib/test/unit.rb
@@ -1374,7 +1374,8 @@ def setup_options(opts, options)
def record(suite, method, assertions, time, error, source_location = nil)
if writer = @options[:launchable_test_reports]
- if path = (source_location || suite.instance_method(method).source_location).first
+ if loc = (source_location || suite.instance_method(method).source_location)
+ path, lineno = loc
# Launchable JSON schema is defined at
# https://github.com/search?q=repo%3Alaunchableinc%2Fcli+https%3A%2F%2Flaunchableinc.com%2Fschema%2FRecordTestInput&type=code.
e = case error
@@ -1416,7 +1417,10 @@ def record(suite, method, assertions, time, error, source_location = nil)
duration: time,
createdAt: Time.now.to_s,
stderr: e,
- stdout: nil
+ stdout: nil,
+ data: {
+ lineNumber: lineno
+ }
}
)
end
diff --git a/tool/test/testunit/test_launchable.rb b/tool/test/testunit/test_launchable.rb
index a91d44b1cea260..70c371e2129f03 100644
--- a/tool/test/testunit/test_launchable.rb
+++ b/tool/test/testunit/test_launchable.rb
@@ -15,7 +15,10 @@ def test_json_stream_writer
status: "TEST_FAILED",
stdout: nil,
stderr: nil,
- createdAt: "2021-10-05T12:34:00"
+ createdAt: "2021-10-05T12:34:00",
+ data: {
+ lineNumber: 1
+ }
}
)
json_stream_writer.write_object(
@@ -25,7 +28,10 @@ def test_json_stream_writer
status: "TEST_PASSED",
stdout: "This is stdout",
stderr: "This is stderr",
- createdAt: "2021-10-05T12:36:00"
+ createdAt: "2021-10-05T12:36:00",
+ data: {
+ lineNumber: 10
+ }
}
)
json_stream_writer.close()
@@ -38,7 +44,10 @@ def test_json_stream_writer
"status": "TEST_FAILED",
"stdout": null,
"stderr": null,
- "createdAt": "2021-10-05T12:34:00"
+ "createdAt": "2021-10-05T12:34:00",
+ "data": {
+ "lineNumber": 1
+ }
},
{
"testPath": "file=test/test_a.rb#class=class1#testcase=testcase899",
@@ -46,7 +55,10 @@ def test_json_stream_writer
"status": "TEST_PASSED",
"stdout": "This is stdout",
"stderr": "This is stderr",
- "createdAt": "2021-10-05T12:36:00"
+ "createdAt": "2021-10-05T12:36:00",
+ "data": {
+ "lineNumber": 10
+ }
}
]
}
diff --git a/universal_parser.c b/universal_parser.c
index 56d2c4cb156f59..1291b487c5fe23 100644
--- a/universal_parser.c
+++ b/universal_parser.c
@@ -214,8 +214,6 @@
#define ENCODING_IS_ASCII8BIT p->config->encoding_is_ascii8bit
#define rb_usascii_encoding p->config->usascii_encoding
-#define rb_ractor_make_shareable p->config->ractor_make_shareable
-
#define rb_local_defined p->config->local_defined
#define rb_dvar_defined p->config->dvar_defined
diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs
index c6e50beab1b156..be719023c01019 100644
--- a/yjit/src/codegen.rs
+++ b/yjit/src/codegen.rs
@@ -421,7 +421,7 @@ fn gen_save_sp(asm: &mut Assembler) {
fn gen_save_sp_with_offset(asm: &mut Assembler, offset: i8) {
if asm.ctx.get_sp_offset() != -offset {
asm_comment!(asm, "save SP to CFP");
- let stack_pointer = asm.ctx.sp_opnd(offset as i32 * SIZEOF_VALUE_I32);
+ let stack_pointer = asm.ctx.sp_opnd(offset as i32);
let sp_addr = asm.lea(stack_pointer);
asm.mov(SP, sp_addr);
let cfp_sp_opnd = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP);
@@ -1578,8 +1578,7 @@ fn gen_newarray(
Opnd::UImm(0)
} else {
asm_comment!(asm, "load pointer to array elements");
- let offset_magnitude = (SIZEOF_VALUE as u32) * n;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(n as i32));
asm.lea(values_opnd)
};
@@ -1793,7 +1792,7 @@ fn gen_pushtoarray(
// Get the operands from the stack
let ary_opnd = asm.stack_opnd(num as i32);
- let objp_opnd = asm.lea(asm.ctx.sp_opnd(-(num as i32) * SIZEOF_VALUE_I32));
+ let objp_opnd = asm.lea(asm.ctx.sp_opnd(-(num as i32)));
let ary = asm.ccall(rb_ary_cat as *const u8, vec![ary_opnd, objp_opnd, num.into()]);
asm.stack_pop(num as usize + 1); // Keep it on stack during ccall for GC
@@ -3237,7 +3236,7 @@ fn gen_concatstrings(
// rb_str_concat_literals may raise Encoding::CompatibilityError
jit_prepare_non_leaf_call(jit, asm);
- let values_ptr = asm.lea(asm.ctx.sp_opnd(-(SIZEOF_VALUE_I32 * n as i32)));
+ let values_ptr = asm.lea(asm.ctx.sp_opnd(-(n as i32)));
// call rb_str_concat_literals(size_t n, const VALUE *strings);
let return_value = asm.ccall(
@@ -4103,8 +4102,7 @@ fn gen_opt_newarray_max(
fn rb_vm_opt_newarray_max(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -4156,8 +4154,7 @@ fn gen_opt_newarray_hash(
fn rb_vm_opt_newarray_hash(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -4191,8 +4188,7 @@ fn gen_opt_newarray_min(
fn rb_vm_opt_newarray_min(ec: EcPtr, num: u32, elts: *const VALUE) -> VALUE;
}
- let offset_magnitude = (SIZEOF_VALUE as u32) * num;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(num as i32));
let values_ptr = asm.lea(values_opnd);
let val_opnd = asm.ccall(
@@ -6363,7 +6359,8 @@ fn gen_send_cfunc(
// #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
// REG_CFP <= REG_SP + 4 * SIZEOF_VALUE + sizeof(rb_control_frame_t)
asm_comment!(asm, "stack overflow check");
- let stack_limit = asm.lea(asm.ctx.sp_opnd((SIZEOF_VALUE * 4 + 2 * RUBY_SIZEOF_CONTROL_FRAME) as i32));
+ const _: () = assert!(RUBY_SIZEOF_CONTROL_FRAME % SIZEOF_VALUE == 0, "sizeof(rb_control_frame_t) is a multiple of sizeof(VALUE)");
+ let stack_limit = asm.lea(asm.ctx.sp_opnd((4 + 2 * (RUBY_SIZEOF_CONTROL_FRAME / SIZEOF_VALUE)) as i32));
asm.cmp(CFP, stack_limit);
asm.jbe(Target::side_exit(Counter::guard_send_se_cf_overflow));
@@ -6502,10 +6499,10 @@ fn gen_send_cfunc(
const _: () = assert!(SIZEOF_VALUE == 8, "opting for a shift since mul on A64 takes no immediates");
let splat_size_bytes = asm.lshift(splat_size, 3usize.into());
// 3 items for method metadata, minus one to remove the splat array
- let static_stack_top = asm.lea(asm.ctx.sp_opnd(SIZEOF_VALUE_I32 * 2));
+ let static_stack_top = asm.lea(asm.ctx.sp_opnd(2));
asm.add(static_stack_top, splat_size_bytes)
} else {
- asm.lea(asm.ctx.sp_opnd(SIZEOF_VALUE_I32 * 3))
+ asm.lea(asm.ctx.sp_opnd(3))
};
let specval = if block_arg_type == Some(Type::BlockParamProxy) {
@@ -6579,14 +6576,14 @@ fn gen_send_cfunc(
vec![
passed_argc_opnd,
- asm.lea(asm.ctx.sp_opnd(-argc * SIZEOF_VALUE_I32)),
+ asm.lea(asm.ctx.sp_opnd(-argc)),
asm.stack_opnd(argc),
]
}
// Variadic method taking a Ruby array
else if cfunc_argc == -2 {
// Slurp up all the arguments into an array
- let stack_args = asm.lea(asm.ctx.sp_opnd(-argc * SIZEOF_VALUE_I32));
+ let stack_args = asm.lea(asm.ctx.sp_opnd(-argc));
let args_array = asm.ccall(
rb_ec_ary_new_from_values as _,
vec![EC, passed_argc.into(), stack_args]
@@ -7188,9 +7185,9 @@ fn gen_send_iseq(
// Note that vm_push_frame checks it against a decremented cfp, hence the multiply by 2.
// #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
asm_comment!(asm, "stack overflow check");
+ const _: () = assert!(RUBY_SIZEOF_CONTROL_FRAME % SIZEOF_VALUE == 0, "sizeof(rb_control_frame_t) is a multiple of sizeof(VALUE)");
let stack_max: i32 = unsafe { get_iseq_body_stack_max(iseq) }.try_into().unwrap();
- let locals_offs =
- SIZEOF_VALUE_I32 * (num_locals + stack_max) + 2 * (RUBY_SIZEOF_CONTROL_FRAME as i32);
+ let locals_offs = (num_locals + stack_max) + 2 * (RUBY_SIZEOF_CONTROL_FRAME / SIZEOF_VALUE) as i32;
let stack_limit = asm.lea(asm.ctx.sp_opnd(locals_offs));
asm.cmp(CFP, stack_limit);
asm.jbe(Target::side_exit(Counter::guard_send_se_cf_overflow));
@@ -7260,7 +7257,7 @@ fn gen_send_iseq(
return None;
}
let proc = asm.stack_pop(1); // Pop first, as argc doesn't account for the block arg
- let callee_specval = asm.ctx.sp_opnd(callee_specval * SIZEOF_VALUE_I32);
+ let callee_specval = asm.ctx.sp_opnd(callee_specval);
asm.store(callee_specval, proc);
}
None => {
@@ -7323,8 +7320,7 @@ fn gen_send_iseq(
// diff is >0 so no need to worry about null pointer
asm_comment!(asm, "load pointer to array elements");
- let offset_magnitude = SIZEOF_VALUE as u32 * diff;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(diff as i32));
let values_ptr = asm.lea(values_opnd);
asm_comment!(asm, "prepend stack values to rest array");
@@ -7368,8 +7364,7 @@ fn gen_send_iseq(
Opnd::UImm(0)
} else {
asm_comment!(asm, "load pointer to array elements");
- let offset_magnitude = SIZEOF_VALUE as u32 * n;
- let values_opnd = asm.ctx.sp_opnd(-(offset_magnitude as i32));
+ let values_opnd = asm.ctx.sp_opnd(-(n as i32));
asm.lea(values_opnd)
};
@@ -7463,7 +7458,7 @@ fn gen_send_iseq(
asm_comment!(asm, "{}", comment);
for i in fill_range {
- let value_slot = asm.ctx.sp_opnd(i * SIZEOF_VALUE_I32);
+ let value_slot = asm.ctx.sp_opnd(i);
asm.store(value_slot, Qnil.into());
}
}
@@ -7481,9 +7476,7 @@ fn gen_send_iseq(
);
// Nil-initialize the block parameter. It's the last parameter local
if iseq_has_block_param {
- let block_param = asm.ctx.sp_opnd(
- SIZEOF_VALUE_I32 * (-argc + num_params - 1)
- );
+ let block_param = asm.ctx.sp_opnd(-argc + num_params - 1);
asm.store(block_param, Qnil.into());
}
// Nil-initialize non-parameter locals
@@ -7508,15 +7501,14 @@ fn gen_send_iseq(
// Store the updated SP on the current frame (pop arguments and receiver)
asm_comment!(asm, "store caller sp");
- let caller_sp = asm.lea(asm.ctx.sp_opnd(SIZEOF_VALUE_I32 * -sp_offset));
+ let caller_sp = asm.lea(asm.ctx.sp_opnd(-sp_offset));
asm.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP), caller_sp);
// Store the next PC in the current frame
jit_save_pc(jit, asm);
// Adjust the callee's stack pointer
- let offs = SIZEOF_VALUE_I32 * (-argc + num_locals + VM_ENV_DATA_SIZE as i32);
- let callee_sp = asm.lea(asm.ctx.sp_opnd(offs));
+ let callee_sp = asm.lea(asm.ctx.sp_opnd(-argc + num_locals + VM_ENV_DATA_SIZE as i32));
let specval = if let Some(prev_ep) = prev_ep {
// We've already side-exited if the callee expects a block, so we
@@ -7824,7 +7816,7 @@ fn gen_iseq_kw_call(
gen_save_sp(asm);
// Build the kwrest hash. `struct rb_callinfo_kwarg` is malloc'd, so no GC concerns.
- let kwargs_start = asm.lea(asm.ctx.sp_opnd(-caller_keyword_len_i32 * SIZEOF_VALUE_I32));
+ let kwargs_start = asm.lea(asm.ctx.sp_opnd(-caller_keyword_len_i32));
let hash = asm.ccall(
build_kw_rest as _,
vec![rest_mask.into(), kwargs_start, Opnd::const_ptr(ci_kwarg.cast())]
@@ -8954,7 +8946,7 @@ fn gen_invokeblock_specialized(
}
asm_comment!(asm, "call ifunc");
let captured_opnd = asm.and(block_handler_opnd, Opnd::Imm(!0x3));
- let argv = asm.lea(asm.ctx.sp_opnd(-argc * SIZEOF_VALUE_I32));
+ let argv = asm.lea(asm.ctx.sp_opnd(-argc));
let ret = asm.ccall(
rb_vm_yield_with_cfunc as *const u8,
vec![EC, captured_opnd, argc.into(), argv],
@@ -9306,7 +9298,7 @@ fn gen_toregexp(
// raise an exception.
jit_prepare_non_leaf_call(jit, asm);
- let values_ptr = asm.lea(asm.ctx.sp_opnd(-(SIZEOF_VALUE_I32 * cnt as i32)));
+ let values_ptr = asm.lea(asm.ctx.sp_opnd(-(cnt as i32)));
let ary = asm.ccall(
rb_ary_tmp_new_from_values as *const u8,
diff --git a/yjit/src/core.rs b/yjit/src/core.rs
index 7f6e7d47f9defd..fb7d52cc5d0f61 100644
--- a/yjit/src/core.rs
+++ b/yjit/src/core.rs
@@ -1793,8 +1793,8 @@ impl Context {
}
/// Get an operand for the adjusted stack pointer address
- pub fn sp_opnd(&self, offset_bytes: i32) -> Opnd {
- let offset = ((self.sp_offset as i32) * SIZEOF_VALUE_I32) + offset_bytes;
+ pub fn sp_opnd(&self, offset: i32) -> Opnd {
+ let offset = (self.sp_offset as i32 + offset) * SIZEOF_VALUE_I32;
return Opnd::mem(64, SP, offset);
}
diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs
index fb4ed03bd5c4af..0a63fab8b049ae 100644
--- a/yjit/src/stats.rs
+++ b/yjit/src/stats.rs
@@ -4,6 +4,7 @@
#![allow(dead_code)] // Counters are only used with the stats features
use std::alloc::{GlobalAlloc, Layout, System};
+use std::ptr::addr_of_mut;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Instant;
use std::collections::HashMap;
@@ -69,12 +70,14 @@ static mut ISEQ_CALL_COUNT: Option> = None;
/// Assign an index to a given cfunc name string
pub fn get_cfunc_idx(name: &str) -> usize {
- unsafe { get_method_idx(name, &mut CFUNC_NAME_TO_IDX, &mut CFUNC_CALL_COUNT) }
+ // SAFETY: We acquire a VM lock and don't create multiple &mut references to these static mut variables.
+ unsafe { get_method_idx(name, &mut *addr_of_mut!(CFUNC_NAME_TO_IDX), &mut *addr_of_mut!(CFUNC_CALL_COUNT)) }
}
/// Assign an index to a given ISEQ name string
pub fn get_iseq_idx(name: &str) -> usize {
- unsafe { get_method_idx(name, &mut ISEQ_NAME_TO_IDX, &mut ISEQ_CALL_COUNT) }
+ // SAFETY: We acquire a VM lock and don't create multiple &mut references to these static mut variables.
+ unsafe { get_method_idx(name, &mut *addr_of_mut!(ISEQ_NAME_TO_IDX), &mut *addr_of_mut!(ISEQ_CALL_COUNT)) }
}
fn get_method_idx(
@@ -815,12 +818,12 @@ fn rb_yjit_gen_stats_dict(context: bool) -> VALUE {
// Create a hash for the cfunc call counts
let cfunc_calls = rb_hash_new();
rb_hash_aset(hash, rust_str_to_sym("cfunc_calls"), cfunc_calls);
- set_call_counts(cfunc_calls, &mut CFUNC_NAME_TO_IDX, &mut CFUNC_CALL_COUNT);
+ set_call_counts(cfunc_calls, &mut *addr_of_mut!(CFUNC_NAME_TO_IDX), &mut *addr_of_mut!(CFUNC_CALL_COUNT));
// Create a hash for the ISEQ call counts
let iseq_calls = rb_hash_new();
rb_hash_aset(hash, rust_str_to_sym("iseq_calls"), iseq_calls);
- set_call_counts(iseq_calls, &mut ISEQ_NAME_TO_IDX, &mut ISEQ_CALL_COUNT);
+ set_call_counts(iseq_calls, &mut *addr_of_mut!(ISEQ_NAME_TO_IDX), &mut *addr_of_mut!(ISEQ_CALL_COUNT));
}
hash