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