summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/-ext-/integer/test_my_integer.rb34
-rw-r--r--test/-ext-/string/test_chilled.rb19
-rw-r--r--test/-ext-/thread/test_instrumentation_api.rb4
-rw-r--r--test/.excludes-prism/TestAssignment.rb1
-rw-r--r--test/.excludes-prism/TestAssignmentGen.rb1
-rw-r--r--test/.excludes-prism/TestCall.rb3
-rw-r--r--test/.excludes-prism/TestCoverage.rb1
-rw-r--r--test/.excludes-prism/TestIRB/RubyLexTest.rb1
-rw-r--r--test/.excludes-prism/TestISeq.rb3
-rw-r--r--test/.excludes-prism/TestM17N.rb4
-rw-r--r--test/.excludes-prism/TestMixedUnicodeEscape.rb2
-rw-r--r--test/.excludes-prism/TestParse.rb19
-rw-r--r--test/.excludes-prism/TestPatternMatching.rb2
-rw-r--r--test/.excludes-prism/TestRegexp.rb1
-rw-r--r--test/.excludes-prism/TestRequire.rb1
-rw-r--r--test/.excludes-prism/TestRubyLiteral.rb6
-rw-r--r--test/.excludes-prism/TestRubyVM.rb1
-rw-r--r--test/.excludes-prism/TestSetTraceFunc.rb1
-rw-r--r--test/.excludes-prism/TestSyntax.rb26
-rw-r--r--test/.excludes-prism/TestUnicodeEscape.rb1
-rw-r--r--test/error_highlight/test_error_highlight.rb11
-rw-r--r--test/irb/command/test_custom_command.rb44
-rw-r--r--test/irb/command/test_disable_irb.rb28
-rw-r--r--test/irb/command/test_help.rb2
-rw-r--r--test/irb/helper.rb4
-rw-r--r--test/irb/test_debugger_integration.rb16
-rw-r--r--test/irb/test_init.rb92
-rw-r--r--test/irb/test_irb.rb7
-rw-r--r--test/logger/test_logperiod.rb83
-rw-r--r--test/objspace/test_objspace.rb2
-rw-r--r--test/objspace/test_ractor.rb24
-rw-r--r--test/openssl/test_pair.rb9
-rw-r--r--test/openssl/test_pkcs12.rb31
-rw-r--r--test/openssl/test_pkcs7.rb6
-rw-r--r--test/openssl/test_ts.rb2
-rw-r--r--test/openssl/test_x509cert.rb32
-rw-r--r--test/prism/api/command_line_test.rb (renamed from test/prism/command_line_test.rb)10
-rw-r--r--test/prism/api/dump_test.rb56
-rw-r--r--test/prism/api/parse_comments_test.rb (renamed from test/prism/parse_comments_test.rb)14
-rw-r--r--test/prism/api/parse_stream_test.rb (renamed from test/prism/parse_stream_test.rb)11
-rw-r--r--test/prism/api/parse_success_test.rb16
-rw-r--r--test/prism/api/parse_test.rb66
-rw-r--r--test/prism/bom_test.rb2
-rw-r--r--test/prism/encoding/encodings_test.rb101
-rw-r--r--test/prism/encoding/regular_expression_encoding_test.rb131
-rw-r--r--test/prism/encoding/string_encoding_test.rb136
-rw-r--r--test/prism/encoding/symbol_encoding_test.rb108
-rw-r--r--test/prism/encoding_test.rb577
-rw-r--r--test/prism/errors_test.rb130
-rw-r--r--test/prism/fixtures/break.txt4
-rw-r--r--test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt2
-rw-r--r--test/prism/fixtures/whitequark/method_definition_in_while_cond.txt7
-rw-r--r--test/prism/fixtures_test.rb21
-rw-r--r--test/prism/format_errors_test.rb24
-rw-r--r--test/prism/fuzzer_test.rb10
-rw-r--r--test/prism/heredoc_dedent_test.rb133
-rw-r--r--test/prism/lex_test.rb90
-rw-r--r--test/prism/library_symbols_test.rb2
-rw-r--r--test/prism/locals_test.rb197
-rw-r--r--test/prism/magic_comment_test.rb121
-rw-r--r--test/prism/memsize_test.rb17
-rw-r--r--test/prism/newline_offsets_test.rb22
-rw-r--r--test/prism/newline_test.rb30
-rw-r--r--test/prism/onigmo_test.rb66
-rw-r--r--test/prism/parse_test.rb371
-rw-r--r--test/prism/parser_test.rb186
-rw-r--r--test/prism/regexp_test.rb126
-rw-r--r--test/prism/result/attribute_write_test.rb (renamed from test/prism/attribute_write_test.rb)10
-rw-r--r--test/prism/result/comments_test.rb (renamed from test/prism/comments_test.rb)4
-rw-r--r--test/prism/result/constant_path_node_test.rb (renamed from test/prism/constant_path_node_test.rb)16
-rw-r--r--test/prism/result/equality_test.rb22
-rw-r--r--test/prism/result/heredoc_test.rb19
-rw-r--r--test/prism/result/index_write_test.rb (renamed from test/prism/index_write_test.rb)66
-rw-r--r--test/prism/result/integer_base_flags_test.rb33
-rw-r--r--test/prism/result/integer_parse_test.rb (renamed from test/prism/integer_parse_test.rb)8
-rw-r--r--test/prism/result/numeric_value_test.rb21
-rw-r--r--test/prism/result/overlap_test.rb43
-rw-r--r--test/prism/result/redundant_return_test.rb (renamed from test/prism/redundant_return_test.rb)2
-rw-r--r--test/prism/result/regular_expression_options_test.rb25
-rw-r--r--test/prism/result/source_location_test.rb (renamed from test/prism/location_test.rb)41
-rw-r--r--test/prism/result/static_inspect_test.rb (renamed from test/prism/static_inspect_test.rb)7
-rw-r--r--test/prism/result/static_literals_test.rb (renamed from test/prism/static_literals_test.rb)2
-rw-r--r--test/prism/result/warnings_test.rb (renamed from test/prism/warnings_test.rb)31
-rw-r--r--test/prism/ruby/compiler_test.rb (renamed from test/prism/compiler_test.rb)2
-rw-r--r--test/prism/ruby/desugar_compiler_test.rb (renamed from test/prism/desugar_compiler_test.rb)2
-rw-r--r--test/prism/ruby/dispatcher_test.rb (renamed from test/prism/dispatcher_test.rb)2
-rw-r--r--test/prism/ruby/location_test.rb173
-rw-r--r--test/prism/ruby/parameters_signature_test.rb (renamed from test/prism/parameters_signature_test.rb)20
-rw-r--r--test/prism/ruby/parser_test.rb290
-rw-r--r--test/prism/ruby/pattern_test.rb (renamed from test/prism/pattern_test.rb)2
-rw-r--r--test/prism/ruby/reflection_test.rb (renamed from test/prism/reflection_test.rb)2
-rw-r--r--test/prism/ruby/ripper_test.rb (renamed from test/prism/ripper_test.rb)33
-rw-r--r--test/prism/ruby/ruby_parser_test.rb127
-rw-r--r--test/prism/ruby/tunnel_test.rb26
-rw-r--r--test/prism/ruby_api_test.rb275
-rw-r--r--test/prism/ruby_parser_test.rb144
-rw-r--r--test/prism/snapshots/arrays.txt20
-rw-r--r--test/prism/snapshots/blocks.txt4
-rw-r--r--test/prism/snapshots/boolean_operators.txt4
-rw-r--r--test/prism/snapshots/break.txt156
-rw-r--r--test/prism/snapshots/constants.txt123
-rw-r--r--test/prism/snapshots/defined.txt4
-rw-r--r--test/prism/snapshots/if.txt2
-rw-r--r--test/prism/snapshots/method_calls.txt49
-rw-r--r--test/prism/snapshots/methods.txt2
-rw-r--r--test/prism/snapshots/modules.txt28
-rw-r--r--test/prism/snapshots/numbers.txt73
-rw-r--r--test/prism/snapshots/patterns.txt84
-rw-r--r--test/prism/snapshots/rescue.txt2
-rw-r--r--test/prism/snapshots/seattlerb/assoc_label.txt2
-rw-r--r--test/prism/snapshots/seattlerb/bug_249.txt2
-rw-r--r--test/prism/snapshots/seattlerb/bug_hash_args.txt2
-rw-r--r--test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_assoc.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_arg_kwsplat.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt6
-rw-r--r--test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_new.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt2
-rw-r--r--test/prism/snapshots/seattlerb/call_kwsplat.txt2
-rw-r--r--test/prism/snapshots/seattlerb/case_in_86.txt7
-rw-r--r--test/prism/snapshots/seattlerb/case_in_86_2.txt7
-rw-r--r--test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt7
-rw-r--r--test/prism/snapshots/seattlerb/case_in_multiple.txt14
-rw-r--r--test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt14
-rw-r--r--test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt7
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_and1.txt11
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_and2.txt7
-rw-r--r--test/prism/snapshots/seattlerb/const_op_asgn_or.txt7
-rw-r--r--test/prism/snapshots/seattlerb/dasgn_icky2.txt61
-rw-r--r--test/prism/snapshots/seattlerb/defn_kwarg_env.txt2
-rw-r--r--test/prism/snapshots/seattlerb/difficult2_.txt2
-rw-r--r--test/prism/snapshots/seattlerb/index_0_opasgn.txt4
-rw-r--r--test/prism/snapshots/seattlerb/masgn_colon2.txt7
-rw-r--r--test/prism/snapshots/seattlerb/masgn_colon3.txt14
-rw-r--r--test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt11
-rw-r--r--test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt2
-rw-r--r--test/prism/snapshots/seattlerb/multiline_hash_declaration.txt6
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt11
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt4
-rw-r--r--test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt4
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_defn_complex.txt4
-rw-r--r--test/prism/snapshots/seattlerb/parse_line_op_asgn.txt4
-rw-r--r--test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt2
-rw-r--r--test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt4
-rw-r--r--test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt2
-rw-r--r--test/prism/snapshots/seattlerb/return_call_assocs.txt12
-rw-r--r--test/prism/snapshots/seattlerb/ruby21_numbers.txt14
-rw-r--r--test/prism/snapshots/seattlerb/safe_op_asgn.txt4
-rw-r--r--test/prism/snapshots/seattlerb/yield_arg.txt16
-rw-r--r--test/prism/snapshots/seattlerb/yield_call_assocs.txt224
-rw-r--r--test/prism/snapshots/seattlerb/yield_empty_parens.txt10
-rw-r--r--test/prism/snapshots/symbols.txt7
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/assignment.txt28
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/class.txt49
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/defs.txt14
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/literal.txt26
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/module.txt21
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/opasgn.txt64
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/send.txt12
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/since/32.txt2
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/variables.txt28
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/yield.txt56
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/literal.txt13
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/opasgn.txt4
-rw-r--r--test/prism/snapshots/whitequark/args_args_assocs.txt4
-rw-r--r--test/prism/snapshots/whitequark/args_args_assocs_comma.txt2
-rw-r--r--test/prism/snapshots/whitequark/args_assocs.txt195
-rw-r--r--test/prism/snapshots/whitequark/args_assocs_comma.txt2
-rw-r--r--test/prism/snapshots/whitequark/args_assocs_legacy.txt195
-rw-r--r--test/prism/snapshots/whitequark/bug_cmdarg.txt4
-rw-r--r--test/prism/snapshots/whitequark/casgn_scoped.txt7
-rw-r--r--test/prism/snapshots/whitequark/casgn_toplevel.txt7
-rw-r--r--test/prism/snapshots/whitequark/complex.txt13
-rw-r--r--test/prism/snapshots/whitequark/const_op_asgn.txt40
-rw-r--r--test/prism/snapshots/whitequark/const_scoped.txt7
-rw-r--r--test/prism/snapshots/whitequark/const_toplevel.txt7
-rw-r--r--test/prism/snapshots/whitequark/cpath.txt14
-rw-r--r--test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt2
-rw-r--r--test/prism/snapshots/whitequark/forwarded_kwrestarg.txt2
-rw-r--r--test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt2
-rw-r--r--test/prism/snapshots/whitequark/keyword_argument_omission.txt2
-rw-r--r--test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt2
-rw-r--r--test/prism/snapshots/whitequark/masgn_const.txt14
-rw-r--r--test/prism/snapshots/whitequark/method_definition_in_while_cond.txt199
-rw-r--r--test/prism/snapshots/whitequark/newline_in_hash_argument.txt4
-rw-r--r--test/prism/snapshots/whitequark/op_asgn.txt12
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_cmd.txt23
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_index.txt4
-rw-r--r--test/prism/snapshots/whitequark/op_asgn_index_cmd.txt4
-rw-r--r--test/prism/snapshots/whitequark/parser_bug_525.txt2
-rw-r--r--test/prism/snapshots/whitequark/rational.txt13
-rw-r--r--test/prism/snapshots/whitequark/rescue_mod_op_assign.txt4
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11380.txt2
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11873_a.txt24
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12073.txt9
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12402.txt54
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_12669.txt16
-rw-r--r--test/prism/snapshots/whitequark/send_attr_asgn.txt7
-rw-r--r--test/prism/snapshots/whitequark/var_op_asgn.txt16
-rw-r--r--test/prism/snapshots/whitequark/var_op_asgn_cmd.txt4
-rw-r--r--test/prism/snapshots/whitequark/yield.txt51
-rw-r--r--test/prism/snapshots_test.rb73
-rw-r--r--test/prism/snippets_test.rb42
-rw-r--r--test/prism/test_helper.rb213
-rw-r--r--test/prism/unescape_test.rb2
-rw-r--r--test/reline/helper.rb31
-rw-r--r--test/reline/test_ansi_with_terminfo.rb4
-rw-r--r--test/reline/test_ansi_without_terminfo.rb4
-rw-r--r--test/reline/test_config.rb98
-rw-r--r--test/reline/test_key_actor_emacs.rb271
-rw-r--r--test/reline/test_key_actor_vi.rb42
-rw-r--r--test/reline/test_key_stroke.rb56
-rw-r--r--test/reline/test_line_editor.rb10
-rw-r--r--test/reline/test_reline.rb62
-rw-r--r--test/reline/test_reline_key.rb51
-rw-r--r--test/reline/test_within_pipe.rb1
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb50
-rw-r--r--test/ripper/test_lexer.rb81
-rw-r--r--test/ripper/test_parser_events.rb32
-rw-r--r--test/ruby/test_allocation.rb121
-rw-r--r--test/ruby/test_ast.rb23
-rw-r--r--test/ruby/test_bignum.rb6
-rw-r--r--test/ruby/test_call.rb6
-rw-r--r--test/ruby/test_file.rb33
-rw-r--r--test/ruby/test_gc.rb2
-rw-r--r--test/ruby/test_gc_compact.rb22
-rw-r--r--test/ruby/test_io.rb9
-rw-r--r--test/ruby/test_iseq.rb28
-rw-r--r--test/ruby/test_literal.rb13
-rw-r--r--test/ruby/test_marshal.rb10
-rw-r--r--test/ruby/test_module.rb13
-rw-r--r--test/ruby/test_pack.rb18
-rw-r--r--test/ruby/test_parse.rb81
-rw-r--r--test/ruby/test_pattern_matching.rb2
-rw-r--r--test/ruby/test_regexp.rb6
-rw-r--r--test/ruby/test_require.rb4
-rw-r--r--test/ruby/test_rubyoptions.rb10
-rw-r--r--test/ruby/test_rubyvm.rb9
-rw-r--r--test/ruby/test_sprintf.rb11
-rw-r--r--test/ruby/test_string.rb7
-rw-r--r--test/ruby/test_symbol.rb3
-rw-r--r--test/ruby/test_syntax.rb86
-rw-r--r--test/ruby/test_thread.rb16
-rw-r--r--test/ruby/test_yjit.rb22
-rw-r--r--test/rubygems/helper.rb42
-rw-r--r--test/rubygems/test_bundled_ca.rb2
-rw-r--r--test/rubygems/test_gem.rb21
-rw-r--r--test/rubygems/test_gem_ci_detector.rb14
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_rebuild_command.rb13
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb23
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb10
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb25
-rw-r--r--test/rubygems/test_gem_platform.rb3
-rw-r--r--test/rubygems/test_gem_specification.rb27
-rw-r--r--test/rubygems/test_gem_uninstaller.rb97
-rw-r--r--test/rubygems/test_webauthn_poller.rb12
-rw-r--r--test/stringio/test_stringio.rb3
-rw-r--r--test/test_tempfile.rb50
-rw-r--r--test/test_timeout.rb4
-rw-r--r--test/win32/test_registry.rb97
-rw-r--r--test/zlib/test_zlib.rb25
267 files changed, 5218 insertions, 4087 deletions
diff --git a/test/-ext-/integer/test_my_integer.rb b/test/-ext-/integer/test_my_integer.rb
index 1b6f8489f8..0dfa234921 100644
--- a/test/-ext-/integer/test_my_integer.rb
+++ b/test/-ext-/integer/test_my_integer.rb
@@ -8,19 +8,13 @@ class Test_MyInteger < Test::Unit::TestCase
Bug::Integer::MyInteger.new.to_f
end
- begin
- Bug::Integer::MyInteger.class_eval do
- def to_f
- end
+ int = Class.new(Bug::Integer::MyInteger) do
+ def to_f
end
+ end
- assert_nothing_raised do
- Bug::Integer::MyInteger.new.to_f
- end
- ensure
- Bug::Integer::MyInteger.class_eval do
- remove_method :to_f
- end
+ assert_nothing_raised do
+ int.new.to_f
end
end
@@ -29,20 +23,14 @@ class Test_MyInteger < Test::Unit::TestCase
Bug::Integer::MyInteger.new <=> 0
end
- begin
- Bug::Integer::MyInteger.class_eval do
- def <=>(other)
- 0
- end
+ int = Class.new(Bug::Integer::MyInteger) do
+ def <=>(other)
+ 0
end
+ end
- assert_nothing_raised do
- Bug::Integer::MyInteger.new <=> 0
- end
- ensure
- Bug::Integer::MyInteger.class_eval do
- remove_method :<=>
- end
+ assert_nothing_raised do
+ int.new <=> 0
end
end
end
diff --git a/test/-ext-/string/test_chilled.rb b/test/-ext-/string/test_chilled.rb
deleted file mode 100644
index dccab61ced..0000000000
--- a/test/-ext-/string/test_chilled.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require 'test/unit'
-require '-test-/string'
-
-class Test_String_ChilledString < Test::Unit::TestCase
- def test_rb_str_chilled_p
- str = ""
- assert_equal true, Bug::String.rb_str_chilled_p(str)
- end
-
- def test_rb_str_chilled_p_frozen
- str = "".freeze
- assert_equal false, Bug::String.rb_str_chilled_p(str)
- end
-
- def test_rb_str_chilled_p_mutable
- str = "".dup
- assert_equal false, Bug::String.rb_str_chilled_p(str)
- end
-end
diff --git a/test/-ext-/thread/test_instrumentation_api.rb b/test/-ext-/thread/test_instrumentation_api.rb
index 9a3b67fa10..663e41be53 100644
--- a/test/-ext-/thread/test_instrumentation_api.rb
+++ b/test/-ext-/thread/test_instrumentation_api.rb
@@ -54,7 +54,7 @@ class TestThreadInstrumentation < Test::Unit::TestCase
thread&.join
end
- def test_muti_thread_timeline
+ def test_multi_thread_timeline
threads = nil
full_timeline = record do
threads = threaded_cpu_bound_work(1.0)
@@ -68,7 +68,7 @@ class TestThreadInstrumentation < Test::Unit::TestCase
threads.each do |thread|
timeline = timeline_for(thread, full_timeline)
assert_consistent_timeline(timeline)
- assert timeline.count(:suspended) > 1, "Expected threads to yield suspended at least once: #{timeline.inspect}"
+ assert_operator timeline.count(:suspended), :>=, 1, "Expected threads to yield suspended at least once: #{timeline.inspect}"
end
timeline = timeline_for(Thread.current, full_timeline)
diff --git a/test/.excludes-prism/TestAssignment.rb b/test/.excludes-prism/TestAssignment.rb
deleted file mode 100644
index 23a1b4c5e7..0000000000
--- a/test/.excludes-prism/TestAssignment.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_massign_order, "https://github.com/ruby/prism/issues/2370")
diff --git a/test/.excludes-prism/TestAssignmentGen.rb b/test/.excludes-prism/TestAssignmentGen.rb
deleted file mode 100644
index 5f3bb12ef7..0000000000
--- a/test/.excludes-prism/TestAssignmentGen.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_assignment, "https://github.com/ruby/prism/issues/2370")
diff --git a/test/.excludes-prism/TestCall.rb b/test/.excludes-prism/TestCall.rb
deleted file mode 100644
index 969e32ea5a..0000000000
--- a/test/.excludes-prism/TestCall.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-exclude(:test_call_op_asgn_keywords, "https://github.com/ruby/prism/issues/2438")
-exclude(:test_call_op_asgn_keywords_mutable, "https://github.com/ruby/prism/issues/2438")
-exclude(:test_kwsplat_block_order_op_asgn, "https://github.com/ruby/prism/issues/2438")
diff --git a/test/.excludes-prism/TestCoverage.rb b/test/.excludes-prism/TestCoverage.rb
deleted file mode 100644
index 20f9972f89..0000000000
--- a/test/.excludes-prism/TestCoverage.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_eval, "unknown")
diff --git a/test/.excludes-prism/TestIRB/RubyLexTest.rb b/test/.excludes-prism/TestIRB/RubyLexTest.rb
deleted file mode 100644
index 2274ae62cf..0000000000
--- a/test/.excludes-prism/TestIRB/RubyLexTest.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_code_block_open_with_should_continue, "symbol encoding")
diff --git a/test/.excludes-prism/TestISeq.rb b/test/.excludes-prism/TestISeq.rb
deleted file mode 100644
index 3768d8fc05..0000000000
--- a/test/.excludes-prism/TestISeq.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-exclude(:test_each_child, "https://github.com/ruby/prism/issues/2660")
-exclude(:test_syntax_error_message, "Assertion checks against specific error format")
-exclude(:test_trace_points, "https://github.com/ruby/prism/issues/2660")
diff --git a/test/.excludes-prism/TestM17N.rb b/test/.excludes-prism/TestM17N.rb
index 25c4394634..7f8c44d02a 100644
--- a/test/.excludes-prism/TestM17N.rb
+++ b/test/.excludes-prism/TestM17N.rb
@@ -1,3 +1 @@
-exclude(:test_regexp_ascii, "https://github.com/ruby/prism/issues/2664")
-exclude(:test_regexp_usascii, "unknown")
-exclude(:test_string_mixed_unicode, "unknown")
+exclude(:test_regexp_usascii, "https://bugs.ruby-lang.org/issues/20504")
diff --git a/test/.excludes-prism/TestMixedUnicodeEscape.rb b/test/.excludes-prism/TestMixedUnicodeEscape.rb
index 09e3cc168b..7bf964ebf1 100644
--- a/test/.excludes-prism/TestMixedUnicodeEscape.rb
+++ b/test/.excludes-prism/TestMixedUnicodeEscape.rb
@@ -1 +1 @@
-exclude(:test_basic, "unknown")
+exclude(:test_basic, "https://bugs.ruby-lang.org/issues/20504")
diff --git a/test/.excludes-prism/TestParse.rb b/test/.excludes-prism/TestParse.rb
deleted file mode 100644
index 34b4e9c111..0000000000
--- a/test/.excludes-prism/TestParse.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-exclude(:test_error_def_in_argument, "unknown")
-exclude(:test_global_variable, "unknown")
-exclude(:test_invalid_char, "unknown")
-exclude(:test_location_of_invalid_token, "unknown")
-exclude(:test_op_asgn1_with_block, "unknown")
-exclude(:test_percent, "unknown")
-exclude(:test_question, "unknown")
-exclude(:test_shareable_constant_value_ignored, "unknown")
-exclude(:test_shareable_constant_value_nested, "ractor support")
-exclude(:test_shareable_constant_value_nonliteral, "ractor support")
-exclude(:test_shareable_constant_value_simple, "ractor support")
-exclude(:test_shareable_constant_value_unfrozen, "ractor support")
-exclude(:test_shareable_constant_value_unshareable_literal, "ractor support")
-exclude(:test_string, "unknown")
-exclude(:test_truncated_source_line, "unknown")
-exclude(:test_unexpected_eof, "unknown")
-exclude(:test_unexpected_token_after_numeric, "unknown")
-exclude(:test_unused_variable, "missing warning")
-exclude(:test_void_value_in_rhs, "unknown")
diff --git a/test/.excludes-prism/TestPatternMatching.rb b/test/.excludes-prism/TestPatternMatching.rb
deleted file mode 100644
index cfd0c6bed9..0000000000
--- a/test/.excludes-prism/TestPatternMatching.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-exclude(:test_hash_pattern, "useless literal warning missing")
-exclude(:test_invalid_syntax, "[a:] is disallowed")
diff --git a/test/.excludes-prism/TestRegexp.rb b/test/.excludes-prism/TestRegexp.rb
deleted file mode 100644
index 62e704fac5..0000000000
--- a/test/.excludes-prism/TestRegexp.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_unescape, "unknown")
diff --git a/test/.excludes-prism/TestRequire.rb b/test/.excludes-prism/TestRequire.rb
deleted file mode 100644
index a7f66c5d80..0000000000
--- a/test/.excludes-prism/TestRequire.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_require_nonascii_path_shift_jis, "encoding")
diff --git a/test/.excludes-prism/TestRubyLiteral.rb b/test/.excludes-prism/TestRubyLiteral.rb
index 926f0c5a5e..853f23a3b9 100644
--- a/test/.excludes-prism/TestRubyLiteral.rb
+++ b/test/.excludes-prism/TestRubyLiteral.rb
@@ -1,5 +1 @@
-exclude(:test_debug_frozen_string_in_array_literal, "unknown")
-exclude(:test_debug_frozen_string, "unknown")
-exclude(:test_dregexp, "https://github.com/ruby/prism/issues/2664")
-exclude(:test_hash_value_omission, "unknown")
-exclude(:test_string, "https://github.com/ruby/prism/issues/2331")
+exclude(:test_dregexp, "https://bugs.ruby-lang.org/issues/20504")
diff --git a/test/.excludes-prism/TestRubyVM.rb b/test/.excludes-prism/TestRubyVM.rb
deleted file mode 100644
index 6d4c3ca6fe..0000000000
--- a/test/.excludes-prism/TestRubyVM.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_keep_script_lines, "unknown")
diff --git a/test/.excludes-prism/TestSetTraceFunc.rb b/test/.excludes-prism/TestSetTraceFunc.rb
deleted file mode 100644
index 036faef650..0000000000
--- a/test/.excludes-prism/TestSetTraceFunc.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_return, "unknown")
diff --git a/test/.excludes-prism/TestSyntax.rb b/test/.excludes-prism/TestSyntax.rb
deleted file mode 100644
index db53263a26..0000000000
--- a/test/.excludes-prism/TestSyntax.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-exclude(:test__END___cr, "unknown")
-exclude(:test_anonymous_block_forwarding, "unknown")
-exclude(:test_anonymous_keyword_rest_forwarding, "unknown")
-exclude(:test_anonymous_rest_forwarding, "unknown")
-exclude(:test_argument_forwarding_with_super, "unknown")
-exclude(:test_argument_forwarding, "unknown")
-exclude(:test_brace_after_literal_argument, "unknown")
-exclude(:test_dedented_heredoc_concatenation, "unknown")
-exclude(:test_dedented_heredoc_continued_line, "unknown")
-exclude(:test_duplicated_when, "unknown")
-exclude(:test_error_message_encoding, "unknown")
-exclude(:test_heredoc_cr, "unknown")
-exclude(:test_heredoc_no_terminator, "unknown")
-exclude(:test_it, "https://github.com/ruby/prism/issues/2323")
-exclude(:test_keyword_invalid_name, "unknown")
-exclude(:test_keyword_self_reference, "unknown")
-exclude(:test_keywords_specified_and_not_accepted, "unknown")
-exclude(:test_methoddef_endless_command, "unknown")
-exclude(:test_numbered_parameter, "unknown")
-exclude(:test_optional_self_reference, "unknown")
-exclude(:test_safe_call_in_massign_lhs, "unknown")
-exclude(:test_syntax_error_at_newline, "unknown")
-exclude(:test_unexpected_fraction, "unknown")
-exclude(:test_unterminated_heredoc_cr, "unknown")
-exclude(:test_warn_balanced, "unknown")
-exclude(:test_warn_unreachable, "unknown")
diff --git a/test/.excludes-prism/TestUnicodeEscape.rb b/test/.excludes-prism/TestUnicodeEscape.rb
deleted file mode 100644
index add4911bc2..0000000000
--- a/test/.excludes-prism/TestUnicodeEscape.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_fail, "unknown")
diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb
index 2095970af1..9af530b69f 100644
--- a/test/error_highlight/test_error_highlight.rb
+++ b/test/error_highlight/test_error_highlight.rb
@@ -1,9 +1,17 @@
require "test/unit"
require "error_highlight"
+require "did_you_mean"
require "tempfile"
class ErrorHighlightTest < Test::Unit::TestCase
+ # We can't revisit instruction sequences to find node ids if the prism
+ # compiler was used instead of the parse.y compiler. In that case, we'll omit
+ # some tests.
+ def self.compiling_with_prism?
+ RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
+ end
+
class DummyFormatter
def self.message_for(corrections)
""
@@ -868,7 +876,7 @@ uninitialized constant ErrorHighlightTest::NotDefined
end
end
- if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH)
+ if ErrorHighlight.const_get(:Spotter).const_get(:OPT_GETCONSTANT_PATH) && !compiling_with_prism?
def test_COLON2_5
# Unfortunately, we cannot identify which `NotDefined` caused the NameError
assert_error_message(NameError, <<~END) do
@@ -1334,6 +1342,7 @@ undefined method `foo' for #{ NIL_RECV_MESSAGE }
def test_spot_with_node
omit unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location)
+ omit if ErrorHighlightTest.compiling_with_prism?
begin
raise_name_error
diff --git a/test/irb/command/test_custom_command.rb b/test/irb/command/test_custom_command.rb
index 6642d2b160..3a3ad11d5a 100644
--- a/test/irb/command/test_custom_command.rb
+++ b/test/irb/command/test_custom_command.rb
@@ -5,7 +5,7 @@ require_relative "../helper"
module TestIRB
class CustomCommandIntegrationTest < TestIRB::IntegrationTestCase
- def test_command_regsitration_can_happen_after_irb_require
+ def test_command_registration_can_happen_after_irb_require
write_ruby <<~RUBY
require "irb"
require "irb/command"
@@ -15,7 +15,6 @@ module TestIRB
description 'print_command'
def execute(*)
puts "Hello from PrintCommand"
- nil
end
end
@@ -25,14 +24,14 @@ module TestIRB
RUBY
output = run_ruby_file do
- type "print!\n"
+ type "print!"
type "exit"
end
assert_include(output, "Hello from PrintCommand")
end
- def test_command_regsitration_accepts_string_too
+ def test_command_registration_accepts_string_too
write_ruby <<~RUBY
require "irb/command"
@@ -41,7 +40,6 @@ module TestIRB
description 'print_command'
def execute(*)
puts "Hello from PrintCommand"
- nil
end
end
@@ -51,14 +49,14 @@ module TestIRB
RUBY
output = run_ruby_file do
- type "print!\n"
+ type "print!"
type "exit"
end
assert_include(output, "Hello from PrintCommand")
end
- def test_arguments_propogation
+ def test_arguments_propagation
write_ruby <<~RUBY
require "irb/command"
@@ -69,7 +67,6 @@ module TestIRB
$nth_execution ||= 0
puts "\#{$nth_execution} arg=\#{arg.inspect}"
$nth_execution += 1
- nil
end
end
@@ -79,9 +76,9 @@ module TestIRB
RUBY
output = run_ruby_file do
- type "print_arg\n"
+ type "print_arg"
type "print_arg \n"
- type "print_arg a r g\n"
+ type "print_arg a r g"
type "print_arg a r g \n"
type "exit"
end
@@ -103,7 +100,6 @@ module TestIRB
$nth_execution ||= 1
puts "\#{$nth_execution} FooBar executed"
$nth_execution += 1
- nil
end
end
@@ -123,5 +119,31 @@ module TestIRB
assert_include(output, "2 FooBar executed")
assert_include(output, "foobar_description")
end
+
+ def test_no_meta_command_also_works
+ write_ruby <<~RUBY
+ require "irb/command"
+
+ class NoMetaCommand < IRB::Command::Base
+ def execute(*)
+ puts "This command does not override meta attributes"
+ end
+ end
+
+ IRB::Command.register(:no_meta, NoMetaCommand)
+
+ binding.irb
+ RUBY
+
+ output = run_ruby_file do
+ type "no_meta"
+ type "help no_meta"
+ type "exit"
+ end
+
+ assert_include(output, "This command does not override meta attributes")
+ assert_include(output, "No description provided.")
+ assert_not_include(output, "Maybe IRB bug")
+ end
end
end
diff --git a/test/irb/command/test_disable_irb.rb b/test/irb/command/test_disable_irb.rb
new file mode 100644
index 0000000000..14a20043d5
--- /dev/null
+++ b/test/irb/command/test_disable_irb.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: false
+require 'irb'
+
+require_relative "../helper"
+
+module TestIRB
+ class DisableIRBTest < IntegrationTestCase
+ def test_disable_irb_disable_further_irb_breakpoints
+ write_ruby <<~'ruby'
+ puts "First line"
+ puts "Second line"
+ binding.irb
+ puts "Third line"
+ binding.irb
+ puts "Fourth line"
+ ruby
+
+ output = run_ruby_file do
+ type "disable_irb"
+ end
+
+ assert_match(/First line\r\n/, output)
+ assert_match(/Second line\r\n/, output)
+ assert_match(/Third line\r\n/, output)
+ assert_match(/Fourth line\r\n/, output)
+ end
+ end
+end
diff --git a/test/irb/command/test_help.rb b/test/irb/command/test_help.rb
index df3753dae7..b34832b022 100644
--- a/test/irb/command/test_help.rb
+++ b/test/irb/command/test_help.rb
@@ -69,7 +69,7 @@ module TestIRB
type "exit"
end
- assert_match(/Helper methods\s+conf\s+Returns the current context/, out)
+ assert_match(/Helper methods\s+conf\s+Returns the current IRB context/, out)
end
end
end
diff --git a/test/irb/helper.rb b/test/irb/helper.rb
index 591bd05b7a..acaf6277f3 100644
--- a/test/irb/helper.rb
+++ b/test/irb/helper.rb
@@ -121,7 +121,9 @@ module TestIRB
@envs["XDG_CONFIG_HOME"] ||= tmp_dir
@envs["IRBRC"] = nil unless @envs.key?("IRBRC")
- PTY.spawn(@envs.merge("TERM" => "dumb"), *cmd) do |read, write, pid|
+ envs_for_spawn = @envs.merge('TERM' => 'dumb', 'TEST_IRB_FORCE_INTERACTIVE' => 'true')
+
+ PTY.spawn(envs_for_spawn, *cmd) do |read, write, pid|
Timeout.timeout(TIMEOUT_SEC) do
while line = safe_gets(read)
lines << line
diff --git a/test/irb/test_debugger_integration.rb b/test/irb/test_debugger_integration.rb
index eca40c5702..8b1bddea17 100644
--- a/test/irb/test_debugger_integration.rb
+++ b/test/irb/test_debugger_integration.rb
@@ -67,6 +67,22 @@ module TestIRB
assert_match(/IRB is already running with a debug session/, output)
end
+ def test_debug_command_can_only_be_called_from_binding_irb
+ write_ruby <<~'ruby'
+ require "irb"
+ # trick test framework
+ puts "binding.irb"
+ IRB.start
+ ruby
+
+ output = run_ruby_file do
+ type "debug"
+ type "exit"
+ end
+
+ assert_include(output, "Debugging commands are only available when IRB is started with binding.irb")
+ end
+
def test_next
write_ruby <<~'ruby'
binding.irb
diff --git a/test/irb/test_init.rb b/test/irb/test_init.rb
index f11d7398c8..3207c2898b 100644
--- a/test/irb/test_init.rb
+++ b/test/irb/test_init.rb
@@ -268,15 +268,95 @@ module TestIRB
end
end
+ class ConfigValidationTest < TestCase
+ def setup
+ @original_home = ENV["HOME"]
+ @original_irbrc = ENV["IRBRC"]
+ # To prevent the test from using the user's .irbrc file
+ ENV["HOME"] = @home = Dir.mktmpdir
+ IRB.instance_variable_set(:@existing_rc_name_generators, nil)
+ super
+ end
+
+ def teardown
+ super
+ ENV["IRBRC"] = @original_irbrc
+ ENV["HOME"] = @original_home
+ File.unlink(@irbrc)
+ Dir.rmdir(@home)
+ end
+
+ def test_irb_name_converts_non_string_values_to_string
+ assert_no_irb_validation_error(<<~'RUBY')
+ IRB.conf[:IRB_NAME] = :foo
+ RUBY
+
+ assert_equal "foo", IRB.conf[:IRB_NAME]
+ end
+
+ def test_irb_rc_name_only_takes_callable_objects
+ assert_irb_validation_error(<<~'RUBY', "IRB.conf[:IRB_RC] should be a callable object. Got :foo.")
+ IRB.conf[:IRB_RC] = :foo
+ RUBY
+ end
+
+ def test_back_trace_limit_only_accepts_integers
+ assert_irb_validation_error(<<~'RUBY', "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got \"foo\".")
+ IRB.conf[:BACK_TRACE_LIMIT] = "foo"
+ RUBY
+ end
+
+ def test_prompt_only_accepts_hash
+ assert_irb_validation_error(<<~'RUBY', "IRB.conf[:PROMPT] should be a Hash. Got \"foo\".")
+ IRB.conf[:PROMPT] = "foo"
+ RUBY
+ end
+
+ def test_eval_history_only_accepts_integers
+ assert_irb_validation_error(<<~'RUBY', "IRB.conf[:EVAL_HISTORY] should be an integer. Got \"foo\".")
+ IRB.conf[:EVAL_HISTORY] = "foo"
+ RUBY
+ end
+
+ private
+
+ def assert_irb_validation_error(rc_content, error_message)
+ write_rc rc_content
+
+ assert_raise_with_message(TypeError, error_message) do
+ IRB.setup(__FILE__)
+ end
+ end
+
+ def assert_no_irb_validation_error(rc_content)
+ write_rc rc_content
+
+ assert_nothing_raised do
+ IRB.setup(__FILE__)
+ end
+ end
+
+ def write_rc(content)
+ @irbrc = Tempfile.new('irbrc')
+ @irbrc.write(content)
+ @irbrc.close
+ ENV['IRBRC'] = @irbrc.path
+ end
+ end
+
class InitIntegrationTest < IntegrationTestCase
- def test_load_error_in_rc_file_is_warned
- write_rc <<~'IRBRC'
- require "file_that_does_not_exist"
- IRBRC
+ def setup
+ super
write_ruby <<~'RUBY'
binding.irb
RUBY
+ end
+
+ def test_load_error_in_rc_file_is_warned
+ write_rc <<~'IRBRC'
+ require "file_that_does_not_exist"
+ IRBRC
output = run_ruby_file do
type "'foobar'"
@@ -293,10 +373,6 @@ module TestIRB
raise "I'm an error"
IRBRC
- write_ruby <<~'RUBY'
- binding.irb
- RUBY
-
output = run_ruby_file do
type "'foobar'"
type "exit"
diff --git a/test/irb/test_irb.rb b/test/irb/test_irb.rb
index 28be744088..3d8044c5a1 100644
--- a/test/irb/test_irb.rb
+++ b/test/irb/test_irb.rb
@@ -825,6 +825,13 @@ module TestIRB
end
class BacktraceFilteringTest < TestIRB::IntegrationTestCase
+ def setup
+ super
+ # These tests are sensitive to warnings, so we disable them
+ original_rubyopt = [ENV["RUBYOPT"], @envs["RUBYOPT"]].compact.join(" ")
+ @envs["RUBYOPT"] = original_rubyopt + " -W0"
+ end
+
def test_backtrace_filtering
write_ruby <<~'RUBY'
def foo
diff --git a/test/logger/test_logperiod.rb b/test/logger/test_logperiod.rb
index 6e6e5e9533..ee38d877c6 100644
--- a/test/logger/test_logperiod.rb
+++ b/test/logger/test_logperiod.rb
@@ -1,80 +1,67 @@
# coding: US-ASCII
# frozen_string_literal: false
-require 'logger'
-require 'time'
+require "logger"
+require "time"
class TestLogPeriod < Test::Unit::TestCase
def test_next_rotate_time
time = Time.parse("2019-07-18 13:52:02")
- daily_result = Logger::Period.next_rotate_time(time, 'daily')
- next_day = Time.parse("2019-07-19 00:00:00")
- assert_equal(next_day, daily_result)
+ assert_next_rotate_time_words(time, "2019-07-19 00:00:00", ["daily", :daily])
+ assert_next_rotate_time_words(time, "2019-07-21 00:00:00", ["weekly", :weekly])
+ assert_next_rotate_time_words(time, "2019-08-01 00:00:00", ["monthly", :monthly])
- weekly_result = Logger::Period.next_rotate_time(time, 'weekly')
- next_week = Time.parse("2019-07-21 00:00:00")
- assert_equal(next_week, weekly_result)
-
- monthly_result = Logger::Period.next_rotate_time(time, 'monthly')
- next_month = Time.parse("2019-08-1 00:00:00")
- assert_equal(next_month, monthly_result)
-
- assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, 'invalid') }
+ assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, "invalid") }
end
def test_next_rotate_time_extreme_cases
# First day of Month and Saturday
time = Time.parse("2018-07-01 00:00:00")
- daily_result = Logger::Period.next_rotate_time(time, 'daily')
- next_day = Time.parse("2018-07-02 00:00:00")
- assert_equal(next_day, daily_result)
-
- weekly_result = Logger::Period.next_rotate_time(time, 'weekly')
- next_week = Time.parse("2018-07-08 00:00:00")
- assert_equal(next_week, weekly_result)
+ assert_next_rotate_time_words(time, "2018-07-02 00:00:00", ["daily", :daily])
+ assert_next_rotate_time_words(time, "2018-07-08 00:00:00", ["weekly", :weekly])
+ assert_next_rotate_time_words(time, "2018-08-01 00:00:00", ["monthly", :monthly])
- monthly_result = Logger::Period.next_rotate_time(time, 'monthly')
- next_month = Time.parse("2018-08-1 00:00:00")
- assert_equal(next_month, monthly_result)
-
- assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, 'invalid') }
+ assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, "invalid") }
end
def test_previous_period_end
time = Time.parse("2019-07-18 13:52:02")
- daily_result = Logger::Period.previous_period_end(time, 'daily')
- day_ago = Time.parse("2019-07-17 23:59:59")
- assert_equal(day_ago, daily_result)
-
- weekly_result = Logger::Period.previous_period_end(time, 'weekly')
- week_ago = Time.parse("2019-07-13 23:59:59")
- assert_equal(week_ago, weekly_result)
-
- monthly_result = Logger::Period.previous_period_end(time, 'monthly')
- month_ago = Time.parse("2019-06-30 23:59:59")
- assert_equal(month_ago, monthly_result)
+ assert_previous_period_end_words(time, "2019-07-17 23:59:59", ["daily", :daily])
+ assert_previous_period_end_words(time, "2019-07-13 23:59:59", ["weekly", :weekly])
+ assert_previous_period_end_words(time, "2019-06-30 23:59:59", ["monthly", :monthly])
- assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, 'invalid') }
+ assert_raise(ArgumentError) { Logger::Period.previous_period_end(time, "invalid") }
end
def test_previous_period_end_extreme_cases
# First day of Month and Saturday
time = Time.parse("2018-07-01 00:00:00")
+ previous_date = "2018-06-30 23:59:59"
- daily_result = Logger::Period.previous_period_end(time, 'daily')
- day_ago = Time.parse("2018-06-30 23:59:59")
- assert_equal(day_ago, daily_result)
+ assert_previous_period_end_words(time, previous_date, ["daily", :daily])
+ assert_previous_period_end_words(time, previous_date, ["weekly", :weekly])
+ assert_previous_period_end_words(time, previous_date, ["monthly", :monthly])
- weekly_result = Logger::Period.previous_period_end(time, 'weekly')
- week_ago = Time.parse("2018-06-30 23:59:59")
- assert_equal(week_ago, weekly_result)
+ assert_raise(ArgumentError) { Logger::Period.previous_period_end(time, "invalid") }
+ end
+
+ private
- monthly_result = Logger::Period.previous_period_end(time, 'monthly')
- month_ago = Time.parse("2018-06-30 23:59:59")
- assert_equal(month_ago, monthly_result)
+ def assert_next_rotate_time_words(time, next_date, words)
+ assert_time_words(:next_rotate_time, time, next_date, words)
+ end
+
+ def assert_previous_period_end_words(time, previous_date, words)
+ assert_time_words(:previous_period_end, time, previous_date, words)
+ end
- assert_raise(ArgumentError) { Logger::Period.next_rotate_time(time, 'invalid') }
+ def assert_time_words(method, time, date, words)
+ words.each do |word|
+ daily_result = Logger::Period.public_send(method, time, word)
+ expected_result = Time.parse(date)
+ assert_equal(expected_result, daily_result)
+ end
end
end
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index b798b897b4..17f73e4433 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -416,7 +416,7 @@ class TestObjSpace < Test::Unit::TestCase
assert_equal('true', ObjectSpace.dump(true))
assert_equal('false', ObjectSpace.dump(false))
assert_equal('0', ObjectSpace.dump(0))
- assert_equal('{"type":"SYMBOL", "value":"foo"}', ObjectSpace.dump(:foo))
+ assert_equal('{"type":"SYMBOL", "value":"test_dump_special_consts"}', ObjectSpace.dump(:test_dump_special_consts))
end
def test_dump_singleton_class
diff --git a/test/objspace/test_ractor.rb b/test/objspace/test_ractor.rb
index b7008ea731..4901eeae2e 100644
--- a/test/objspace/test_ractor.rb
+++ b/test/objspace/test_ractor.rb
@@ -1,17 +1,17 @@
require "test/unit"
class TestObjSpaceRactor < Test::Unit::TestCase
- def test_tracing_does_not_crash
- assert_ractor(<<~RUBY, require: 'objspace')
- ObjectSpace.trace_object_allocations do
- r = Ractor.new do
- obj = 'a' * 1024
- Ractor.yield obj
- end
+ def test_tracing_does_not_crash
+ assert_ractor(<<~RUBY, require: 'objspace')
+ ObjectSpace.trace_object_allocations do
+ r = Ractor.new do
+ obj = 'a' * 1024
+ Ractor.yield obj
+ end
- r.take
- r.take
- end
- RUBY
- end
+ r.take
+ r.take
+ end
+ RUBY
+ end
end
diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb
index b616883925..66e36a7ab4 100644
--- a/test/openssl/test_pair.rb
+++ b/test/openssl/test_pair.rb
@@ -250,12 +250,17 @@ module OpenSSL::TestPairM
buf = +"garbage"
assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
- assert_equal "", buf
+ assert_equal "garbage", buf
s1.close
buf = +"garbage"
- assert_equal nil, s2.read(100, buf)
+ assert_nil s2.read(100, buf)
assert_equal "", buf
+
+ buf = +"garbage"
+ ret = s2.read(0, buf)
+ assert_same buf, ret
+ assert_equal "", ret
}
end
diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb
index e6b91b52af..faf26c9e3e 100644
--- a/test/openssl/test_pkcs12.rb
+++ b/test/openssl/test_pkcs12.rb
@@ -159,7 +159,6 @@ module OpenSSL
DEFAULT_PBE_PKEYS,
DEFAULT_PBE_CERTS,
nil,
- nil,
2048
)
@@ -178,6 +177,36 @@ module OpenSSL
end
end
+ def test_create_with_keytype
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ @mykey,
+ @mycert,
+ [],
+ DEFAULT_PBE_PKEYS,
+ DEFAULT_PBE_CERTS,
+ nil,
+ nil,
+ OpenSSL::PKCS12::KEY_SIG
+ )
+
+ assert_raise(ArgumentError) do
+ OpenSSL::PKCS12.create(
+ "omg",
+ "hello",
+ @mykey,
+ @mycert,
+ [],
+ DEFAULT_PBE_PKEYS,
+ DEFAULT_PBE_CERTS,
+ nil,
+ nil,
+ 2048
+ )
+ end
+ end
+
def test_new_with_no_keys
# generated with:
# openssl pkcs12 -certpbe PBE-SHA1-3DES -in <@mycert> -nokeys -export
diff --git a/test/openssl/test_pkcs7.rb b/test/openssl/test_pkcs7.rb
index 96f3f1f6be..c049ed444a 100644
--- a/test/openssl/test_pkcs7.rb
+++ b/test/openssl/test_pkcs7.rb
@@ -227,6 +227,12 @@ END
assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der)
end
+ def test_to_text
+ p7 = OpenSSL::PKCS7.new
+ p7.type = "signed"
+ assert_match(/signed/, p7.to_text)
+ end
+
def test_degenerate_pkcs7
ca_cert_pem = <<END
-----BEGIN CERTIFICATE-----
diff --git a/test/openssl/test_ts.rb b/test/openssl/test_ts.rb
index 7cb1a1fe8e..ac0469ad56 100644
--- a/test/openssl/test_ts.rb
+++ b/test/openssl/test_ts.rb
@@ -323,6 +323,8 @@ _end_of_pem_
resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
assert_equal("1.2.3.4.6", resp.token_info.policy_id)
+
+ assert_match(/1\.2\.3\.4\.6/, resp.to_text)
end
def test_response_bad_purpose
diff --git a/test/openssl/test_x509cert.rb b/test/openssl/test_x509cert.rb
index 64805504de..85c978f022 100644
--- a/test/openssl/test_x509cert.rb
+++ b/test/openssl/test_x509cert.rb
@@ -222,6 +222,29 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
}
end
+ def test_sign_and_verify_ed25519
+ # See test_ed25519 in test_pkey.rb
+
+ # Ed25519 is not FIPS-approved.
+ omit_on_fips
+
+ begin
+ ed25519 = OpenSSL::PKey::generate_key("ED25519")
+ rescue OpenSSL::PKey::PKeyError => e
+ # OpenSSL < 1.1.1
+ #
+ pend "Ed25519 is not implemented" unless openssl?(1, 1, 1)
+
+ raise e
+ end
+
+ # See ASN1_item_sign_ctx in ChangeLog for 3.8.1: https://github.com/libressl/portable/blob/master/ChangeLog
+ pend 'ASN1 signing with Ed25519 not yet working' unless openssl? or libressl?(3, 8, 1)
+
+ cert = issue_cert(@ca, ed25519, 1, [], nil, nil, digest: nil)
+ assert_equal(true, cert.verify(ed25519))
+ end
+
def test_dsa_with_sha2
cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256")
assert_equal("dsa_with_SHA256", cert.signature_algorithm)
@@ -322,6 +345,15 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase
end
end
+ def test_tbs_precert_bytes
+ pend "LibreSSL < 3.5 does not have i2d_re_X509_tbs" if libressl? && !libressl?(3, 5, 0)
+
+ cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
+ seq = OpenSSL::ASN1.decode(cert.tbs_bytes)
+
+ assert_equal 7, seq.value.size
+ end
+
private
def certificate_error_returns_false
diff --git a/test/prism/command_line_test.rb b/test/prism/api/command_line_test.rb
index 4b04c36f3a..a313845ead 100644
--- a/test/prism/command_line_test.rb
+++ b/test/prism/api/command_line_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class CommandLineTest < TestCase
@@ -67,7 +67,7 @@ module Prism
end
def test_command_line_x_implicit
- result = Prism.parse(<<~RUBY)
+ result = Prism.parse_statement(<<~RUBY)
#!/bin/bash
exit 1
@@ -75,18 +75,18 @@ module Prism
1
RUBY
- assert_kind_of IntegerNode, result.value.statements.body.first
+ assert_kind_of IntegerNode, result
end
def test_command_line_x_explicit
- result = Prism.parse(<<~RUBY, command_line: "x")
+ result = Prism.parse_statement(<<~RUBY, command_line: "x")
exit 1
#!/usr/bin/env ruby
1
RUBY
- assert_kind_of IntegerNode, result.value.statements.body.first
+ assert_kind_of IntegerNode, result
end
def test_command_line_x_implicit_fail
diff --git a/test/prism/api/dump_test.rb b/test/prism/api/dump_test.rb
new file mode 100644
index 0000000000..941088e159
--- /dev/null
+++ b/test/prism/api/dump_test.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+return if ENV["PRISM_BUILD_MINIMAL"]
+
+require_relative "../test_helper"
+
+module Prism
+ class DumpTest < TestCase
+ Fixture.each do |fixture|
+ define_method(fixture.test_name) { assert_dump(fixture) }
+ end
+
+ def test_dump
+ filepath = __FILE__
+ source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
+
+ assert_equal Prism.lex(source, filepath: filepath).value, Prism.lex_file(filepath).value
+ assert_equal Prism.dump(source, filepath: filepath), Prism.dump_file(filepath)
+
+ serialized = Prism.dump(source, filepath: filepath)
+ ast1 = Prism.load(source, serialized).value
+ ast2 = Prism.parse(source, filepath: filepath).value
+ ast3 = Prism.parse_file(filepath).value
+
+ assert_equal_nodes ast1, ast2
+ assert_equal_nodes ast2, ast3
+ end
+
+ def test_dump_file
+ assert_nothing_raised do
+ Prism.dump_file(__FILE__)
+ end
+
+ error = assert_raise Errno::ENOENT do
+ Prism.dump_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.dump_file(nil)
+ end
+ end
+
+ private
+
+ def assert_dump(fixture)
+ source = fixture.read
+
+ result = Prism.parse(source, filepath: fixture.path)
+ dumped = Prism.dump(source, filepath: fixture.path)
+
+ assert_equal_nodes(result.value, Prism.load(source, dumped).value)
+ end
+ end
+end
diff --git a/test/prism/parse_comments_test.rb b/test/prism/api/parse_comments_test.rb
index 30086e3155..4dbcca1827 100644
--- a/test/prism/parse_comments_test.rb
+++ b/test/prism/api/parse_comments_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class ParseCommentsTest < TestCase
@@ -17,5 +17,17 @@ module Prism
assert_kind_of Array, comments
assert_equal 1, comments.length
end
+
+ def test_parse_file_comments_error
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_file_comments("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_file_comments(nil)
+ end
+ end
end
end
diff --git a/test/prism/parse_stream_test.rb b/test/prism/api/parse_stream_test.rb
index 9e6347b92b..0edee74cc2 100644
--- a/test/prism/parse_stream_test.rb
+++ b/test/prism/api/parse_stream_test.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
-require "stringio"
+require_relative "../test_helper"
module Prism
class ParseStreamTest < TestCase
@@ -10,7 +9,7 @@ module Prism
result = Prism.parse_stream(io)
assert result.success?
- assert_kind_of Prism::CallNode, result.value.statements.body.first
+ assert_kind_of Prism::CallNode, result.statement
end
def test_multi_line
@@ -18,8 +17,8 @@ module Prism
result = Prism.parse_stream(io)
assert result.success?
- assert_kind_of Prism::CallNode, result.value.statements.body.first
- assert_kind_of Prism::CallNode, result.value.statements.body.last
+ assert_kind_of Prism::CallNode, result.statement
+ assert_kind_of Prism::CallNode, result.statement
end
def test_multi_read
@@ -27,7 +26,7 @@ module Prism
result = Prism.parse_stream(io)
assert result.success?
- assert_kind_of Prism::CallNode, result.value.statements.body.first
+ assert_kind_of Prism::CallNode, result.statement
end
def test___END__
diff --git a/test/prism/api/parse_success_test.rb b/test/prism/api/parse_success_test.rb
new file mode 100644
index 0000000000..2caaa5136e
--- /dev/null
+++ b/test/prism/api/parse_success_test.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class ParseSuccessTest < TestCase
+ def test_parse_success?
+ assert Prism.parse_success?("1")
+ refute Prism.parse_success?("<>")
+ end
+
+ def test_parse_file_success?
+ assert Prism.parse_file_success?(__FILE__)
+ end
+ end
+end
diff --git a/test/prism/api/parse_test.rb b/test/prism/api/parse_test.rb
new file mode 100644
index 0000000000..864d38461a
--- /dev/null
+++ b/test/prism/api/parse_test.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class ParseTest < TestCase
+ def test_parse_empty_string
+ result = Prism.parse("")
+ assert_equal [], result.value.statements.body
+ end
+
+ def test_parse_takes_file_path
+ filepath = "filepath.rb"
+ result = Prism.parse("def foo; __FILE__; end", filepath: filepath)
+
+ assert_equal filepath, find_source_file_node(result.value).filepath
+ end
+
+ def test_parse_takes_line
+ line = 4
+ result = Prism.parse("def foo\n __FILE__\nend", line: line)
+
+ assert_equal line, result.value.location.start_line
+ assert_equal line + 1, find_source_file_node(result.value).location.start_line
+
+ result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
+ assert_equal line, result.value.first.location.start_line
+ end
+
+ def test_parse_takes_negative_lines
+ line = -2
+ result = Prism.parse("def foo\n __FILE__\nend", line: line)
+
+ assert_equal line, result.value.location.start_line
+ assert_equal line + 1, find_source_file_node(result.value).location.start_line
+
+ result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
+ assert_equal line, result.value.first.location.start_line
+ end
+
+ def test_parse_file
+ node = Prism.parse_file(__FILE__).value
+ assert_kind_of ProgramNode, node
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_file(nil)
+ end
+ end
+
+ private
+
+ def find_source_file_node(program)
+ queue = [program]
+ while (node = queue.shift)
+ return node if node.is_a?(SourceFileNode)
+ queue.concat(node.compact_child_nodes)
+ end
+ end
+ end
+end
diff --git a/test/prism/bom_test.rb b/test/prism/bom_test.rb
index 1525caf458..890bc4b36c 100644
--- a/test/prism/bom_test.rb
+++ b/test/prism/bom_test.rb
@@ -2,7 +2,7 @@
# Don't bother checking this on these engines, this is such a specific Ripper
# test.
-return if RUBY_ENGINE == "jruby" || RUBY_ENGINE == "truffleruby"
+return if RUBY_ENGINE != "ruby"
require_relative "test_helper"
diff --git a/test/prism/encoding/encodings_test.rb b/test/prism/encoding/encodings_test.rb
new file mode 100644
index 0000000000..4ad2b465cc
--- /dev/null
+++ b/test/prism/encoding/encodings_test.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE != "ruby"
+
+require_relative "../test_helper"
+
+module Prism
+ class EncodingsTest < TestCase
+ class ConstantContext < BasicObject
+ def self.const_missing(const)
+ const
+ end
+ end
+
+ class IdentifierContext < BasicObject
+ def method_missing(name, *)
+ name
+ end
+ end
+
+ # These test that we're correctly parsing codepoints for each alias of each
+ # encoding that prism supports.
+ each_encoding do |encoding, range|
+ (encoding.names - %w[external internal filesystem locale]).each do |name|
+ define_method(:"test_encoding_#{name}") do
+ assert_encoding(encoding, name, range)
+ end
+ end
+ end
+
+ private
+
+ def assert_encoding_constant(name, character)
+ source = "# encoding: #{name}\n#{character}"
+ expected = ConstantContext.new.instance_eval(source)
+
+ result = Prism.parse(source)
+ assert result.success?
+
+ actual = result.value.statements.body.last
+ assert_kind_of ConstantReadNode, actual
+ assert_equal expected, actual.name
+ end
+
+ def assert_encoding_identifier(name, character)
+ source = "# encoding: #{name}\n#{character}"
+ expected = IdentifierContext.new.instance_eval(source)
+
+ result = Prism.parse(source)
+ assert result.success?
+
+ actual = result.value.statements.body.last
+ assert_kind_of CallNode, actual
+ assert_equal expected, actual.name
+ end
+
+ # Check that we can properly parse every codepoint in the given encoding.
+ def assert_encoding(encoding, name, range)
+ # I'm not entirely sure, but I believe these codepoints are incorrect in
+ # their parsing in CRuby. They all report as matching `[[:lower:]]` but
+ # then they are parsed as constants. This is because CRuby determines if
+ # an identifier is a constant or not by case folding it down to lowercase
+ # and checking if there is a difference. And even though they report
+ # themselves as lowercase, their case fold is different. I have reported
+ # this bug upstream.
+ case encoding
+ when Encoding::UTF_8, Encoding::UTF_8_MAC, Encoding::UTF8_DoCoMo, Encoding::UTF8_KDDI, Encoding::UTF8_SoftBank, Encoding::CESU_8
+ range = range.to_a - [
+ 0x01c5, 0x01c8, 0x01cb, 0x01f2, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
+ 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
+ 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
+ 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fcc, 0x1ffc,
+ ]
+ when Encoding::Windows_1253
+ range = range.to_a - [0xb5]
+ end
+
+ range.each do |codepoint|
+ character = codepoint.chr(encoding)
+
+ if character.match?(/[[:alpha:]]/)
+ if character.match?(/[[:upper:]]/)
+ assert_encoding_constant(name, character)
+ else
+ assert_encoding_identifier(name, character)
+ end
+ elsif character.match?(/[[:alnum:]]/)
+ assert_encoding_identifier(name, "_#{character}")
+ else
+ next if ["/", "{"].include?(character)
+
+ source = "# encoding: #{name}\n/(?##{character})/\n"
+ assert Prism.parse_success?(source), "Expected #{source.inspect} to parse successfully."
+ end
+ rescue RangeError
+ source = "# encoding: #{name}\n\\x#{codepoint.to_s(16)}"
+ assert Prism.parse_failure?(source)
+ end
+ end
+ end
+end
diff --git a/test/prism/encoding/regular_expression_encoding_test.rb b/test/prism/encoding/regular_expression_encoding_test.rb
new file mode 100644
index 0000000000..5d062fe59a
--- /dev/null
+++ b/test/prism/encoding/regular_expression_encoding_test.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+return unless defined?(RubyVM::InstructionSequence)
+return if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
+
+require_relative "../test_helper"
+
+module Prism
+ class RegularExpressionEncodingTest < TestCase
+ each_encoding do |encoding, _|
+ define_method(:"test_regular_expression_encoding_flags_#{encoding.name}") do
+ assert_regular_expression_encoding_flags(encoding, ["/a/", "/ą/", "//"])
+ end
+
+ escapes = ["\\x00", "\\x7F", "\\x80", "\\xFF", "\\u{00}", "\\u{7F}", "\\u{80}", "\\M-\\C-?"]
+ escapes = escapes.concat(escapes.product(escapes).map(&:join))
+
+ define_method(:"test_regular_expression_escape_encoding_flags_#{encoding.name}") do
+ assert_regular_expression_encoding_flags(encoding, escapes.map { |e| "/#{e}/" })
+ end
+
+ ["n", "u", "e", "s"].each do |modifier|
+ define_method(:"test_regular_expression_encoding_modifiers_/#{modifier}_#{encoding.name}") do
+ regexp_sources = ["abc", "garçon", "\\x80", "gar\\xC3\\xA7on", "gar\\u{E7}on", "abc\\u{FFFFFF}", "\\x80\\u{80}" ]
+
+ assert_regular_expression_encoding_flags(
+ encoding,
+ regexp_sources.product(["n", "u", "e", "s"]).map { |r, modifier| "/#{r}/#{modifier}" }
+ )
+ end
+ end
+ end
+
+ private
+
+ def assert_regular_expression_encoding_flags(encoding, regexps)
+ regexps.each do |regexp|
+ regexp_modifier_used = regexp.end_with?("/u") || regexp.end_with?("/e") || regexp.end_with?("/s") || regexp.end_with?("/n")
+ source = "# encoding: #{encoding.name}\n#{regexp}"
+
+ encoding_errors = ["invalid multibyte char", "escaped non ASCII character in UTF-8 regexp", "differs from source encoding"]
+ skipped_errors = ["invalid multibyte escape", "incompatible character encoding", "UTF-8 character in non UTF-8 regexp", "invalid Unicode range", "invalid Unicode list"]
+
+ # TODO (nirvdrum 21-Feb-2024): Prism currently does not handle Regexp validation unless modifiers are used. So, skip processing those errors for now: https://github.com/ruby/prism/issues/2104
+ unless regexp_modifier_used
+ skipped_errors += encoding_errors
+ encoding_errors.clear
+ end
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ if encoding_errors.find { |e| error.message.include?(e) }
+ error.message.split("\n").map { |m| m[/: (.+?)$/, 1] }
+ elsif skipped_errors.find { |e| error.message.include?(e) }
+ next
+ else
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ regexp = result.statement
+
+ actual_encoding = if regexp.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif regexp.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ elsif regexp.forced_us_ascii_encoding?
+ Encoding::US_ASCII
+ elsif regexp.ascii_8bit?
+ Encoding::ASCII_8BIT
+ elsif regexp.utf_8?
+ Encoding::UTF_8
+ elsif regexp.euc_jp?
+ Encoding::EUC_JP
+ elsif regexp.windows_31j?
+ Encoding::Windows_31J
+ else
+ encoding
+ end
+
+ if regexp.utf_8? && actual_encoding != Encoding::UTF_8
+ raise "expected regexp encoding to be UTF-8 due to '/u' modifier, but got #{actual_encoding.name}"
+ elsif regexp.ascii_8bit? && (actual_encoding != Encoding::ASCII_8BIT && actual_encoding != Encoding::US_ASCII)
+ raise "expected regexp encoding to be ASCII-8BIT or US-ASCII due to '/n' modifier, but got #{actual_encoding.name}"
+ elsif regexp.euc_jp? && actual_encoding != Encoding::EUC_JP
+ raise "expected regexp encoding to be EUC-JP due to '/e' modifier, but got #{actual_encoding.name}"
+ elsif regexp.windows_31j? && actual_encoding != Encoding::Windows_31J
+ raise "expected regexp encoding to be Windows-31J due to '/s' modifier, but got #{actual_encoding.name}"
+ end
+
+ if regexp.utf_8? && regexp.forced_utf8_encoding?
+ raise "the forced_utf8 flag should not be set when the UTF-8 modifier (/u) is used"
+ elsif regexp.ascii_8bit? && regexp.forced_binary_encoding?
+ raise "the forced_ascii_8bit flag should not be set when the UTF-8 modifier (/u) is used"
+ end
+
+ actual_encoding
+ else
+ errors = result.errors.map(&:message)
+
+ if errors.last&.include?("UTF-8 mixed within")
+ nil
+ else
+ errors
+ end
+ end
+ end
+
+ # TODO (nirvdrum 22-Feb-2024): Remove this workaround once Prism better maps CRuby's error messages.
+ # This class of error message is tricky. The part not being compared is a representation of the regexp.
+ # Depending on the source encoding and any encoding modifiers being used, CRuby alters how the regexp is represented.
+ # Sometimes it's an MBC string. Other times it uses hexadecimal character escapes. And in other cases it uses
+ # the long-form Unicode escape sequences. This short-circuit checks that the error message is mostly correct.
+ if expected.is_a?(Array) && actual.is_a?(Array)
+ if expected.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:") &&
+ actual.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:")
+ expected.last.clear
+ actual.last.clear
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/encoding/string_encoding_test.rb b/test/prism/encoding/string_encoding_test.rb
new file mode 100644
index 0000000000..6f9d86df3b
--- /dev/null
+++ b/test/prism/encoding/string_encoding_test.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class StringEncodingTest < TestCase
+ each_encoding do |encoding, _|
+ define_method(:"test_#{encoding.name}") do
+ assert_encoding(encoding)
+ end
+ end
+
+ def test_coding
+ actual = Prism.parse_statement("# coding: utf-8\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_coding_with_whitespace
+ actual = Prism.parse_statement("# coding \t \r \v : \t \v \r ascii-8bit \n'string'").unescaped.encoding
+ assert_equal Encoding::ASCII_8BIT, actual
+ end
+
+ def test_emacs_style
+ actual = Prism.parse_statement("# -*- coding: utf-8 -*-\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_utf_8_unix
+ actual = Prism.parse_statement("# coding: utf-8-unix\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_utf_8_dos
+ actual = Prism.parse_statement("# coding: utf-8-dos\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_utf_8_mac
+ actual = Prism.parse_statement("# coding: utf-8-mac\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_utf_8_star
+ actual = Prism.parse_statement("# coding: utf-8-*\n'string'").unescaped.encoding
+ assert_equal Encoding::UTF_8, actual
+ end
+
+ def test_first_lexed_token
+ encoding = Prism.lex("# encoding: ascii-8bit").value[0][0].value.encoding
+ assert_equal Encoding::ASCII_8BIT, encoding
+ end
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ # This test may be a little confusing. Basically when we use our strpbrk,
+ # it takes into account the encoding of the file.
+ def test_strpbrk_multibyte
+ result = Prism.parse(<<~RUBY)
+ # encoding: Shift_JIS
+ %w[\x81\x5c]
+ RUBY
+
+ assert(result.errors.empty?)
+ assert_equal(
+ (+"\x81\x5c").force_encoding(Encoding::Shift_JIS),
+ result.statement.elements.first.unescaped
+ )
+ end
+
+ def test_slice_encoding
+ slice = Prism.parse("# encoding: Shift_JIS\nア").value.slice
+ assert_equal (+"ア").force_encoding(Encoding::SHIFT_JIS), slice
+ assert_equal Encoding::SHIFT_JIS, slice.encoding
+ end
+
+ def test_multibyte_escapes
+ [
+ ["'", "'"],
+ ["\"", "\""],
+ ["`", "`"],
+ ["/", "/"],
+ ["<<'HERE'\n", "\nHERE"],
+ ["<<-HERE\n", "\nHERE"]
+ ].each do |opening, closing|
+ assert Prism.parse_success?("# encoding: shift_jis\n'\\\x82\xA0'\n")
+ end
+ end
+ end
+
+ private
+
+ def assert_encoding(encoding)
+ escapes = ["\\x00", "\\x7F", "\\x80", "\\xFF", "\\u{00}", "\\u{7F}", "\\u{80}", "\\M-\\C-?"]
+ escapes = escapes.concat(escapes.product(escapes).map(&:join))
+
+ escapes.each do |escaped|
+ source = "# encoding: #{encoding.name}\n\"#{escaped}\""
+
+ expected =
+ begin
+ eval(source).encoding
+ rescue SyntaxError => error
+ if error.message.include?("UTF-8 mixed within")
+ error.message[/UTF-8 mixed within .+? source/]
+ else
+ raise
+ end
+ end
+
+ actual =
+ Prism.parse(source).then do |result|
+ if result.success?
+ string = result.statement
+
+ if string.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif string.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ else
+ encoding
+ end
+ else
+ error = result.errors.first
+
+ if error.message.include?("mixed")
+ error.message
+ else
+ raise error.message
+ end
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/encoding/symbol_encoding_test.rb b/test/prism/encoding/symbol_encoding_test.rb
new file mode 100644
index 0000000000..20c998a58b
--- /dev/null
+++ b/test/prism/encoding/symbol_encoding_test.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE != "ruby"
+
+require_relative "../test_helper"
+
+module Prism
+ class SymbolEncodingTest < TestCase
+ each_encoding do |encoding, _|
+ define_method(:"test_symbols_#{encoding.name}") do
+ assert_symbols(encoding)
+ end
+
+ define_method(:"test_escapes_#{encoding.name}") do
+ assert_escapes(encoding)
+ end
+ end
+
+ private
+
+ def expected_encoding(source)
+ eval(source).encoding
+ end
+
+ def actual_encoding(source, encoding)
+ result = Prism.parse(source)
+
+ if result.success?
+ symbol = result.statement
+
+ if symbol.forced_utf8_encoding?
+ Encoding::UTF_8
+ elsif symbol.forced_binary_encoding?
+ Encoding::ASCII_8BIT
+ elsif symbol.forced_us_ascii_encoding?
+ Encoding::US_ASCII
+ else
+ encoding
+ end
+ else
+ raise SyntaxError.new(result.errors.map(&:message).join("\n"))
+ end
+ end
+
+ def assert_symbols(encoding)
+ [:a, :ą, :+].each do |symbol|
+ source = "# encoding: #{encoding.name}\n#{symbol.inspect}"
+
+ expected =
+ begin
+ expected_encoding(source)
+ rescue SyntaxError => error
+ if error.message.include?("invalid multibyte")
+ "invalid multibyte"
+ else
+ raise
+ end
+ end
+
+ actual =
+ begin
+ actual_encoding(source, encoding)
+ rescue SyntaxError => error
+ if error.message.include?("invalid multibyte")
+ "invalid multibyte"
+ else
+ raise
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+
+ def assert_escapes(encoding)
+ escapes = ["\\x00", "\\x7F", "\\x80", "\\xFF", "\\u{00}", "\\u{7F}", "\\u{80}", "\\M-\\C-?"]
+ escapes = escapes.concat(escapes.product(escapes).map(&:join))
+
+ escapes.each do |escaped|
+ source = "# encoding: #{encoding.name}\n:\"#{escaped}\""
+
+ expected =
+ begin
+ expected_encoding(source)
+ rescue SyntaxError => error
+ if error.message.include?("UTF-8 mixed within")
+ error.message[/UTF-8 mixed within .+? source/]
+ else
+ raise
+ end
+ end
+
+ actual =
+ begin
+ actual_encoding(source, encoding)
+ rescue SyntaxError => error
+ if error.message.include?("mixed")
+ error.message.split("\n", 2).first
+ else
+ raise
+ end
+ end
+
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/test/prism/encoding_test.rb b/test/prism/encoding_test.rb
deleted file mode 100644
index 2aee473ddf..0000000000
--- a/test/prism/encoding_test.rb
+++ /dev/null
@@ -1,577 +0,0 @@
-# frozen_string_literal: true
-
-return if RUBY_ENGINE != "ruby"
-
-require_relative "test_helper"
-
-module Prism
- class EncodingTest < TestCase
- codepoints_1byte = 0...0x100
- encodings = {
- Encoding::ASCII_8BIT => codepoints_1byte,
- Encoding::US_ASCII => codepoints_1byte
- }
-
- if !ENV["PRISM_BUILD_MINIMAL"]
- encodings[Encoding::Windows_1253] = codepoints_1byte
- end
-
- # By default we don't test every codepoint in these encodings because it
- # takes a very long time.
- if ENV["PRISM_TEST_ALL_ENCODINGS"]
- codepoints_2bytes = 0...0x10000
- codepoints_unicode = (0...0x110000)
-
- codepoints_eucjp = [
- *(0...0x10000),
- *(0...0x10000).map { |bytes| bytes | 0x8F0000 }
- ]
-
- codepoints_emacs_mule = [
- *(0...0x80),
- *((0x81...0x90).flat_map { |byte1| (0x90...0x100).map { |byte2| byte1 << 8 | byte2 } }),
- *((0x90...0x9C).flat_map { |byte1| (0xA0...0x100).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| byte1 << 16 | byte2 << 8 | byte3 } } }),
- *((0xF0...0xF5).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| (0xA0...0x100).flat_map { |byte4| 0x9C << 24 | byte3 << 16 | byte3 << 8 | byte4 } } }),
- ]
-
- codepoints_gb18030 = [
- *(0...0x80),
- *((0x81..0xFE).flat_map { |byte1| (0x40...0x100).map { |byte2| byte1 << 8 | byte2 } }),
- *((0x81..0xFE).flat_map { |byte1| (0x30...0x40).flat_map { |byte2| (0x81..0xFE).flat_map { |byte3| (0x2F...0x41).map { |byte4| byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4 } } } }),
- ]
-
- codepoints_euc_tw = [
- *(0..0x7F),
- *(0xA1..0xFF).flat_map { |byte1| (0xA1..0xFF).map { |byte2| (byte1 << 8) | byte2 } },
- *(0xA1..0xB0).flat_map { |byte2| (0xA1..0xFF).flat_map { |byte3| (0xA1..0xFF).flat_map { |byte4| 0x8E << 24 | byte2 << 16 | byte3 << 8 | byte4 } } }
- ]
-
- encodings.merge!(
- Encoding::CP850 => codepoints_1byte,
- Encoding::CP852 => codepoints_1byte,
- Encoding::CP855 => codepoints_1byte,
- Encoding::GB1988 => codepoints_1byte,
- Encoding::IBM437 => codepoints_1byte,
- Encoding::IBM720 => codepoints_1byte,
- Encoding::IBM737 => codepoints_1byte,
- Encoding::IBM775 => codepoints_1byte,
- Encoding::IBM852 => codepoints_1byte,
- Encoding::IBM855 => codepoints_1byte,
- Encoding::IBM857 => codepoints_1byte,
- Encoding::IBM860 => codepoints_1byte,
- Encoding::IBM861 => codepoints_1byte,
- Encoding::IBM862 => codepoints_1byte,
- Encoding::IBM863 => codepoints_1byte,
- Encoding::IBM864 => codepoints_1byte,
- Encoding::IBM865 => codepoints_1byte,
- Encoding::IBM866 => codepoints_1byte,
- Encoding::IBM869 => codepoints_1byte,
- Encoding::ISO_8859_1 => codepoints_1byte,
- Encoding::ISO_8859_2 => codepoints_1byte,
- Encoding::ISO_8859_3 => codepoints_1byte,
- Encoding::ISO_8859_4 => codepoints_1byte,
- Encoding::ISO_8859_5 => codepoints_1byte,
- Encoding::ISO_8859_6 => codepoints_1byte,
- Encoding::ISO_8859_7 => codepoints_1byte,
- Encoding::ISO_8859_8 => codepoints_1byte,
- Encoding::ISO_8859_9 => codepoints_1byte,
- Encoding::ISO_8859_10 => codepoints_1byte,
- Encoding::ISO_8859_11 => codepoints_1byte,
- Encoding::ISO_8859_13 => codepoints_1byte,
- Encoding::ISO_8859_14 => codepoints_1byte,
- Encoding::ISO_8859_15 => codepoints_1byte,
- Encoding::ISO_8859_16 => codepoints_1byte,
- Encoding::KOI8_R => codepoints_1byte,
- Encoding::KOI8_U => codepoints_1byte,
- Encoding::MACCENTEURO => codepoints_1byte,
- Encoding::MACCROATIAN => codepoints_1byte,
- Encoding::MACCYRILLIC => codepoints_1byte,
- Encoding::MACGREEK => codepoints_1byte,
- Encoding::MACICELAND => codepoints_1byte,
- Encoding::MACROMAN => codepoints_1byte,
- Encoding::MACROMANIA => codepoints_1byte,
- Encoding::MACTHAI => codepoints_1byte,
- Encoding::MACTURKISH => codepoints_1byte,
- Encoding::MACUKRAINE => codepoints_1byte,
- Encoding::TIS_620 => codepoints_1byte,
- Encoding::Windows_1250 => codepoints_1byte,
- Encoding::Windows_1251 => codepoints_1byte,
- Encoding::Windows_1252 => codepoints_1byte,
- Encoding::Windows_1254 => codepoints_1byte,
- Encoding::Windows_1255 => codepoints_1byte,
- Encoding::Windows_1256 => codepoints_1byte,
- Encoding::Windows_1257 => codepoints_1byte,
- Encoding::Windows_1258 => codepoints_1byte,
- Encoding::Windows_874 => codepoints_1byte,
- Encoding::Big5 => codepoints_2bytes,
- Encoding::Big5_HKSCS => codepoints_2bytes,
- Encoding::Big5_UAO => codepoints_2bytes,
- Encoding::CP949 => codepoints_2bytes,
- Encoding::CP950 => codepoints_2bytes,
- Encoding::CP951 => codepoints_2bytes,
- Encoding::EUC_KR => codepoints_2bytes,
- Encoding::GBK => codepoints_2bytes,
- Encoding::GB12345 => codepoints_2bytes,
- Encoding::GB2312 => codepoints_2bytes,
- Encoding::MACJAPANESE => codepoints_2bytes,
- Encoding::Shift_JIS => codepoints_2bytes,
- Encoding::SJIS_DoCoMo => codepoints_2bytes,
- Encoding::SJIS_KDDI => codepoints_2bytes,
- Encoding::SJIS_SoftBank => codepoints_2bytes,
- Encoding::Windows_31J => codepoints_2bytes,
- Encoding::UTF_8 => codepoints_unicode,
- Encoding::UTF8_MAC => codepoints_unicode,
- Encoding::UTF8_DoCoMo => codepoints_unicode,
- Encoding::UTF8_KDDI => codepoints_unicode,
- Encoding::UTF8_SoftBank => codepoints_unicode,
- Encoding::CESU_8 => codepoints_unicode,
- Encoding::CP51932 => codepoints_eucjp,
- Encoding::EUC_JP => codepoints_eucjp,
- Encoding::EUCJP_MS => codepoints_eucjp,
- Encoding::EUC_JIS_2004 => codepoints_eucjp,
- Encoding::EMACS_MULE => codepoints_emacs_mule,
- Encoding::STATELESS_ISO_2022_JP => codepoints_emacs_mule,
- Encoding::STATELESS_ISO_2022_JP_KDDI => codepoints_emacs_mule,
- Encoding::GB18030 => codepoints_gb18030,
- Encoding::EUC_TW => codepoints_euc_tw
- )
- end
-
- # These test that we're correctly parsing codepoints for each alias of each
- # encoding that prism supports.
- encodings.each do |encoding, range|
- (encoding.names - %w[external internal filesystem locale]).each do |name|
- define_method(:"test_encoding_#{name}") do
- assert_encoding(encoding, name, range)
- end
- end
- end
-
- # These test that we're correctly setting the flags on strings for each
- # encoding that prism supports.
- escapes = ["\\x00", "\\x7F", "\\x80", "\\xFF", "\\u{00}", "\\u{7F}", "\\u{80}", "\\M-\\C-?"]
- escapes = escapes.concat(escapes.product(escapes).map(&:join))
- symbols = [:a, :ą, :+]
- regexps = [/a/, /ą/, //]
-
- encodings.each_key do |encoding|
- define_method(:"test_encoding_flags_#{encoding.name}") do
- assert_encoding_flags(encoding, escapes)
- end
-
- define_method(:"test_symbol_encoding_flags_#{encoding.name}") do
- assert_symbol_encoding_flags(encoding, symbols)
- end
-
- define_method(:"test_symbol_character_escape_encoding_flags_#{encoding.name}") do
- assert_symbol_character_escape_encoding_flags(encoding, escapes)
- end
-
- define_method(:"test_regular_expression_encoding_flags_#{encoding.name}") do
- assert_regular_expression_encoding_flags(encoding, regexps.map(&:inspect))
- end
-
- define_method(:"test_regular_expression_escape_encoding_flags_#{encoding.name}") do
- assert_regular_expression_encoding_flags(encoding, escapes.map { |e| "/#{e}/" })
- end
- end
-
- encoding_modifiers = { ascii_8bit: "n", utf_8: "u", euc_jp: "e", windows_31j: "s" }
- regexp_sources = ["abc", "garçon", "\\x80", "gar\\xC3\\xA7on", "gar\\u{E7}on", "abc\\u{FFFFFF}", "\\x80\\u{80}" ]
-
- encoding_modifiers.each_value do |modifier|
- encodings.each_key do |encoding|
- define_method(:"test_regular_expression_encoding_modifiers_/#{modifier}_#{encoding.name}") do
- assert_regular_expression_encoding_flags(
- encoding,
- regexp_sources.product(encoding_modifiers.values).map { |r, modifier| "/#{r}/#{modifier}" }
- )
- end
- end
- end
-
- def test_coding
- result = Prism.parse("# coding: utf-8\n'string'")
- actual = result.value.statements.body.first.unescaped.encoding
- assert_equal Encoding.find("utf-8"), actual
- end
-
- def test_coding_with_whitespace
- result = Prism.parse("# coding \t \r \v : \t \v \r ascii-8bit \n'string'")
- actual = result.value.statements.body.first.unescaped.encoding
- assert_equal Encoding.find("ascii-8bit"), actual
- end
-
- def test_emacs_style
- result = Prism.parse("# -*- coding: utf-8 -*-\n'string'")
- actual = result.value.statements.body.first.unescaped.encoding
- assert_equal Encoding.find("utf-8"), actual
- end
-
- def test_utf_8_variations
- %w[
- utf-8-unix
- utf-8-dos
- utf-8-mac
- utf-8-*
- ].each do |encoding|
- result = Prism.parse("# coding: #{encoding}\n'string'")
- actual = result.value.statements.body.first.unescaped.encoding
- assert_equal Encoding.find("utf-8"), actual
- end
- end
-
- def test_first_lexed_token
- encoding = Prism.lex("# encoding: ascii-8bit").value[0][0].value.encoding
- assert_equal Encoding.find("ascii-8bit"), encoding
- end
-
- if !ENV["PRISM_BUILD_MINIMAL"]
- # This test may be a little confusing. Basically when we use our strpbrk,
- # it takes into account the encoding of the file.
- def test_strpbrk_multibyte
- result = Prism.parse(<<~RUBY)
- # encoding: Shift_JIS
- %w[\x81\x5c]
- RUBY
-
- assert(result.errors.empty?)
- assert_equal(
- (+"\x81\x5c").force_encoding(Encoding::Shift_JIS),
- result.value.statements.body.first.elements.first.unescaped
- )
- end
-
- def test_slice_encoding
- slice = Prism.parse("# encoding: Shift_JIS\nア").value.slice
- assert_equal (+"ア").force_encoding(Encoding::SHIFT_JIS), slice
- assert_equal Encoding::SHIFT_JIS, slice.encoding
- end
-
- def test_multibyte_escapes
- [
- ["'", "'"],
- ["\"", "\""],
- ["`", "`"],
- ["/", "/"],
- ["<<'HERE'\n", "\nHERE"],
- ["<<-HERE\n", "\nHERE"]
- ].each do |opening, closing|
- assert Prism.parse_success?("# encoding: shift_jis\n'\\\x82\xA0'\n")
- end
- end
- end
-
- private
-
- class ConstantContext < BasicObject
- def self.const_missing(const)
- const
- end
- end
-
- def constant_context
- ConstantContext.new
- end
-
- class IdentifierContext < BasicObject
- def method_missing(name, *)
- name
- end
- end
-
- def identifier_context
- IdentifierContext.new
- end
-
- def assert_encoding_constant(name, character)
- source = "# encoding: #{name}\n#{character}"
- expected = constant_context.instance_eval(source)
-
- result = Prism.parse(source)
- assert result.success?
-
- actual = result.value.statements.body.last
- assert_kind_of ConstantReadNode, actual
- assert_equal expected, actual.name
- end
-
- def assert_encoding_identifier(name, character)
- source = "# encoding: #{name}\n#{character}"
- expected = identifier_context.instance_eval(source)
-
- result = Prism.parse(source)
- assert result.success?
-
- actual = result.value.statements.body.last
- assert_kind_of CallNode, actual
- assert_equal expected, actual.name
- end
-
- # Check that we can properly parse every codepoint in the given encoding.
- def assert_encoding(encoding, name, range)
- # I'm not entirely sure, but I believe these codepoints are incorrect in
- # their parsing in CRuby. They all report as matching `[[:lower:]]` but
- # then they are parsed as constants. This is because CRuby determines if
- # an identifier is a constant or not by case folding it down to lowercase
- # and checking if there is a difference. And even though they report
- # themselves as lowercase, their case fold is different. I have reported
- # this bug upstream.
- case encoding
- when Encoding::UTF_8, Encoding::UTF_8_MAC, Encoding::UTF8_DoCoMo, Encoding::UTF8_KDDI, Encoding::UTF8_SoftBank, Encoding::CESU_8
- range = range.to_a - [
- 0x01c5, 0x01c8, 0x01cb, 0x01f2, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b,
- 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b,
- 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab,
- 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fbc, 0x1fcc, 0x1ffc,
- ]
- when Encoding::Windows_1253
- range = range.to_a - [0xb5]
- end
-
- range.each do |codepoint|
- character = codepoint.chr(encoding)
-
- if character.match?(/[[:alpha:]]/)
- if character.match?(/[[:upper:]]/)
- assert_encoding_constant(name, character)
- else
- assert_encoding_identifier(name, character)
- end
- elsif character.match?(/[[:alnum:]]/)
- assert_encoding_identifier(name, "_#{character}")
- else
- next if ["/", "{"].include?(character)
-
- source = "# encoding: #{name}\n/(?##{character})/\n"
- assert Prism.parse(source).success?, "Expected #{source.inspect} to parse successfully."
- end
- rescue RangeError
- source = "# encoding: #{name}\n\\x#{codepoint.to_s(16)}"
- refute Prism.parse(source).success?
- end
- end
-
- def assert_encoding_flags(encoding, escapes)
- escapes.each do |escaped|
- source = "# encoding: #{encoding.name}\n\"#{escaped}\""
-
- expected =
- begin
- eval(source).encoding
- rescue SyntaxError => error
- if error.message.include?("UTF-8 mixed within")
- error.message[/: (.+?)\n/, 1]
- else
- raise
- end
- end
-
- actual =
- Prism.parse(source).then do |result|
- if result.success?
- string = result.value.statements.body.first
-
- if string.forced_utf8_encoding?
- Encoding::UTF_8
- elsif string.forced_binary_encoding?
- Encoding::ASCII_8BIT
- else
- encoding
- end
- else
- error = result.errors.first
-
- if error.message.include?("mixed")
- error.message
- else
- raise error.message
- end
- end
- end
-
- assert_equal expected, actual
- end
- end
-
- # Test Symbol literals without any interpolation or escape sequences.
- def assert_symbol_encoding_flags(encoding, symbols)
- symbols.each do |symbol|
- source = "# encoding: #{encoding.name}\n#{symbol.inspect}"
-
- expected =
- begin
- eval(source).encoding
- rescue SyntaxError => error
- unless error.message.include?("invalid multibyte char")
- raise
- end
- end
-
- actual =
- Prism.parse(source).then do |result|
- if result.success?
- symbol = result.value.statements.body.first
-
- if symbol.forced_utf8_encoding?
- Encoding::UTF_8
- elsif symbol.forced_binary_encoding?
- Encoding::ASCII_8BIT
- elsif symbol.forced_us_ascii_encoding?
- Encoding::US_ASCII
- else
- encoding
- end
- else
- error = result.errors.last
-
- unless error.message.include?("invalid symbol")
- raise error.message
- end
- end
- end
-
- assert_equal expected, actual
- end
- end
-
- def assert_symbol_character_escape_encoding_flags(encoding, escapes)
- escapes.each do |escaped|
- source = "# encoding: #{encoding.name}\n:\"#{escaped}\""
-
- expected =
- begin
- eval(source).encoding
- rescue SyntaxError => error
- if error.message.include?("UTF-8 mixed within")
- error.message[/: (.+?)\n/, 1]
- else
- raise
- end
- end
-
- actual =
- Prism.parse(source).then do |result|
- if result.success?
- symbol = result.value.statements.body.first
-
- if symbol.forced_utf8_encoding?
- Encoding::UTF_8
- elsif symbol.forced_binary_encoding?
- Encoding::ASCII_8BIT
- elsif symbol.forced_us_ascii_encoding?
- Encoding::US_ASCII
- else
- encoding
- end
- else
- error = result.errors.first
-
- if error.message.include?("mixed")
- error.message
- else
- raise error.message
- end
- end
- end
-
- assert_equal expected, actual
- end
- end
-
- def assert_regular_expression_encoding_flags(encoding, regexps)
- regexps.each do |regexp|
- regexp_modifier_used = regexp.end_with?("/u") || regexp.end_with?("/e") || regexp.end_with?("/s") || regexp.end_with?("/n")
- source = "# encoding: #{encoding.name}\n#{regexp}"
-
- encoding_errors = ["invalid multibyte char", "escaped non ASCII character in UTF-8 regexp", "differs from source encoding"]
- skipped_errors = ["invalid multibyte escape", "incompatible character encoding", "UTF-8 character in non UTF-8 regexp", "invalid Unicode range", "invalid Unicode list"]
-
- # TODO (nirvdrum 21-Feb-2024): Prism currently does not handle Regexp validation unless modifiers are used. So, skip processing those errors for now: https://github.com/ruby/prism/issues/2104
- unless regexp_modifier_used
- skipped_errors += encoding_errors
- encoding_errors.clear
- end
-
- expected =
- begin
- eval(source).encoding
- rescue SyntaxError => error
- if encoding_errors.find { |e| error.message.include?(e) }
- error.message.split("\n").map { |m| m[/: (.+?)$/, 1] }
- elsif skipped_errors.find { |e| error.message.include?(e) }
- next
- else
- raise
- end
- end
-
- actual =
- Prism.parse(source).then do |result|
- if result.success?
- regexp = result.value.statements.body.first
-
- actual_encoding = if regexp.forced_utf8_encoding?
- Encoding::UTF_8
- elsif regexp.forced_binary_encoding?
- Encoding::ASCII_8BIT
- elsif regexp.forced_us_ascii_encoding?
- Encoding::US_ASCII
- elsif regexp.ascii_8bit?
- Encoding::ASCII_8BIT
- elsif regexp.utf_8?
- Encoding::UTF_8
- elsif regexp.euc_jp?
- Encoding::EUC_JP
- elsif regexp.windows_31j?
- Encoding::Windows_31J
- else
- encoding
- end
-
- if regexp.utf_8? && actual_encoding != Encoding::UTF_8
- raise "expected regexp encoding to be UTF-8 due to '/u' modifier, but got #{actual_encoding.name}"
- elsif regexp.ascii_8bit? && (actual_encoding != Encoding::ASCII_8BIT && actual_encoding != Encoding::US_ASCII)
- raise "expected regexp encoding to be ASCII-8BIT or US-ASCII due to '/n' modifier, but got #{actual_encoding.name}"
- elsif regexp.euc_jp? && actual_encoding != Encoding::EUC_JP
- raise "expected regexp encoding to be EUC-JP due to '/e' modifier, but got #{actual_encoding.name}"
- elsif regexp.windows_31j? && actual_encoding != Encoding::Windows_31J
- raise "expected regexp encoding to be Windows-31J due to '/s' modifier, but got #{actual_encoding.name}"
- end
-
- if regexp.utf_8? && regexp.forced_utf8_encoding?
- raise "the forced_utf8 flag should not be set when the UTF-8 modifier (/u) is used"
- elsif regexp.ascii_8bit? && regexp.forced_binary_encoding?
- raise "the forced_ascii_8bit flag should not be set when the UTF-8 modifier (/u) is used"
- end
-
- actual_encoding
- else
- errors = result.errors.map(&:message)
-
- if errors.last&.include?("UTF-8 mixed within")
- nil
- else
- errors
- end
- end
- end
-
- # TODO (nirvdrum 22-Feb-2024): Remove this workaround once Prism better maps CRuby's error messages.
- # This class of error message is tricky. The part not being compared is a representation of the regexp.
- # Depending on the source encoding and any encoding modifiers being used, CRuby alters how the regexp is represented.
- # Sometimes it's an MBC string. Other times it uses hexadecimal character escapes. And in other cases it uses
- # the long-form Unicode escape sequences. This short-circuit checks that the error message is mostly correct.
- if expected.is_a?(Array) && actual.is_a?(Array)
- if expected.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:") &&
- actual.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:")
- expected.last.clear
- actual.last.clear
- end
- end
-
- assert_equal expected, actual
- end
- end
- end
-end
diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb
index 4d35d057cf..91ba6ea373 100644
--- a/test/prism/errors_test.rb
+++ b/test/prism/errors_test.rb
@@ -99,7 +99,7 @@ module Prism
)
assert_errors expected, "BEGIN { 1 + }", [
- ["expected an expression after the operator", 10..11],
+ ["unexpected '}'; expected an expression after the operator", 12..13],
["unexpected '}', assuming it is closing the parent 'BEGIN' block", 12..13]
]
end
@@ -195,8 +195,7 @@ module Prism
def test_unterminated_parenthesized_expression
assert_errors expression('(1 + 2'), '(1 + 2', [
- ["unexpected end of file, expecting end-of-input", 6..6],
- ["unexpected end of file, assuming it is closing the parent top level context", 6..6],
+ ["unexpected end-of-input, assuming it is closing the parent top level context", 6..6],
["expected a matching `)`", 6..6]
]
end
@@ -209,21 +208,21 @@ module Prism
def test_unterminated_argument_expression
assert_errors expression('a %'), 'a %', [
- ["invalid `%` token", 2..3],
- ["expected an expression after the operator", 2..3],
- ["unexpected end of file, assuming it is closing the parent top level context", 3..3]
+ ["unterminated quoted string meets end of file", 2..3],
+ ["unexpected end-of-input; expected an expression after the operator", 3..3],
+ ["unexpected end-of-input, assuming it is closing the parent top level context", 3..3]
]
end
def test_unterminated_interpolated_symbol
assert_error_messages ":\"#", [
- "expected a closing delimiter for the interpolated symbol"
+ "unterminated symbol; expected a closing delimiter for the interpolated symbol"
]
end
def test_cr_without_lf_in_percent_expression
assert_errors expression("%\r"), "%\r", [
- ["invalid `%` token", 0..2],
+ ["unterminated string meets end of file", 2..2],
]
end
@@ -365,7 +364,7 @@ module Prism
assert_error_messages "x.each { x end", [
"unexpected 'end', expecting end-of-input",
"unexpected 'end', ignoring it",
- "unexpected end of file, assuming it is closing the parent top level context",
+ "unexpected end-of-input, assuming it is closing the parent top level context",
"expected a block beginning with `{` to end with `}`"
]
end
@@ -378,10 +377,13 @@ module Prism
:a,
Location(),
Location(),
- ArgumentsNode(1, [
- KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
- SplatNode(Location(), expression("args"))
- ]),
+ ArgumentsNode(
+ ArgumentsNodeFlags::CONTAINS_KEYWORDS | ArgumentsNodeFlags::CONTAINS_KEYWORD_SPLAT,
+ [
+ KeywordHashNode(0, [AssocSplatNode(expression("kwargs"), Location())]),
+ SplatNode(Location(), expression("args"))
+ ]
+ ),
Location(),
nil
)
@@ -425,7 +427,7 @@ module Prism
:a,
Location(),
Location(),
- ArgumentsNode(0, [
+ ArgumentsNode(ArgumentsNodeFlags::CONTAINS_KEYWORDS, [
KeywordHashNode(1, [
AssocNode(
SymbolNode(SymbolFlags::FORCED_US_ASCII_ENCODING, nil, Location(), Location(), "foo"),
@@ -713,12 +715,13 @@ module Prism
assert_errors expected, '"\u{000z}"', [
["invalid Unicode escape sequence", 7..7],
+ ["unterminated Unicode escape", 7..7]
]
end
def test_unterminated_unicode_brackets_should_be_a_syntax_error
assert_errors expression('?\\u{3'), '?\\u{3', [
- ["invalid Unicode escape sequence; needs closing `}`", 1..5],
+ ["unterminated Unicode escape", 1..5],
]
end
@@ -861,7 +864,7 @@ module Prism
:foo,
Location(),
nil,
- ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil),
+ ParametersNode([], [], nil, [ForwardingParameterNode()], [], ForwardingParameterNode(), nil),
nil,
[],
Location(),
@@ -1238,13 +1241,12 @@ module Prism
expected = CallNode(0, receiver, Location(), :foo, Location(), nil, nil, nil, nil)
assert_errors expected, "<<~FOO.foo\n", [
- ["unterminated heredoc; can't find string \"FOO\"", 3..6]
+ ["unterminated heredoc; can't find string \"FOO\" anywhere before EOF", 3..6]
]
end
def test_invalid_message_name
- result = Prism.parse("+.@foo,+=foo")
- assert_equal :"", result.value.statements.body.first.write_name
+ assert_equal :"", Prism.parse_statement("+.@foo,+=foo").write_name
end
def test_invalid_operator_write_fcall
@@ -1353,14 +1355,14 @@ module Prism
if RUBY_VERSION >= "3.0"
def test_writing_numbered_parameter
- assert_errors expression("-> { _1 = 0 }"), "-> { _1 = 0 }", [
- ["_1 is reserved for numbered parameters", 5..7]
+ assert_error_messages "-> { _1 = 0 }", [
+ "_1 is reserved for numbered parameters"
]
end
def test_targeting_numbered_parameter
- assert_errors expression("-> { _1, = 0 }"), "-> { _1, = 0 }", [
- ["_1 is reserved for numbered parameters", 5..7]
+ assert_error_messages "-> { _1, = 0 }", [
+ "_1 is reserved for numbered parameters"
]
end
@@ -1374,7 +1376,7 @@ module Prism
def test_double_scope_numbered_parameters
source = "-> { _1 + -> { _2 } }"
- errors = [["numbered parameter is already used in outer scope", 15..17]]
+ errors = [["numbered parameter is already used in outer block", 15..17]]
assert_errors expression(source), source, errors
end
@@ -1398,7 +1400,7 @@ module Prism
end
def test_alnum_delimiters
- error_messages = ["invalid `%` token"]
+ error_messages = ["unknown type of %string"]
assert_error_messages "%qXfooX", error_messages
assert_error_messages "%QXfooX", error_messages
@@ -1463,7 +1465,7 @@ module Prism
def test_forwarding_arg_after_keyword_rest
source = "def f(**,...);end"
assert_errors expression(source), source, [
- ["unexpected `...` in parameters", 9..12],
+ ["unexpected parameter order", 9..12]
]
end
@@ -1479,8 +1481,7 @@ module Prism
assert_errors expression(source), source, [
["expected a `do` keyword or a `{` to open the lambda block", 3..3],
- ["unexpected end of file, expecting end-of-input", 7..7],
- ["unexpected end of file, assuming it is closing the parent top level context", 7..7],
+ ["unexpected end-of-input, assuming it is closing the parent top level context", 7..7],
["expected a lambda block beginning with `do` to end with `end`", 7..7]
]
end
@@ -1538,7 +1539,7 @@ module Prism
assert_errors expression(source), source, [
["expected a predicate expression for the `while` statement", 22..22],
- ["unexpected end of file, assuming it is closing the parent top level context", 22..22],
+ ["unexpected end-of-input, assuming it is closing the parent top level context", 22..22],
["expected an `end` to close the `while` statement", 22..22]
]
end
@@ -1628,6 +1629,41 @@ module Prism
]
end
+ def test_void_value_expression_in_begin_statement
+ source = <<~RUBY
+ x = return 1
+ x = return, 1
+ x = 1, return
+ x, y = return
+ x = begin return ensure end
+ x = begin ensure return end
+ x = begin return ensure return end
+ x = begin return; rescue; return end
+ x = begin return; rescue; return; else return end
+ x = begin; return; rescue; retry; end
+ RUBY
+
+ message = 'unexpected void value expression'
+ assert_errors expression(source), source, [
+ [message, 4..12],
+ [message, 17..23],
+ [message, 34..40],
+ [message, 48..54],
+ [message, 65..71],
+ [message, 100..106],
+ [message, 121..127],
+ [message, 156..162],
+ [message, 222..228],
+ [message, 244..250],
+ ]
+
+ refute_error_messages("x = begin return; rescue; end")
+ refute_error_messages("x = begin return; rescue; return; else end")
+ refute_error_messages("x = begin; rescue; retry; end")
+ refute_error_messages("x = begin 1; rescue; retry; ensure; end")
+ refute_error_messages("x = begin 1; rescue; return; end")
+ end
+
def test_void_value_expression_in_def
source = <<~RUBY
def (return).x
@@ -1939,10 +1975,10 @@ module Prism
RUBY
assert_errors expression(source), source, [
- ["unexpected '..', expecting end-of-input", 3..5],
- ["unexpected '..', ignoring it", 3..5],
- ["unexpected '..', expecting end-of-input", 10..12],
- ["unexpected '..', ignoring it", 10..12]
+ ["unexpected .., expecting end-of-input", 3..5],
+ ["unexpected .., ignoring it", 3..5],
+ ["unexpected .., expecting end-of-input", 10..12],
+ ["unexpected .., ignoring it", 10..12]
]
end
@@ -1954,12 +1990,18 @@ module Prism
proc { |foo: foo| }
RUBY
- assert_errors expression(source), source, [
- ["circular argument reference - bar", 8..11],
- ["circular argument reference - bar", 32..35],
- ["circular argument reference - foo", 55..58],
- ["circular argument reference - foo", 76..79]
- ]
+ assert_errors(
+ expression(source),
+ source,
+ [
+ ["circular argument reference - bar", 8..11],
+ ["circular argument reference - bar", 32..35],
+ ["circular argument reference - foo", 55..58],
+ ["circular argument reference - foo", 76..79]
+ ],
+ check_valid_syntax: false,
+ version: "3.3.0"
+ )
refute_error_messages("def foo(bar: bar = 1); end")
end
@@ -2079,7 +2121,7 @@ module Prism
def test_forwarding_arg_and_block
source = 'def foo(...) = foo(...) { }'
assert_errors expression(source), source, [
- ['both a block argument and a forwarding argument; only one block is allowed', 24..27]
+ ['both block arg and actual block given; only one block is allowed', 24..27]
]
end
@@ -2092,14 +2134,14 @@ module Prism
def test_regular_expression_with_unknown_regexp_options
source = "/foo/AZaz"
- errors = [["unknown regexp options: AZaz", 4..9]]
+ errors = [["unknown regexp options - AZaz", 4..9]]
assert_errors expression(source), source, errors
end
def test_interpolated_regular_expression_with_unknown_regexp_options
source = "/\#{foo}/AZaz"
- errors = [["unknown regexp options: AZaz", 7..12]]
+ errors = [["unknown regexp options - AZaz", 7..12]]
assert_errors expression(source), source, errors
end
@@ -2208,10 +2250,10 @@ module Prism
private
- def assert_errors(expected, source, errors, check_valid_syntax: true)
+ def assert_errors(expected, source, errors, check_valid_syntax: true, **options)
refute_valid_syntax(source) if check_valid_syntax
- result = Prism.parse(source)
+ result = Prism.parse(source, **options)
node = result.value.statements.body.last
assert_equal_nodes(expected, node, compare_location: false)
diff --git a/test/prism/fixtures/break.txt b/test/prism/fixtures/break.txt
index 82fa45bdb4..5532322c5c 100644
--- a/test/prism/fixtures/break.txt
+++ b/test/prism/fixtures/break.txt
@@ -23,3 +23,7 @@ tap { break(1) }
foo { break 42 } == 42
foo { |a| break } == 42
+
+while _ && break; end
+
+until _ && break; end
diff --git a/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt b/test/prism/fixtures/seattlerb/pct_Q_backslash_nl.txt
index d88e1fd21c..4420560d2b 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/whitequark/method_definition_in_while_cond.txt b/test/prism/fixtures/whitequark/method_definition_in_while_cond.txt
new file mode 100644
index 0000000000..6ec38906a1
--- /dev/null
+++ b/test/prism/fixtures/whitequark/method_definition_in_while_cond.txt
@@ -0,0 +1,7 @@
+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_test.rb b/test/prism/fixtures_test.rb
new file mode 100644
index 0000000000..7225b4ac66
--- /dev/null
+++ b/test/prism/fixtures_test.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+return if RUBY_VERSION < "3.2.0"
+
+require_relative "test_helper"
+
+module Prism
+ class FixturesTest < TestCase
+ except = []
+
+ # 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
+ except << "heredocs_leading_whitespace.txt" if RUBY_VERSION < "3.3.0"
+
+ Fixture.each(except: except) do |fixture|
+ define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }
+ end
+ end
+end
diff --git a/test/prism/format_errors_test.rb b/test/prism/format_errors_test.rb
deleted file mode 100644
index a1edbef2e8..0000000000
--- a/test/prism/format_errors_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-return if Prism::BACKEND == :FFI
-
-module Prism
- class FormatErrorsTest < TestCase
- def test_format_errors
- assert_equal <<~ERROR, Debug.format_errors("<>", false)
- > 1 | <>
- | ^ unexpected '<', ignoring it
- | ^ unexpected '>', ignoring it
- ERROR
-
- assert_equal <<~'ERROR', Debug.format_errors('"%W"\u"', false)
- > 1 | "%W"\u"
- | ^ unexpected backslash, ignoring it
- | ^ unexpected local variable or method, expecting end-of-input
- | ^ unterminated string meets end of file
- ERROR
- end
- end
-end
diff --git a/test/prism/fuzzer_test.rb b/test/prism/fuzzer_test.rb
index 511210e7ee..4927478bdc 100644
--- a/test/prism/fuzzer_test.rb
+++ b/test/prism/fuzzer_test.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-return if ENV["PRISM_BUILD_MINIMAL"]
-
require_relative "test_helper"
module Prism
@@ -9,7 +7,7 @@ module Prism
# invalid memory access.
class FuzzerTest < TestCase
def self.snippet(name, source)
- define_method(:"test_fuzzer_#{name}") { Prism.dump(source) }
+ define_method(:"test_fuzzer_#{name}") { Prism.profile(source) }
end
snippet "incomplete global variable", "$"
@@ -39,29 +37,31 @@ module Prism
snippet "escaped unicode at end of file 8", '"\\u33'
snippet "escaped unicode at end of file 9", '"\\u333'
snippet "float suffix at end of file", "1e"
+ snippet "parameter name that is zero length", "a { |b;"
snippet "statements node with multiple heredocs", <<~EOF
for <<A + <<B
A
B
EOF
+
snippet "create a binary call node with arg before receiver", <<~EOF
<<-A.g/{/
A
/, ""\\
EOF
+
snippet "regular expression with start and end out of order", <<~RUBY
<<-A.g//,
A
/{/, ''\\
RUBY
+
snippet "interpolated regular expression with start and end out of order", <<~RUBY
<<-A.g/{/,
A
a
/{/, ''\\
RUBY
-
- snippet "parameter name that is zero length", "a { |b;"
end
end
diff --git a/test/prism/heredoc_dedent_test.rb b/test/prism/heredoc_dedent_test.rb
index 9fbc4d936a..4e7a3c0a14 100644
--- a/test/prism/heredoc_dedent_test.rb
+++ b/test/prism/heredoc_dedent_test.rb
@@ -4,24 +4,131 @@ require_relative "test_helper"
module Prism
class HeredocDedentTest < TestCase
- filepath = File.expand_path("fixtures/tilde_heredocs.txt", __dir__)
+ def test_content_dedented_interpolation_content
+ assert_heredoc_dedent(
+ " a\n" "1\n" " a\n",
+ "<<~EOF\n" " a\n" "\#{1}\n" " a\n" "EOF\n"
+ )
+ end
+
+ def test_content
+ assert_heredoc_dedent(
+ "a\n",
+ "<<~EOF\n" " a\n" "EOF\n"
+ )
+ end
+
+ def test_tabs_dedent_spaces
+ assert_heredoc_dedent(
+ "\ta\n" "b\n" "\t\tc\n",
+ "<<~EOF\n" "\ta\n" " b\n" "\t\tc\n" "EOF\n"
+ )
+ end
+
+ def test_interpolation_then_content
+ assert_heredoc_dedent(
+ "1 a\n",
+ "<<~EOF\n" " \#{1} a\n" "EOF\n"
+ )
+ end
+
+ def test_content_then_interpolation
+ assert_heredoc_dedent(
+ "a 1\n",
+ "<<~EOF\n" " a \#{1}\n" "EOF\n"
+ )
+ end
+
+ def test_content_dedented_interpolation
+ assert_heredoc_dedent(
+ " a\n" "1\n",
+ "<<~EOF\n" " a\n" " \#{1}\n" "EOF\n"
+ )
+ end
+
+ def test_content_interpolation
+ assert_heredoc_dedent(
+ "a\n" "1\n",
+ "<<~EOF\n" " a\n" " \#{1}\n" "EOF\n"
+ )
+ end
- File.read(filepath).split(/(?=\n)\n(?=<)/).each_with_index do |heredoc, index|
- # The first example in this file has incorrect dedent calculated by
- # TruffleRuby so we skip it.
- next if index == 0 && RUBY_ENGINE == "truffleruby"
+ def test_content_content
+ assert_heredoc_dedent(
+ "a\n" "b\n",
+ "<<~EOF\n" " a\n" " b\n" "EOF\n"
+ )
+ end
- define_method "test_heredoc_#{index}" do
- node = Prism.parse(heredoc).value.statements.body.first
+ def test_content_indented_content
+ assert_heredoc_dedent(
+ "a\n" " b\n",
+ "<<~EOF\n" " a\n" " b\n" "EOF\n"
+ )
+ end
- if node.is_a?(StringNode)
- actual = node.unescaped
- else
- actual = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : "1" }.join
- end
+ def test_content_dedented_content
+ assert_heredoc_dedent(
+ "\ta\n" "b\n",
+ "<<~EOF\n" "\t\t\ta\n" "\t\tb\n" "EOF\n"
+ )
+ end
- assert_equal(eval(heredoc), actual, "Expected heredocs to match.")
+ def test_single_quote
+ assert_heredoc_dedent(
+ "a \#{1}\n",
+ "<<~'EOF'\n" "a \#{1}\n" "EOF\n"
+ )
+ end
+
+ def test_mixed_indentation
+ assert_heredoc_dedent(
+ "a\n" " b\n",
+ "<<~EOF\n" "\ta\n" "\t b\n" "EOF\n"
+ )
+ end
+
+ def test_indented_content_content
+ assert_heredoc_dedent(
+ " a\n" "b\n",
+ "<<~EOF\n" "\t a\n" "\tb\n" "EOF\n"
+ )
+ end
+
+ def test_indent_size
+ assert_heredoc_dedent(
+ "a\n" " b\n",
+ "<<~EOF\n" "\ta\n" " b\n" "EOF\n"
+ )
+ end
+
+ def test_blank_lines
+ assert_heredoc_dedent(
+ "a\n" "\n" "b\n",
+ "<<~EOF\n" " a\n" "\n" " b\n" "EOF\n"
+ )
+ end
+
+ def test_many_blank_lines
+ assert_heredoc_dedent(
+ "a\n" "\n" "\n" "\n" "\n" "b\n",
+ "<<~EOF\n" " a\n" "\n" "\n" "\n" "\n" " b\n" "EOF\n"
+ )
+ end
+
+ private
+
+ def assert_heredoc_dedent(expected, source)
+ node = Prism.parse_statement(source)
+
+ if node.is_a?(StringNode)
+ actual = node.unescaped
+ else
+ actual = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : "1" }.join
end
+
+ assert_equal(expected, actual)
+ assert_equal(eval(source), actual)
end
end
end
diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb
new file mode 100644
index 0000000000..7eac677ef7
--- /dev/null
+++ b/test/prism/lex_test.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+return if !(RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.2.0")
+
+require_relative "test_helper"
+
+module Prism
+ class LexTest < TestCase
+ except = [
+ # It seems like there are some oddities with nested heredocs and ripper.
+ # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838.
+ "seattlerb/heredoc_nested.txt",
+ "whitequark/dedenting_heredoc.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.
+ "spanning_heredoc.txt",
+ "spanning_heredoc_newlines.txt"
+ ]
+
+ if RUBY_VERSION < "3.3.0"
+ # This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
+ # we're on an earlier version.
+ except << "seattlerb/pct_w_heredoc_interp_nested.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
+ except << "heredocs_leading_whitespace.txt"
+ end
+
+ Fixture.each(except: except) do |fixture|
+ define_method(fixture.test_name) { assert_lex(fixture) }
+ end
+
+ def test_lex_file
+ assert_nothing_raised do
+ Prism.lex_file(__FILE__)
+ end
+
+ error = assert_raise Errno::ENOENT do
+ Prism.lex_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.lex_file(nil)
+ end
+ end
+
+ def test_parse_lex
+ node, tokens = Prism.parse_lex("def foo; end").value
+
+ assert_kind_of ProgramNode, node
+ assert_equal 5, tokens.length
+ end
+
+ def test_parse_lex_file
+ node, tokens = Prism.parse_lex_file(__FILE__).value
+
+ assert_kind_of ProgramNode, node
+ refute_empty tokens
+
+ error = assert_raise Errno::ENOENT do
+ Prism.parse_lex_file("idontexist.rb")
+ end
+
+ assert_equal "No such file or directory - idontexist.rb", error.message
+
+ assert_raise TypeError do
+ Prism.parse_lex_file(nil)
+ end
+ end
+
+ private
+
+ def assert_lex(fixture)
+ source = fixture.read
+
+ result = Prism.lex_compat(source)
+ assert_equal [], result.errors
+
+ Prism.lex_ripper(source).zip(result.value).each do |(ripper, prism)|
+ assert_equal ripper, prism
+ end
+ end
+ end
+end
diff --git a/test/prism/library_symbols_test.rb b/test/prism/library_symbols_test.rb
index b10a367c18..44f225478b 100644
--- a/test/prism/library_symbols_test.rb
+++ b/test/prism/library_symbols_test.rb
@@ -3,8 +3,6 @@
require_relative "test_helper"
return if RUBY_PLATFORM !~ /linux/
-
-# TODO: determine why these symbols are incorrect on ppc64le
return if RUBY_PLATFORM =~ /powerpc64le/
module Prism
diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb
index 0eb73f1b9c..27fdfc90ef 100644
--- a/test/prism/locals_test.rb
+++ b/test/prism/locals_test.rb
@@ -17,14 +17,14 @@ require_relative "test_helper"
module Prism
class LocalsTest < TestCase
- base = File.join(__dir__, "fixtures")
- Dir["**/*.txt", base: base].each do |relative|
+ except = [
# Skip this fixture because it has a different number of locals because
# CRuby is eliminating dead code.
- next if relative == "whitequark/ruby_bug_10653.txt"
+ "whitequark/ruby_bug_10653.txt"
+ ]
- filepath = File.join(base, relative)
- define_method("test_#{relative}") { assert_locals(filepath) }
+ Fixture.each(except: except) do |fixture|
+ define_method(fixture.test_name) { assert_locals(fixture) }
end
def setup
@@ -38,21 +38,188 @@ module Prism
private
- def assert_locals(filepath)
- source = File.read(filepath)
+ def assert_locals(fixture)
+ source = fixture.read
- expected = Debug.cruby_locals(source)
- actual = Debug.prism_locals(source)
+ expected = cruby_locals(source)
+ actual = prism_locals(source)
assert_equal(expected, actual)
end
- def ignore_warnings
- previous_verbosity = $VERBOSE
- $VERBOSE = nil
- yield
- ensure
- $VERBOSE = previous_verbosity
+ # A wrapper around a RubyVM::InstructionSequence that provides a more
+ # convenient interface for accessing parts of the iseq.
+ class ISeq
+ attr_reader :parts
+
+ def initialize(parts)
+ @parts = parts
+ end
+
+ def type
+ parts[0]
+ end
+
+ def local_table
+ parts[10]
+ end
+
+ def instructions
+ parts[13]
+ end
+
+ def each_child
+ instructions.each do |instruction|
+ # Only look at arrays. Other instructions are line numbers or
+ # tracepoint events.
+ next unless instruction.is_a?(Array)
+
+ instruction.each do |opnd|
+ # Only look at arrays. Other operands are literals.
+ next unless opnd.is_a?(Array)
+
+ # Only look at instruction sequences. Other operands are literals.
+ next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
+
+ yield ISeq.new(opnd)
+ end
+ end
+ end
+ end
+
+ # Used to hold the place of a local that will be in the local table but
+ # cannot be accessed directly from the source code. For example, the
+ # iteration variable in a for loop or the positional parameter on a method
+ # definition that is destructured.
+ AnonymousLocal = Object.new
+
+ # For the given source, compiles with CRuby and returns a list of all of the
+ # sets of local variables that were encountered.
+ def cruby_locals(source)
+ locals = [] #: Array[Array[Symbol | Integer]]
+ stack = [ISeq.new(ignore_warnings { RubyVM::InstructionSequence.compile(source) }.to_a)]
+
+ while (iseq = stack.pop)
+ names = [*iseq.local_table]
+ names.map!.with_index do |name, index|
+ # When an anonymous local variable is present in the iseq's local
+ # table, it is represented as the stack offset from the top.
+ # However, when these are dumped to binary and read back in, they
+ # are replaced with the symbol :#arg_rest. To consistently handle
+ # this, we replace them here with their index.
+ if name == :"#arg_rest"
+ names.length - index + 1
+ else
+ name
+ end
+ end
+
+ locals << names
+ iseq.each_child { |child| stack << child }
+ end
+
+ locals
+ end
+
+ # For the given source, parses with prism and returns a list of all of the
+ # sets of local variables that were encountered.
+ def prism_locals(source)
+ locals = [] #: Array[Array[Symbol | Integer]]
+ stack = [Prism.parse(source).value] #: Array[Prism::node]
+
+ while (node = stack.pop)
+ case node
+ when BlockNode, DefNode, LambdaNode
+ names = node.locals
+ params =
+ if node.is_a?(DefNode)
+ node.parameters
+ elsif node.parameters.is_a?(NumberedParametersNode)
+ nil
+ else
+ node.parameters&.parameters
+ end
+
+ # prism places parameters in the same order that they appear in the
+ # source. CRuby places them in the order that they need to appear
+ # according to their own internal calling convention. We mimic that
+ # order here so that we can compare properly.
+ if params
+ sorted = [
+ *params.requireds.map do |required|
+ if required.is_a?(RequiredParameterNode)
+ required.name
+ else
+ AnonymousLocal
+ end
+ end,
+ *params.optionals.map(&:name),
+ *((params.rest.name || :*) if params.rest && !params.rest.is_a?(ImplicitRestNode)),
+ *params.posts.map do |post|
+ if post.is_a?(RequiredParameterNode)
+ post.name
+ else
+ AnonymousLocal
+ end
+ end,
+ *params.keywords.grep(RequiredKeywordParameterNode).map(&:name),
+ *params.keywords.grep(OptionalKeywordParameterNode).map(&:name),
+ ]
+
+ sorted << AnonymousLocal if params.keywords.any?
+
+ if params.keyword_rest.is_a?(ForwardingParameterNode)
+ sorted.push(:*, :**, :&, :"...")
+ elsif params.keyword_rest.is_a?(KeywordRestParameterNode)
+ sorted << (params.keyword_rest.name || :**)
+ end
+
+ # Recurse down the parameter tree to find any destructured
+ # parameters and add them after the other parameters.
+ param_stack = params.requireds.concat(params.posts).grep(MultiTargetNode).reverse
+ while (param = param_stack.pop)
+ case param
+ when MultiTargetNode
+ param_stack.concat(param.rights.reverse)
+ param_stack << param.rest if param.rest&.expression && !sorted.include?(param.rest.expression.name)
+ param_stack.concat(param.lefts.reverse)
+ when RequiredParameterNode
+ sorted << param.name
+ when SplatNode
+ sorted << param.expression.name
+ end
+ end
+
+ if params.block
+ sorted << (params.block.name || :&)
+ end
+
+ names = sorted.concat(names - sorted)
+ end
+
+ names.map!.with_index do |name, index|
+ if name == AnonymousLocal
+ names.length - index + 1
+ else
+ name
+ end
+ end
+
+ locals << names
+ when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
+ locals << node.locals
+ when ForNode
+ locals << [2]
+ when PostExecutionNode
+ locals.push([], [])
+ when InterpolatedRegularExpressionNode
+ locals << [] if node.once?
+ end
+
+ stack.concat(node.compact_child_nodes)
+ end
+
+ locals
end
end
end
diff --git a/test/prism/magic_comment_test.rb b/test/prism/magic_comment_test.rb
index 9e2e92af92..14653fb0f8 100644
--- a/test/prism/magic_comment_test.rb
+++ b/test/prism/magic_comment_test.rb
@@ -2,32 +2,109 @@
require_relative "test_helper"
-return if RUBY_ENGINE != "ruby"
-
module Prism
class MagicCommentTest < TestCase
- examples = [
- "# encoding: ascii",
- "# coding: ascii",
- "# eNcOdInG: ascii",
- "# CoDiNg: ascii",
- "# \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v",
- "# -*- encoding: ascii -*-",
- "# -*- coding: ascii -*-",
- "# -*- eNcOdInG: ascii -*-",
- "# -*- CoDiNg: ascii -*-",
- "# -*- \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v -*-",
- "# -*- foo: bar; encoding: ascii -*-",
- "# coding \t \r \v : \t \v \r ascii-8bit",
- "# vim: filetype=ruby, fileencoding=windows-31j, tabsize=3, shiftwidth=3"
- ]
-
- examples.each.with_index(1) do |example, index|
- define_method(:"test_magic_comment_#{index}") do
- expected = RubyVM::InstructionSequence.compile(%Q{#{example}\n""}).eval.encoding
- actual = Prism.parse(example).encoding
+ if RUBY_ENGINE == "ruby"
+ class MagicCommentRipper < Ripper
+ attr_reader :magic_comments
+
+ def initialize(*)
+ super
+ @magic_comments = []
+ end
+
+ def on_magic_comment(key, value)
+ @magic_comments << [key, value]
+ super
+ end
+ end
+
+ Fixture.each do |fixture|
+ define_method(fixture.test_name) { assert_magic_comments(fixture) }
+ end
+ end
+
+ def test_encoding
+ assert_magic_encoding(Encoding::US_ASCII, "# encoding: ascii")
+ end
+
+ def test_coding
+ assert_magic_encoding(Encoding::US_ASCII, "# coding: ascii")
+ end
+
+ def test_eNcOdInG
+ assert_magic_encoding(Encoding::US_ASCII, "# eNcOdInG: ascii")
+ end
+
+ def test_CoDiNg
+ assert_magic_encoding(Encoding::US_ASCII, "# CoDiNg: ascii")
+ end
+
+ def test_encoding_whitespace
+ assert_magic_encoding(Encoding::US_ASCII, "# \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v")
+ end
+
+ def test_emacs_encoding
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- encoding: ascii -*-")
+ end
+
+ def test_emacs_coding
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- coding: ascii -*-")
+ end
+
+ def test_emacs_eNcOdInG
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- eNcOdInG: ascii -*-")
+ end
+
+ def test_emacs_CoDiNg
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- CoDiNg: ascii -*-")
+ end
+
+ def test_emacs_whitespace
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- \s\t\v encoding \s\t\v : \s\t\v ascii \s\t\v -*-")
+ end
+
+ def test_emacs_multiple
+ assert_magic_encoding(Encoding::US_ASCII, "# -*- foo: bar; encoding: ascii -*-")
+ end
+
+ def test_coding_whitespace
+ assert_magic_encoding(Encoding::ASCII_8BIT, "# coding \t \r \v : \t \v \r ascii-8bit")
+ end
+
+ def test_vim
+ assert_magic_encoding(Encoding::Windows_31J, "# vim: filetype=ruby, fileencoding=windows-31j, tabsize=3, shiftwidth=3")
+ end
+
+ private
+
+ def assert_magic_encoding(expected, line)
+ source = %Q{#{line}\n""}
+ actual = Prism.parse(source).encoding
+
+ # Compare against our expectation.
+ assert_equal expected, actual
+
+ # Compare against Ruby's expectation.
+ if defined?(RubyVM::InstructionSequence)
+ expected = RubyVM::InstructionSequence.compile(source).eval.encoding
assert_equal expected, actual
end
end
+
+ def assert_magic_comments(fixture)
+ source = fixture.read
+
+ # Check that we get the correct number of magic comments when lexing with
+ # ripper.
+ expected = MagicCommentRipper.new(source).tap(&:parse).magic_comments
+ actual = Prism.parse(source).magic_comments
+
+ assert_equal expected.length, actual.length
+ expected.zip(actual).each do |(expected_key, expected_value), magic_comment|
+ assert_equal expected_key, magic_comment.key
+ assert_equal expected_value, magic_comment.value
+ end
+ end
end
end
diff --git a/test/prism/memsize_test.rb b/test/prism/memsize_test.rb
deleted file mode 100644
index d7e1448dbc..0000000000
--- a/test/prism/memsize_test.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-return if Prism::BACKEND == :FFI
-
-module Prism
- class MemsizeTest < TestCase
- def test_memsize
- result = Debug.memsize("2 + 3")
-
- assert_equal 5, result[:length]
- assert_kind_of Integer, result[:memsize]
- assert_equal 6, result[:node_count]
- end
- end
-end
diff --git a/test/prism/newline_offsets_test.rb b/test/prism/newline_offsets_test.rb
new file mode 100644
index 0000000000..99b808b1df
--- /dev/null
+++ b/test/prism/newline_offsets_test.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class NewlineOffsetsTest < TestCase
+ Fixture.each do |fixture|
+ define_method(fixture.test_name) { assert_newline_offsets(fixture) }
+ end
+
+ private
+
+ def assert_newline_offsets(fixture)
+ source = fixture.read
+
+ expected = [0]
+ source.b.scan("\n") { expected << $~.offset(0)[0] + 1 }
+
+ assert_equal expected, Prism.parse(source).source.offsets
+ end
+ end
+end
diff --git a/test/prism/newline_test.rb b/test/prism/newline_test.rb
index e9975b346e..03d7df4c97 100644
--- a/test/prism/newline_test.rb
+++ b/test/prism/newline_test.rb
@@ -6,11 +6,23 @@ return unless defined?(RubyVM::InstructionSequence)
module Prism
class NewlineTest < TestCase
- base = File.expand_path("../", __FILE__)
- filepaths = Dir["*.rb", base: base] - %w[encoding_test.rb errors_test.rb parser_test.rb static_literals_test.rb unescape_test.rb]
-
- filepaths.each do |relative|
- define_method("test_newline_flags_#{relative}") do
+ skips = %w[
+ errors_test.rb
+ locals_test.rb
+ regexp_test.rb
+ test_helper.rb
+ unescape_test.rb
+ encoding/regular_expression_encoding_test.rb
+ encoding/string_encoding_test.rb
+ result/static_literals_test.rb
+ result/warnings_test.rb
+ ruby/parser_test.rb
+ ruby/ruby_parser_test.rb
+ ]
+
+ base = __dir__
+ (Dir["{,api/,encoding/,result/,ruby/}*.rb", base: base] - skips).each do |relative|
+ define_method(:"test_#{relative}") do
assert_newlines(base, relative)
end
end
@@ -56,14 +68,6 @@ module Prism
assert_equal expected, actual
end
- def ignore_warnings
- previous_verbosity = $VERBOSE
- $VERBOSE = nil
- yield
- ensure
- $VERBOSE = previous_verbosity
- end
-
def rubyvm_lines(source)
queue = [ignore_warnings { RubyVM::InstructionSequence.compile(source) }]
lines = []
diff --git a/test/prism/onigmo_test.rb b/test/prism/onigmo_test.rb
new file mode 100644
index 0000000000..03f44c4e4c
--- /dev/null
+++ b/test/prism/onigmo_test.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE != "ruby"
+
+require_relative "test_helper"
+
+begin
+ require "onigmo"
+rescue LoadError
+ # In CRuby's CI, we're not going to test against the parser gem because we
+ # don't want to have to install it. So in this case we'll just skip this test.
+ return
+end
+
+module Prism
+ class OnigmoTest < TestCase
+ def test_ONIGERR_PARSE_DEPTH_LIMIT_OVER
+ assert_error(%Q{#{"(" * 4096}a#{")" * 4096}}, "parse depth limit over")
+ end
+
+ def test_ONIGERR_EMPTY_CHAR_CLASS
+ assert_error("[]", "empty char-class")
+ end
+
+ def test_ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED
+ assert_error("*", "target of repeat operator is not specified")
+ assert_error("+", "target of repeat operator is not specified")
+ assert_error("?", "target of repeat operator is not specified")
+ end
+
+ def test_ONIGERR_EMPTY_GROUP_NAME
+ assert_error("(?<>)", "group name is empty")
+ end
+
+ def test_ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS
+ assert_error("(", "end pattern with unmatched parenthesis")
+ assert_error("(|", "end pattern with unmatched parenthesis")
+ assert_error("(?<", "end pattern with unmatched parenthesis")
+ end
+
+ def test_ONIGERR_END_PATTERN_IN_GROUP
+ assert_error("(?", "end pattern in group")
+ assert_error("(?#", "end pattern in group")
+ end
+
+ def test_ONIGERR_UNDEFINED_GROUP_OPTION
+ assert_error("(?P", "undefined group option")
+ end
+
+ def test_ONIGERR_UNMATCHED_CLOSE_PARENTHESIS
+ assert_error(")", "unmatched close parenthesis")
+ end
+
+ private
+
+ def assert_error(source, message)
+ result = Prism.parse("/#{source}/")
+
+ assert result.failure?, "Expected #{source.inspect} to error"
+ assert_equal message, result.errors.first.message
+
+ error = assert_raise(ArgumentError) { Onigmo.parse(source) }
+ assert_equal message, error.message
+ end
+ end
+end
diff --git a/test/prism/parse_test.rb b/test/prism/parse_test.rb
deleted file mode 100644
index afb53e0668..0000000000
--- a/test/prism/parse_test.rb
+++ /dev/null
@@ -1,371 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-module Prism
- class ParseTest < TestCase
- # A subclass of Ripper that extracts out magic comments.
- class MagicCommentRipper < Ripper
- attr_reader :magic_comments
-
- def initialize(*)
- super
- @magic_comments = []
- end
-
- def on_magic_comment(key, value)
- @magic_comments << [key, value]
- super
- end
- end
-
- # When we pretty-print the trees to compare against the snapshots, we want to
- # be certain that we print with the same external encoding. This is because
- # methods like Symbol#inspect take into account external encoding and it could
- # change how the snapshot is generated. On machines with certain settings
- # (like LANG=C or -Eascii-8bit) this could have been changed. So here we're
- # going to force it to be UTF-8 to keep the snapshots consistent.
- def setup
- @previous_default_external = Encoding.default_external
- ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
- end
-
- def teardown
- ignore_warnings { Encoding.default_external = @previous_default_external }
- end
-
- def test_empty_string
- result = Prism.parse("")
- assert_equal [], result.value.statements.body
- end
-
- def test_parse_takes_file_path
- filepath = "filepath.rb"
- result = Prism.parse("def foo; __FILE__; end", filepath: filepath)
-
- assert_equal filepath, find_source_file_node(result.value).filepath
- end
-
- def test_parse_takes_line
- line = 4
- result = Prism.parse("def foo\n __FILE__\nend", line: line)
-
- assert_equal line, result.value.location.start_line
- assert_equal line + 1, find_source_file_node(result.value).location.start_line
-
- result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
- assert_equal line, result.value.first.location.start_line
- end
-
- def test_parse_takes_negative_lines
- line = -2
- result = Prism.parse("def foo\n __FILE__\nend", line: line)
-
- assert_equal line, result.value.location.start_line
- assert_equal line + 1, find_source_file_node(result.value).location.start_line
-
- result = Prism.parse_lex("def foo\n __FILE__\nend", line: line)
- assert_equal line, result.value.first.location.start_line
- end
-
- def test_parse_lex
- node, tokens = Prism.parse_lex("def foo; end").value
-
- assert_kind_of ProgramNode, node
- assert_equal 5, tokens.length
- end
-
- if !ENV["PRISM_BUILD_MINIMAL"]
- def test_dump_file
- assert_nothing_raised do
- Prism.dump_file(__FILE__)
- end
-
- error = assert_raise Errno::ENOENT do
- Prism.dump_file("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.dump_file(nil)
- end
- end
- end
-
- def test_lex_file
- assert_nothing_raised do
- Prism.lex_file(__FILE__)
- end
-
- error = assert_raise Errno::ENOENT do
- Prism.lex_file("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.lex_file(nil)
- end
- end
-
- def test_parse_lex_file
- node, tokens = Prism.parse_lex_file(__FILE__).value
-
- assert_kind_of ProgramNode, node
- refute_empty tokens
-
- error = assert_raise Errno::ENOENT do
- Prism.parse_lex_file("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.parse_lex_file(nil)
- end
- end
-
- def test_parse_file
- node = Prism.parse_file(__FILE__).value
- assert_kind_of ProgramNode, node
-
- error = assert_raise Errno::ENOENT do
- Prism.parse_file("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.parse_file(nil)
- end
- end
-
- def test_parse_file_success
- assert_predicate Prism.parse_file_comments(__FILE__), :any?
-
- error = assert_raise Errno::ENOENT do
- Prism.parse_file_comments("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.parse_file_comments(nil)
- end
- end
-
- def test_parse_file_comments
- assert_predicate Prism.parse_file_comments(__FILE__), :any?
-
- error = assert_raise Errno::ENOENT do
- Prism.parse_file_comments("idontexist.rb")
- end
-
- assert_equal "No such file or directory - idontexist.rb", error.message
-
- assert_raise TypeError do
- Prism.parse_file_comments(nil)
- end
- end
-
- # To accurately compare against Ripper, we need to make sure that we're
- # running on CRuby 3.2+.
- ripper_enabled = RUBY_ENGINE == "ruby" && RUBY_VERSION >= "3.2.0"
-
- # The FOCUS environment variable allows you to specify one particular fixture
- # to test, instead of all of them.
- base = File.join(__dir__, "fixtures")
- relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
-
- relatives.each do |relative|
- # These fail on TruffleRuby due to a difference in Symbol#inspect: :测试 vs :"测试"
- next if RUBY_ENGINE == "truffleruby" and %w[emoji_method_calls.txt seattlerb/bug202.txt seattlerb/magic_encoding_comment.txt].include?(relative)
-
- filepath = File.join(base, relative)
- snapshot = File.expand_path(File.join("snapshots", relative), __dir__)
-
- directory = File.dirname(snapshot)
- FileUtils.mkdir_p(directory) unless File.directory?(directory)
-
- 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.
- source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
-
- # 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)
- assert_empty result.errors
-
- # Next, pretty print the source.
- printed = PP.pp(result.value, +"", 79)
-
- if File.exist?(snapshot)
- saved = File.read(snapshot)
-
- # If the snapshot file exists, but the printed value does not match the
- # snapshot, then update the snapshot file.
- if printed != saved
- File.write(snapshot, printed)
- warn("Updated snapshot at #{snapshot}.")
- end
-
- # If the snapshot file exists, then assert that the printed value
- # matches the snapshot.
- assert_equal(saved, printed)
- else
- # If the snapshot file does not yet exist, then write it out now.
- File.write(snapshot, printed)
- warn("Created snapshot at #{snapshot}.")
- end
-
- if !ENV["PRISM_BUILD_MINIMAL"]
- # Next, assert that the value can be serialized and deserialized
- # without changing the shape of the tree.
- assert_equal_nodes(result.value, Prism.load(source, Prism.dump(source, filepath: relative)).value)
- end
-
- # Next, check that the location ranges of each node in the tree are a
- # superset of their respective child nodes.
- assert_non_overlapping_locations(result.value)
-
- # Next, assert that the newlines are in the expected places.
- expected_newlines = [0]
- source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
- assert_equal expected_newlines, Debug.newlines(source)
-
- 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)
- assert_equal [], lex_result.errors
- tokens = lex_result.value
-
- begin
- Prism.lex_ripper(source).zip(tokens).each do |(ripper, prism)|
- assert_equal ripper, prism
- end
- rescue SyntaxError
- raise ArgumentError, "Test file has invalid syntax #{filepath}"
- end
-
- # Next, check that we get the correct number of magic comments when
- # lexing with ripper.
- expected = MagicCommentRipper.new(source).tap(&:parse).magic_comments
- actual = result.magic_comments
-
- assert_equal expected.length, actual.length
- expected.zip(actual).each do |(expected_key, expected_value), magic_comment|
- assert_equal expected_key, magic_comment.key
- assert_equal expected_value, magic_comment.value
- end
- end
- end
- end
-
- Dir["*.txt", base: base].each do |relative|
- next if relative == "newline_terminated.txt" || relative == "spanning_heredoc_newlines.txt"
-
- # We test every snippet (separated by \n\n) in isolation
- # to ensure the parser does not try to read bytes further than the end of each snippet
- define_method "test_individual_snippets_#{relative}" do
- filepath = File.join(base, relative)
-
- # 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.
- file_contents = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
-
- file_contents.split(/(?<=\S)\n\n(?=\S)/).each do |snippet|
- snippet = snippet.rstrip
- result = Prism.parse(snippet, filepath: relative)
- assert_empty result.errors
-
- if !ENV["PRISM_BUILD_MINIMAL"]
- assert_equal_nodes(result.value, Prism.load(snippet, Prism.dump(snippet, filepath: relative)).value)
- end
- end
- end
- end
-
- private
-
- # Check that the location ranges of each node in the tree are a superset of
- # their respective child nodes.
- def assert_non_overlapping_locations(node)
- queue = [node]
-
- while (current = queue.shift)
- # We only want to compare parent/child location overlap in the case that
- # we are not looking at a heredoc. That's because heredoc locations are
- # special in that they only use the declaration of the heredoc.
- compare = !(current.is_a?(StringNode) ||
- current.is_a?(XStringNode) ||
- current.is_a?(InterpolatedStringNode) ||
- current.is_a?(InterpolatedXStringNode)) ||
- !current.opening&.start_with?("<<")
-
- current.child_nodes.each do |child|
- # child_nodes can return nil values, so we need to skip those.
- next unless child
-
- # Now that we know we have a child node, add that to the queue.
- queue << child
-
- if compare
- assert_operator current.location.start_offset, :<=, child.location.start_offset
- assert_operator current.location.end_offset, :>=, child.location.end_offset
- end
- end
- end
- end
-
- def find_source_file_node(program)
- queue = [program]
- while (node = queue.shift)
- return node if node.is_a?(SourceFileNode)
- queue.concat(node.compact_child_nodes)
- end
- end
-
- def ignore_warnings
- previous_verbosity = $VERBOSE
- $VERBOSE = nil
- yield
- ensure
- $VERBOSE = previous_verbosity
- end
- end
-end
diff --git a/test/prism/parser_test.rb b/test/prism/parser_test.rb
deleted file mode 100644
index 79b65cf75b..0000000000
--- a/test/prism/parser_test.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-begin
- verbose, $VERBOSE = $VERBOSE, nil
- require "parser/ruby33"
- require "prism/translation/parser33"
-rescue LoadError
- # In CRuby's CI, we're not going to test against the parser gem because we
- # don't want to have to install it. So in this case we'll just skip this test.
- return
-ensure
- $VERBOSE = verbose
-end
-
-# First, opt in to every AST feature.
-Parser::Builders::Default.modernize
-
-# Modify the source map == check so that it doesn't check against the node
-# itself so we don't get into a recursive loop.
-Parser::Source::Map.prepend(
- Module.new {
- def ==(other)
- self.class == other.class &&
- (instance_variables - %i[@node]).map do |ivar|
- instance_variable_get(ivar) == other.instance_variable_get(ivar)
- end.reduce(:&)
- end
- }
-)
-
-# Next, ensure that we're comparing the nodes and also comparing the source
-# ranges so that we're getting all of the necessary information.
-Parser::AST::Node.prepend(
- Module.new {
- def ==(other)
- super && (location == other.location)
- end
- }
-)
-
-module Prism
- class ParserTest < TestCase
- base = File.join(__dir__, "fixtures")
-
- # These files are erroring because of the parser gem being wrong.
- skip_incorrect = [
- "embdoc_no_newline_at_end.txt"
- ]
-
- # These files are either failing to parse or failing to translate, so we'll
- # skip them for now.
- skip_all = skip_incorrect | [
- "dash_heredocs.txt",
- "dos_endings.txt",
- "heredocs_with_ignored_newlines.txt",
- "regex.txt",
- "regex_char_width.txt",
- "spanning_heredoc.txt",
- "spanning_heredoc_newlines.txt",
- "unescaping.txt"
- ]
-
- # Not sure why these files are failing on JRuby, but skipping them for now.
- if RUBY_ENGINE == "jruby"
- skip_all.push("emoji_method_calls.txt", "symbols.txt")
- end
-
- # These files are failing to translate their lexer output into the lexer
- # output expected by the parser gem, so we'll skip them for now.
- skip_tokens = [
- "comments.txt",
- "heredoc_with_comment.txt",
- "indented_file_end.txt",
- "methods.txt",
- "strings.txt",
- "tilde_heredocs.txt",
- "xstring_with_backslash.txt"
- ]
-
- Dir["*.txt", base: base].each do |name|
- next if skip_all.include?(name)
-
- define_method("test_#{name}") do
- assert_equal_parses(File.join(base, name), compare_tokens: !skip_tokens.include?(name))
- end
- end
-
- private
-
- def assert_equal_parses(filepath, compare_tokens: true)
- buffer = Parser::Source::Buffer.new(filepath, 1)
- buffer.source = File.read(filepath)
-
- parser = Parser::Ruby33.new
- parser.diagnostics.consumer = ->(*) {}
- parser.diagnostics.all_errors_are_fatal = true
-
- expected_ast, expected_comments, expected_tokens =
- begin
- parser.tokenize(buffer)
- rescue ArgumentError, Parser::SyntaxError
- return
- end
-
- actual_ast, actual_comments, actual_tokens =
- Prism::Translation::Parser33.new.tokenize(buffer)
-
- assert_equal expected_ast, actual_ast, -> { assert_equal_asts_message(expected_ast, actual_ast) }
- assert_equal_tokens(expected_tokens, actual_tokens) if compare_tokens
- assert_equal_comments(expected_comments, actual_comments)
- end
-
- def assert_equal_asts_message(expected_ast, actual_ast)
- queue = [[expected_ast, actual_ast]]
-
- while (left, right = queue.shift)
- if left.type != right.type
- return "expected: #{left.type}\nactual: #{right.type}"
- end
-
- if left.location != right.location
- return "expected:\n#{left.inspect}\n#{left.location.inspect}\nactual:\n#{right.inspect}\n#{right.location.inspect}"
- end
-
- if left.type == :str && left.children[0] != right.children[0]
- return "expected: #{left.inspect}\nactual: #{right.inspect}"
- end
-
- left.children.zip(right.children).each do |left_child, right_child|
- queue << [left_child, right_child] if left_child.is_a?(Parser::AST::Node)
- end
- end
-
- "expected: #{expected_ast.inspect}\nactual: #{actual_ast.inspect}"
- end
-
- def assert_equal_tokens(expected_tokens, actual_tokens)
- if expected_tokens != actual_tokens
- expected_index = 0
- actual_index = 0
-
- while expected_index < expected_tokens.length
- expected_token = expected_tokens[expected_index]
- actual_token = actual_tokens[actual_index]
-
- expected_index += 1
- actual_index += 1
-
- # The parser gem always has a space before a string end in list
- # literals, but we don't. So we'll skip over the space.
- if expected_token[0] == :tSPACE && actual_token[0] == :tSTRING_END
- expected_index += 1
- next
- end
-
- # There are a lot of tokens that have very specific meaning according
- # to the context of the parser. We don't expose that information in
- # prism, so we need to normalize these tokens a bit.
- case actual_token[0]
- when :kDO
- actual_token[0] = expected_token[0] if %i[kDO_BLOCK kDO_LAMBDA].include?(expected_token[0])
- when :tLPAREN
- actual_token[0] = expected_token[0] if expected_token[0] == :tLPAREN2
- when :tPOW
- actual_token[0] = expected_token[0] if expected_token[0] == :tDSTAR
- end
-
- # Now we can assert that the tokens are actually equal.
- assert_equal expected_token, actual_token, -> {
- "expected: #{expected_token.inspect}\n" \
- "actual: #{actual_token.inspect}"
- }
- end
- end
- end
-
- def assert_equal_comments(expected_comments, actual_comments)
- assert_equal expected_comments, actual_comments, -> {
- "expected: #{expected_comments.inspect}\n" \
- "actual: #{actual_comments.inspect}"
- }
- end
- end
-end
diff --git a/test/prism/regexp_test.rb b/test/prism/regexp_test.rb
index 0a5fc2b4fc..297020fc72 100644
--- a/test/prism/regexp_test.rb
+++ b/test/prism/regexp_test.rb
@@ -2,78 +2,84 @@
require_relative "test_helper"
-return if Prism::BACKEND == :FFI
-
module Prism
class RegexpTest < TestCase
- ##############################################################################
+ ############################################################################
# These tests test the actual use case of extracting named capture groups
- ##############################################################################
+ ############################################################################
def test_named_captures_with_arrows
- assert_equal(["foo"], named_captures("(?<foo>bar)"))
+ assert_equal([:foo], named_captures("(?<foo>bar)"))
end
def test_named_captures_with_single_quotes
- assert_equal(["foo"], named_captures("(?'foo'bar)"))
+ assert_equal([:foo], named_captures("(?'foo'bar)"))
end
def test_nested_named_captures_with_arrows
- assert_equal(["foo", "bar"], named_captures("(?<foo>(?<bar>baz))"))
+ assert_equal([:foo, :bar], named_captures("(?<foo>(?<bar>baz))"))
end
def test_nested_named_captures_with_single_quotes
- assert_equal(["foo", "bar"], named_captures("(?'foo'(?'bar'baz))"))
+ assert_equal([:foo, :bar], named_captures("(?'foo'(?'bar'baz))"))
end
def test_allows_duplicate_named_captures
- assert_equal(["foo", "foo"], named_captures("(?<foo>bar)(?<foo>baz)"))
+ assert_equal([:foo], named_captures("(?<foo>bar)(?<foo>baz)"))
end
def test_named_capture_inside_fake_range_quantifier
- assert_equal(["foo"], named_captures("foo{1, (?<foo>2)}"))
+ assert_equal([:foo], named_captures("foo{1, (?<foo>2)}"))
+ end
+
+ def test_fake_named_captures_inside_character_sets
+ assert_equal([], named_captures("[a-z(?<foo>)]"))
end
- ##############################################################################
+ def test_fake_named_capture_inside_character_set_with_escaped_ending
+ assert_equal([], named_captures("[a-z\\](?<foo>)]"))
+ end
+
+ ############################################################################
# These tests test the rest of the AST. They are not exhaustive, but they
# should cover the most common cases. We test these to make sure we don't
# accidentally regress and stop being able to extract named captures.
- ##############################################################################
+ ############################################################################
def test_alternation
- refute_nil(named_captures("foo|bar"))
+ assert_valid_regexp("foo|bar")
end
def test_anchors
- refute_nil(named_captures("^foo$"))
+ assert_valid_regexp("^foo$")
end
def test_any
- refute_nil(named_captures("."))
+ assert_valid_regexp(".")
end
def test_posix_character_classes
- refute_nil(named_captures("[[:digit:]]"))
+ assert_valid_regexp("[[:digit:]]")
end
def test_negated_posix_character_classes
- refute_nil(named_captures("[[:^digit:]]"))
+ assert_valid_regexp("[[:^digit:]]")
end
def test_invalid_posix_character_classes_should_fall_back_to_regular_classes
- refute_nil(named_captures("[[:foo]]"))
+ assert_valid_regexp("[[:foo]]")
end
def test_character_sets
- refute_nil(named_captures("[abc]"))
+ assert_valid_regexp("[abc]")
end
def test_nested_character_sets
- refute_nil(named_captures("[[abc]]"))
+ assert_valid_regexp("[[abc]]")
end
def test_nested_character_sets_with_operators
- refute_nil(named_captures("[[abc] && [def]]"))
+ assert_valid_regexp("[[abc] && [def]]")
end
def test_named_capture_inside_nested_character_set
@@ -81,120 +87,108 @@ module Prism
end
def test_negated_character_sets
- refute_nil(named_captures("[^abc]"))
+ assert_valid_regexp("[^abc]")
end
def test_character_ranges
- refute_nil(named_captures("[a-z]"))
+ assert_valid_regexp("[a-z]")
end
def test_negated_character_ranges
- refute_nil(named_captures("[^a-z]"))
- end
-
- def test_fake_named_captures_inside_character_sets
- assert_equal([], named_captures("[a-z(?<foo>)]"))
- end
-
- def test_fake_named_capture_inside_character_set_with_escaped_ending
- assert_equal([], named_captures("[a-z\\](?<foo>)]"))
+ assert_valid_regexp("[^a-z]")
end
def test_comments
- refute_nil(named_captures("(?#foo)"))
+ assert_valid_regexp("(?#foo)")
end
def test_comments_with_escaped_parentheses
- refute_nil(named_captures("(?#foo\\)\\))"))
+ assert_valid_regexp("(?#foo\\)\\))")
end
def test_non_capturing_groups
- refute_nil(named_captures("(?:foo)"))
+ assert_valid_regexp("(?:foo)")
end
def test_positive_lookaheads
- refute_nil(named_captures("(?=foo)"))
+ assert_valid_regexp("(?=foo)")
end
def test_negative_lookaheads
- refute_nil(named_captures("(?!foo)"))
+ assert_valid_regexp("(?!foo)")
end
def test_positive_lookbehinds
- refute_nil(named_captures("(?<=foo)"))
+ assert_valid_regexp("(?<=foo)")
end
def test_negative_lookbehinds
- refute_nil(named_captures("(?<!foo)"))
+ assert_valid_regexp("(?<!foo)")
end
def test_atomic_groups
- refute_nil(named_captures("(?>foo)"))
+ assert_valid_regexp("(?>foo)")
end
def test_absence_operator
- refute_nil(named_captures("(?~foo)"))
+ assert_valid_regexp("(?~foo)")
end
def test_conditional_expression_with_index
- refute_nil(named_captures("(?(1)foo)"))
+ assert_valid_regexp("(?(1)foo)")
end
def test_conditional_expression_with_name
- refute_nil(named_captures("(?(foo)bar)"))
+ assert_valid_regexp("(?(foo)bar)")
end
def test_conditional_expression_with_group
- refute_nil(named_captures("(?(<foo>)bar)"))
+ assert_valid_regexp("(?(<foo>)bar)")
end
def test_options_on_groups
- refute_nil(named_captures("(?imxdau:foo)"))
- end
-
- def test_options_on_groups_with_invalid_options
- assert_nil(named_captures("(?z:bar)"))
+ assert_valid_regexp("(?imxdau:foo)")
end
def test_options_on_groups_getting_turned_off
- refute_nil(named_captures("(?-imx:foo)"))
+ assert_valid_regexp("(?-imx:foo)")
end
def test_options_on_groups_some_getting_turned_on_some_getting_turned_off
- refute_nil(named_captures("(?im-x:foo)"))
+ assert_valid_regexp("(?im-x:foo)")
end
def test_star_quantifier
- refute_nil(named_captures("foo*"))
+ assert_valid_regexp("foo*")
end
def test_plus_quantifier
- refute_nil(named_captures("foo+"))
+ assert_valid_regexp("foo+")
end
def test_question_mark_quantifier
- refute_nil(named_captures("foo?"))
+ assert_valid_regexp("foo?")
end
def test_endless_range_quantifier
- refute_nil(named_captures("foo{1,}"))
+ assert_valid_regexp("foo{1,}")
end
def test_beginless_range_quantifier
- refute_nil(named_captures("foo{,1}"))
+ assert_valid_regexp("foo{,1}")
end
def test_range_quantifier
- refute_nil(named_captures("foo{1,2}"))
+ assert_valid_regexp("foo{1,2}")
end
def test_fake_range_quantifier_because_of_spaces
- refute_nil(named_captures("foo{1, 2}"))
+ assert_valid_regexp("foo{1, 2}")
end
- ##############################################################################
+ ############################################################################
# These test that flag values are correct.
- ##############################################################################
+ ############################################################################
def test_flag_ignorecase
assert_equal(Regexp::IGNORECASE, options("i"))
@@ -229,26 +223,30 @@ module Prism
def test_last_encoding_option_wins
regex = "/foo/nu"
- option = Prism.parse(regex).value.statements.body.first.options
+ option = Prism.parse_statement(regex).options
assert_equal Regexp::FIXEDENCODING, option
regex = "/foo/un"
- option = Prism.parse(regex).value.statements.body.first.options
+ option = Prism.parse_statement(regex).options
assert_equal Regexp::NOENCODING, option
end
private
+ def assert_valid_regexp(source)
+ assert Prism.parse_success?("/#{source}/ =~ \"\"")
+ end
+
def named_captures(source)
- Debug.named_captures(source)
+ Prism.parse("/#{source}/ =~ \"\"").value.locals
end
def options(flags)
options =
["/foo/#{flags}", "/foo\#{1}/#{flags}"].map do |source|
- Prism.parse(source).value.statements.body.first.options
+ Prism.parse_statement(source).options
end
# Check that we get the same set of options from both regular expressions
diff --git a/test/prism/attribute_write_test.rb b/test/prism/result/attribute_write_test.rb
index bd83d72da3..8f2e352738 100644
--- a/test/prism/attribute_write_test.rb
+++ b/test/prism/result/attribute_write_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class AttributeWriteTest < TestCase
@@ -41,18 +41,14 @@ module Prism
private
- def parse(source)
- Prism.parse(source).value.statements.body.first
- end
-
def assert_attribute_write(source)
- call = parse(source)
+ call = Prism.parse_statement(source)
assert(call.attribute_write?)
assert_equal(1, eval(source))
end
def refute_attribute_write(source)
- call = parse(source)
+ call = Prism.parse_statement(source)
refute(call.attribute_write?)
refute_equal(1, eval(source))
end
diff --git a/test/prism/comments_test.rb b/test/prism/result/comments_test.rb
index b99c00268c..178623a75f 100644
--- a/test/prism/comments_test.rb
+++ b/test/prism/result/comments_test.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class CommentsTest < TestCase
def test_comment_inline
source = "# comment"
- assert_equal [0], Debug.newlines(source)
+ assert_equal [0], Prism.parse(source).source.offsets
assert_comment(
source,
diff --git a/test/prism/constant_path_node_test.rb b/test/prism/result/constant_path_node_test.rb
index dffb55c0ff..75925600ca 100644
--- a/test/prism/constant_path_node_test.rb
+++ b/test/prism/result/constant_path_node_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class ConstantPathNodeTest < TestCase
@@ -11,7 +11,7 @@ module Prism
Qux
RUBY
- constant_path = Prism.parse(source).value.statements.body.first
+ constant_path = Prism.parse_statement(source)
assert_equal("Foo::Bar::Baz::Qux", constant_path.full_name)
end
@@ -22,7 +22,7 @@ module Prism
Qux
RUBY
- constant_path = Prism.parse(source).value.statements.body.first
+ constant_path = Prism.parse_statement(source)
assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
constant_path.full_name
end
@@ -35,7 +35,7 @@ module Prism
Qux
RUBY
- constant_path = Prism.parse(source).value.statements.body.first
+ constant_path = Prism.parse_statement(source)
assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
constant_path.full_name
@@ -49,7 +49,7 @@ module Prism
Qux, Something = [1, 2]
RUBY
- node = Prism.parse(source).value.statements.body.first
+ node = Prism.parse_statement(source)
assert_equal("Foo::Bar::Baz::Qux", node.lefts.first.full_name)
end
@@ -60,7 +60,7 @@ module Prism
Qux, Something = [1, 2]
RUBY
- node = Prism.parse(source).value.statements.body.first
+ node = Prism.parse_statement(source)
assert_equal("::Foo::Bar::Baz::Qux", node.lefts.first.full_name)
end
@@ -69,7 +69,7 @@ module Prism
self::Foo, Bar = [1, 2]
RUBY
- constant_target = Prism.parse(source).value.statements.body.first
+ constant_target = Prism.parse_statement(source)
dynamic, static = constant_target.lefts
assert_raise(ConstantPathNode::DynamicPartsInConstantPathError) do
@@ -84,7 +84,7 @@ module Prism
Bar
RUBY
- constant = Prism.parse(source).value.statements.body.first
+ constant = Prism.parse_statement(source)
assert_equal("Bar", constant.full_name)
end
end
diff --git a/test/prism/result/equality_test.rb b/test/prism/result/equality_test.rb
new file mode 100644
index 0000000000..4f6e665a88
--- /dev/null
+++ b/test/prism/result/equality_test.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class EqualityTest < TestCase
+ def test_equality
+ assert_operator Prism.parse_statement("1"), :===, Prism.parse_statement("1")
+ assert_operator Prism.parse("1").value, :===, Prism.parse("1").value
+
+ complex_source = "class Something; @var = something.else { _1 }; end"
+ assert_operator Prism.parse_statement(complex_source), :===, Prism.parse_statement(complex_source)
+
+ refute_operator Prism.parse_statement("1"), :===, Prism.parse_statement("2")
+ refute_operator Prism.parse_statement("1"), :===, Prism.parse_statement("0x1")
+
+ complex_source_1 = "class Something; @var = something.else { _1 }; end"
+ complex_source_2 = "class Something; @var = something.else { _2 }; end"
+ refute_operator Prism.parse_statement(complex_source_1), :===, Prism.parse_statement(complex_source_2)
+ end
+ end
+end
diff --git a/test/prism/result/heredoc_test.rb b/test/prism/result/heredoc_test.rb
new file mode 100644
index 0000000000..7913c04a88
--- /dev/null
+++ b/test/prism/result/heredoc_test.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class HeredocTest < TestCase
+ def test_heredoc?
+ refute Prism.parse_statement("\"foo\"").heredoc?
+ refute Prism.parse_statement("\"foo \#{1}\"").heredoc?
+ refute Prism.parse_statement("`foo`").heredoc?
+ refute Prism.parse_statement("`foo \#{1}`").heredoc?
+
+ assert Prism.parse_statement("<<~HERE\nfoo\nHERE\n").heredoc?
+ assert Prism.parse_statement("<<~HERE\nfoo \#{1}\nHERE\n").heredoc?
+ assert Prism.parse_statement("<<~`HERE`\nfoo\nHERE\n").heredoc?
+ assert Prism.parse_statement("<<~`HERE`\nfoo \#{1}\nHERE\n").heredoc?
+ end
+ end
+end
diff --git a/test/prism/index_write_test.rb b/test/prism/result/index_write_test.rb
index 4c387da16c..0d5383b601 100644
--- a/test/prism/index_write_test.rb
+++ b/test/prism/result/index_write_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class IndexWriteTest < TestCase
@@ -40,41 +40,41 @@ module Prism
RUBY
end
- # def test_keywords_latest
- # assert_parse_failure(<<~RUBY)
- # foo[bar: 1] = 1
- # foo[bar: 1] &&= 1
- # foo[bar: 1] ||= 1
- # foo[bar: 1] += 1
- # RUBY
+ def test_keywords_latest
+ assert_parse_failure(<<~RUBY)
+ foo[bar: 1] = 1
+ foo[bar: 1] &&= 1
+ foo[bar: 1] ||= 1
+ foo[bar: 1] += 1
+ RUBY
- # assert_parse_failure(<<~RUBY)
- # def foo(**)
- # bar[**] = 1
- # bar[**] &&= 1
- # bar[**] ||= 1
- # bar[**] += 1
- # end
- # RUBY
- # end
+ assert_parse_failure(<<~RUBY)
+ def foo(**)
+ bar[**] = 1
+ bar[**] &&= 1
+ bar[**] ||= 1
+ bar[**] += 1
+ end
+ RUBY
+ end
- # def test_block_latest
- # assert_parse_failure(<<~RUBY)
- # foo[&bar] = 1
- # foo[&bar] &&= 1
- # foo[&bar] ||= 1
- # foo[&bar] += 1
- # RUBY
+ def test_block_latest
+ assert_parse_failure(<<~RUBY)
+ foo[&bar] = 1
+ foo[&bar] &&= 1
+ foo[&bar] ||= 1
+ foo[&bar] += 1
+ RUBY
- # assert_parse_failure(<<~RUBY)
- # def foo(&)
- # bar[&] = 1
- # bar[&] &&= 1
- # bar[&] ||= 1
- # bar[&] += 1
- # end
- # RUBY
- # end
+ assert_parse_failure(<<~RUBY)
+ def foo(&)
+ bar[&] = 1
+ bar[&] &&= 1
+ bar[&] ||= 1
+ bar[&] += 1
+ end
+ RUBY
+ end
private
diff --git a/test/prism/result/integer_base_flags_test.rb b/test/prism/result/integer_base_flags_test.rb
new file mode 100644
index 0000000000..ef15fb437c
--- /dev/null
+++ b/test/prism/result/integer_base_flags_test.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class IntegerBaseFlagsTest < TestCase
+ # Through some bit hackery, we want to allow consumers to use the integer
+ # base flags as the base itself. It has a nice property that the current
+ # alignment provides them in the correct order. So here we test that our
+ # assumption holds so that it doesn't change out from under us.
+ #
+ # In C, this would look something like:
+ #
+ # ((flags & ~DECIMAL) << 1) || 10
+ #
+ # We have to do some other work in Ruby because 0 is truthy and ~ on an
+ # integer doesn't have a fixed width.
+ def test_flags
+ assert_equal 2, base("0b1")
+ assert_equal 8, base("0o1")
+ assert_equal 10, base("0d1")
+ assert_equal 16, base("0x1")
+ end
+
+ private
+
+ def base(source)
+ node = Prism.parse_statement(source)
+ value = (node.send(:flags) & (0b1111 - IntegerBaseFlags::DECIMAL)) << 1
+ value == 0 ? 10 : value
+ end
+ end
+end
diff --git a/test/prism/integer_parse_test.rb b/test/prism/result/integer_parse_test.rb
index f42e817e79..7b5ce98bb6 100644
--- a/test/prism/integer_parse_test.rb
+++ b/test/prism/result/integer_parse_test.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
-
-return if Prism::BACKEND == :FFI
+require_relative "../test_helper"
module Prism
class IntegerParseTest < TestCase
@@ -37,9 +35,7 @@ module Prism
private
def assert_integer_parse(expected, source = expected.to_s)
- integer, string = Debug.integer_parse(source)
- assert_equal expected, integer
- assert_equal expected.to_s, string
+ assert_equal expected, Prism.parse_statement(source).value
end
end
end
diff --git a/test/prism/result/numeric_value_test.rb b/test/prism/result/numeric_value_test.rb
new file mode 100644
index 0000000000..5c89230a1f
--- /dev/null
+++ b/test/prism/result/numeric_value_test.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class NumericValueTest < TestCase
+ def test_numeric_value
+ assert_equal 123, Prism.parse_statement("123").value
+ assert_equal 3.14, Prism.parse_statement("3.14").value
+ assert_equal 42i, Prism.parse_statement("42i").value
+ assert_equal 42.1ri, Prism.parse_statement("42.1ri").value
+ assert_equal 3.14i, Prism.parse_statement("3.14i").value
+ assert_equal 42r, Prism.parse_statement("42r").value
+ assert_equal 0.5r, Prism.parse_statement("0.5r").value
+ assert_equal 42ri, Prism.parse_statement("42ri").value
+ assert_equal 0.5ri, Prism.parse_statement("0.5ri").value
+ assert_equal 0xFFr, Prism.parse_statement("0xFFr").value
+ assert_equal 0xFFri, Prism.parse_statement("0xFFri").value
+ end
+ end
+end
diff --git a/test/prism/result/overlap_test.rb b/test/prism/result/overlap_test.rb
new file mode 100644
index 0000000000..155bc870d3
--- /dev/null
+++ b/test/prism/result/overlap_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class OverlapTest < TestCase
+ Fixture.each do |fixture|
+ define_method(fixture.test_name) { assert_overlap(fixture) }
+ end
+
+ private
+
+ # Check that the location ranges of each node in the tree are a superset of
+ # their respective child nodes.
+ def assert_overlap(fixture)
+ queue = [Prism.parse_file(fixture.full_path).value]
+
+ while (current = queue.shift)
+ # We only want to compare parent/child location overlap in the case that
+ # we are not looking at a heredoc. That's because heredoc locations are
+ # special in that they only use the declaration of the heredoc.
+ compare = !(current.is_a?(StringNode) ||
+ current.is_a?(XStringNode) ||
+ current.is_a?(InterpolatedStringNode) ||
+ current.is_a?(InterpolatedXStringNode)) ||
+ !current.opening&.start_with?("<<")
+
+ current.child_nodes.each do |child|
+ # child_nodes can return nil values, so we need to skip those.
+ next unless child
+
+ # Now that we know we have a child node, add that to the queue.
+ queue << child
+
+ if compare
+ assert_operator current.location.start_offset, :<=, child.location.start_offset
+ assert_operator current.location.end_offset, :>=, child.location.end_offset
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/test/prism/redundant_return_test.rb b/test/prism/result/redundant_return_test.rb
index c668169245..3b20aeba00 100644
--- a/test/prism/redundant_return_test.rb
+++ b/test/prism/result/redundant_return_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class RedundantReturnTest < TestCase
diff --git a/test/prism/result/regular_expression_options_test.rb b/test/prism/result/regular_expression_options_test.rb
new file mode 100644
index 0000000000..ff6e20526f
--- /dev/null
+++ b/test/prism/result/regular_expression_options_test.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class RegularExpressionOptionsTest < TestCase
+ def test_options
+ assert_equal "", Prism.parse_statement("__FILE__").filepath
+ assert_equal "foo.rb", Prism.parse_statement("__FILE__", filepath: "foo.rb").filepath
+
+ assert_equal 1, Prism.parse_statement("foo").location.start_line
+ assert_equal 10, Prism.parse_statement("foo", line: 10).location.start_line
+
+ refute Prism.parse_statement("\"foo\"").frozen?
+ assert Prism.parse_statement("\"foo\"", frozen_string_literal: true).frozen?
+ refute Prism.parse_statement("\"foo\"", frozen_string_literal: false).frozen?
+
+ assert_kind_of CallNode, Prism.parse_statement("foo")
+ assert_kind_of LocalVariableReadNode, Prism.parse_statement("foo", scopes: [[:foo]])
+ assert_equal 1, Prism.parse_statement("foo", scopes: [[:foo], []]).depth
+
+ assert_equal [:foo], Prism.parse("foo", scopes: [[:foo]]).value.locals
+ end
+ end
+end
diff --git a/test/prism/location_test.rb b/test/prism/result/source_location_test.rb
index 81417fbcb3..ca74b36e6f 100644
--- a/test/prism/location_test.rb
+++ b/test/prism/result/source_location_test.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
- class LocationTest < TestCase
+ class SourceLocationTest < TestCase
def test_AliasGlobalVariableNode
assert_location(AliasGlobalVariableNode, "alias $foo $bar")
end
@@ -175,14 +175,6 @@ module Prism
assert_location(CallNode, "foo bar baz")
assert_location(CallNode, "foo bar('baz')")
-
- assert_location(CallNode, "-> { it }", 5...7, version: "3.3.0") do |node|
- node.body.body.first
- end
-
- assert_location(LocalVariableReadNode, "-> { it }", 5...7, version: "3.4.0") do |node|
- node.body.body.first
- end
end
def test_CallAndWriteNode
@@ -298,7 +290,6 @@ module Prism
def test_ConstantReadNode
assert_location(ConstantReadNode, "Foo")
- assert_location(ConstantReadNode, "Foo::Bar", 5...8, &:child)
end
def test_ConstantTargetNode
@@ -478,7 +469,7 @@ module Prism
end
def test_IndexTargetNode
- assert_location(IndexTargetNode, "foo[bar, &baz], = qux", 0...14) do |node|
+ assert_location(IndexTargetNode, "foo[bar], = qux", 0...8) do |node|
node.lefts.first
end
end
@@ -544,6 +535,24 @@ module Prism
assert_location(InterpolatedXStringNode, '`foo #{bar} baz`')
end
+ def test_ItLocalVariableReadNode
+ assert_location(ItLocalVariableReadNode, "-> { it }", 5...7) do |node|
+ node.body.body.first
+ end
+
+ assert_location(ItLocalVariableReadNode, "foo { it }", 6...8) do |node|
+ node.block.body.body.first
+ end
+
+ assert_location(CallNode, "-> { it }", 5...7, version: "3.3.0") do |node|
+ node.body.body.first
+ end
+
+ assert_location(ItLocalVariableReadNode, "-> { it }", 5...7, version: "3.4.0") do |node|
+ node.body.body.first
+ end
+ end
+
def test_ItParametersNode
assert_location(ItParametersNode, "-> { it }", &:parameters)
end
@@ -584,12 +593,6 @@ module Prism
def test_LocalVariableReadNode
assert_location(LocalVariableReadNode, "foo = 1; foo", 9...12)
- assert_location(LocalVariableReadNode, "-> { it }", 5...7) do |node|
- node.body.body.first
- end
- assert_location(LocalVariableReadNode, "foo { it }", 6...8) do |node|
- node.block.body.body.first
- end
end
def test_LocalVariableTargetNode
@@ -918,7 +921,7 @@ module Prism
def test_all_tested
expected = Prism.constants.grep(/.Node$/).sort - %i[MissingNode ProgramNode]
- actual = LocationTest.instance_methods(false).grep(/.Node$/).map { |name| name[5..].to_sym }.sort
+ actual = SourceLocationTest.instance_methods(false).grep(/.Node$/).map { |name| name[5..].to_sym }.sort
assert_equal expected, actual
end
diff --git a/test/prism/static_inspect_test.rb b/test/prism/result/static_inspect_test.rb
index 8df2fd241e..cf8cef3298 100644
--- a/test/prism/static_inspect_test.rb
+++ b/test/prism/result/static_inspect_test.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
-
-return if Prism::BACKEND == :FFI
+require_relative "../test_helper"
module Prism
class StaticInspectTest < TestCase
@@ -84,7 +82,8 @@ module Prism
private
def static_inspect(source, **options)
- Debug.static_inspect(source, **options)
+ warnings = Prism.parse("{ #{source} => 1, #{source} => 1 }", **options).warnings
+ warnings.last.message[/^key (.+) is duplicated and overwritten on line \d/, 1]
end
end
end
diff --git a/test/prism/static_literals_test.rb b/test/prism/result/static_literals_test.rb
index 31c802bf90..dcfc692897 100644
--- a/test/prism/static_literals_test.rb
+++ b/test/prism/result/static_literals_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class StaticLiteralsTest < TestCase
diff --git a/test/prism/warnings_test.rb b/test/prism/result/warnings_test.rb
index d01db01a0e..ea062d4221 100644
--- a/test/prism/warnings_test.rb
+++ b/test/prism/result/warnings_test.rb
@@ -2,8 +2,7 @@
return if RUBY_VERSION < "3.1"
-require_relative "test_helper"
-require "stringio"
+require_relative "../test_helper"
module Prism
class WarningsTest < TestCase
@@ -23,6 +22,23 @@ module Prism
assert_warning("a /b/", "wrap regexp in parentheses")
end
+ def test_binary_operator
+ [
+ [:**, "argument prefix"],
+ [:*, "argument prefix"],
+ [:<<, "here document"],
+ [:&, "argument prefix"],
+ [:+, "unary operator"],
+ [:-, "unary operator"],
+ [:/, "regexp literal"],
+ [:%, "string literal"]
+ ].each do |(operator, warning)|
+ assert_warning("puts 1 #{operator}0", warning)
+ assert_warning("puts :a #{operator}0", warning)
+ assert_warning("m = 1; puts m #{operator}0", warning)
+ end
+ end
+
def test_equal_in_conditional
assert_warning("if a = 1; end; a = a", "should be ==")
end
@@ -48,7 +64,7 @@ module Prism
end
def test_duplicated_when_clause
- assert_warning("case 1; when 1, 1; end", "clause with line")
+ assert_warning("case 1; when 1, 1; end", "when' clause")
end
def test_float_out_of_range
@@ -64,6 +80,15 @@ module Prism
assert_warning("if true\nelsif\nfalse; end", "end of line")
end
+ def test_shareable_constant_value
+ assert_warning("foo # shareable_constant_value: none", "ignored")
+ assert_warning("\v # shareable_constant_value: none", "ignored")
+
+ refute_warning("# shareable_constant_value: none")
+ refute_warning(" # shareable_constant_value: none")
+ refute_warning("\t\t# shareable_constant_value: none")
+ end
+
def test_string_in_predicate
assert_warning("if 'foo'; end", "string")
assert_warning("if \"\#{foo}\"; end", "string")
diff --git a/test/prism/compiler_test.rb b/test/prism/ruby/compiler_test.rb
index 9a326eb8d6..35ccfd5950 100644
--- a/test/prism/compiler_test.rb
+++ b/test/prism/ruby/compiler_test.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# typed: ignore
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class CompilerTest < TestCase
diff --git a/test/prism/desugar_compiler_test.rb b/test/prism/ruby/desugar_compiler_test.rb
index 1a1d580d2d..fe9a25e030 100644
--- a/test/prism/desugar_compiler_test.rb
+++ b/test/prism/ruby/desugar_compiler_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class DesugarCompilerTest < TestCase
diff --git a/test/prism/dispatcher_test.rb b/test/prism/ruby/dispatcher_test.rb
index 0d8a6d35e9..1b6d7f4117 100644
--- a/test/prism/dispatcher_test.rb
+++ b/test/prism/ruby/dispatcher_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class DispatcherTest < TestCase
diff --git a/test/prism/ruby/location_test.rb b/test/prism/ruby/location_test.rb
new file mode 100644
index 0000000000..fc80a5b875
--- /dev/null
+++ b/test/prism/ruby/location_test.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class LocationTest < TestCase
+ def test_join
+ call = Prism.parse_statement("1234 + 567")
+ receiver = call.receiver
+ argument = call.arguments.arguments.first
+
+ joined = receiver.location.join(argument.location)
+ assert_equal 0, joined.start_offset
+ assert_equal 10, joined.length
+
+ assert_raise(RuntimeError, "Incompatible locations") do
+ argument.location.join(receiver.location)
+ end
+
+ other_argument = Prism.parse_statement("1234 + 567").arguments.arguments.first
+
+ assert_raise(RuntimeError, "Incompatible sources") do
+ other_argument.location.join(receiver.location)
+ end
+
+ assert_raise(RuntimeError, "Incompatible sources") do
+ receiver.location.join(other_argument.location)
+ end
+ end
+
+ def test_character_offsets
+ program = Prism.parse("😀 + 😀\n😍 ||= 😍").value
+
+ # first 😀
+ location = program.statements.body.first.receiver.location
+ assert_equal 0, location.start_character_offset
+ assert_equal 1, location.end_character_offset
+ assert_equal 0, location.start_character_column
+ assert_equal 1, location.end_character_column
+
+ # second 😀
+ location = program.statements.body.first.arguments.arguments.first.location
+ assert_equal 4, location.start_character_offset
+ assert_equal 5, location.end_character_offset
+ assert_equal 4, location.start_character_column
+ assert_equal 5, location.end_character_column
+
+ # first 😍
+ location = program.statements.body.last.name_loc
+ assert_equal 6, location.start_character_offset
+ assert_equal 7, location.end_character_offset
+ assert_equal 0, location.start_character_column
+ assert_equal 1, location.end_character_column
+
+ # second 😍
+ location = program.statements.body.last.value.location
+ assert_equal 12, location.start_character_offset
+ assert_equal 13, location.end_character_offset
+ assert_equal 6, location.start_character_column
+ assert_equal 7, location.end_character_column
+ end
+
+ def test_code_units
+ program = Prism.parse("😀 + 😀\n😍 ||= 😍").value
+
+ # first 😀
+ location = program.statements.body.first.receiver.location
+
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # second 😀
+ location = program.statements.body.first.arguments.arguments.first.location
+
+ assert_equal 4, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 5, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 4, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 5, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 5, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 4, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 5, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 4, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 5, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 5, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # first 😍
+ location = program.statements.body.last.name_loc
+
+ assert_equal 6, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 8, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 6, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 10, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 7, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
+
+ # second 😍
+ location = program.statements.body.last.value.location
+
+ assert_equal 12, location.start_code_units_offset(Encoding::UTF_8)
+ assert_equal 15, location.start_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 12, location.start_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 13, location.end_code_units_offset(Encoding::UTF_8)
+ assert_equal 17, location.end_code_units_offset(Encoding::UTF_16LE)
+ assert_equal 13, location.end_code_units_offset(Encoding::UTF_32LE)
+
+ assert_equal 6, location.start_code_units_column(Encoding::UTF_8)
+ assert_equal 7, location.start_code_units_column(Encoding::UTF_16LE)
+ assert_equal 6, location.start_code_units_column(Encoding::UTF_32LE)
+
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_8)
+ assert_equal 9, location.end_code_units_column(Encoding::UTF_16LE)
+ assert_equal 7, location.end_code_units_column(Encoding::UTF_32LE)
+ end
+
+ def test_chop
+ location = Prism.parse("foo").value.location
+
+ assert_equal "fo", location.chop.slice
+ assert_equal "", location.chop.chop.chop.slice
+
+ # Check that we don't go negative.
+ 10.times { location = location.chop }
+ assert_equal "", location.slice
+ end
+
+ def test_slice_lines
+ method = Prism.parse_statement("\nprivate def foo\nend\n").arguments.arguments.first
+
+ assert_equal "private def foo\nend\n", method.slice_lines
+ end
+
+ def test_adjoin
+ program = Prism.parse("foo.bar = 1").value
+
+ location = program.statements.body.first.message_loc
+ adjoined = location.adjoin("=")
+
+ assert_kind_of Location, adjoined
+ refute_equal location, adjoined
+
+ assert_equal 4, adjoined.start_offset
+ assert_equal 9, adjoined.end_offset
+ end
+ end
+end
diff --git a/test/prism/parameters_signature_test.rb b/test/prism/ruby/parameters_signature_test.rb
index 0eed8d993d..9256bcc070 100644
--- a/test/prism/parameters_signature_test.rb
+++ b/test/prism/ruby/parameters_signature_test.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require_relative "test_helper"
-
return if RUBY_VERSION < "3.2"
+require_relative "../test_helper"
+
module Prism
class ParametersSignatureTest < TestCase
def test_req
@@ -56,7 +56,6 @@ module Prism
def test_key_ordering
omit("TruffleRuby returns keys in order they were declared") if RUBY_ENGINE == "truffleruby"
-
assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
end
@@ -75,14 +74,13 @@ module Prism
private
def assert_parameters(expected, source)
- eval("def self.m(#{source}); end")
-
- begin
- assert_equal(expected, method(:m).parameters)
- assert_equal(expected, signature(source))
- ensure
- singleton_class.undef_method(:m)
- end
+ # Compare against our expectation.
+ assert_equal(expected, signature(source))
+
+ # Compare against Ruby's expectation.
+ object = Object.new
+ eval("def object.m(#{source}); end")
+ assert_equal(expected, object.method(:m).parameters)
end
def signature(source)
diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb
new file mode 100644
index 0000000000..a5cf919ae5
--- /dev/null
+++ b/test/prism/ruby/parser_test.rb
@@ -0,0 +1,290 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+begin
+ verbose, $VERBOSE = $VERBOSE, nil
+ require "parser/ruby33"
+ require "prism/translation/parser33"
+rescue LoadError
+ # In CRuby's CI, we're not going to test against the parser gem because we
+ # don't want to have to install it. So in this case we'll just skip this test.
+ return
+ensure
+ $VERBOSE = verbose
+end
+
+# First, opt in to every AST feature.
+Parser::Builders::Default.modernize
+
+# Modify the source map == check so that it doesn't check against the node
+# itself so we don't get into a recursive loop.
+Parser::Source::Map.prepend(
+ Module.new {
+ def ==(other)
+ self.class == other.class &&
+ (instance_variables - %i[@node]).map do |ivar|
+ instance_variable_get(ivar) == other.instance_variable_get(ivar)
+ end.reduce(:&)
+ end
+ }
+)
+
+# Next, ensure that we're comparing the nodes and also comparing the source
+# ranges so that we're getting all of the necessary information.
+Parser::AST::Node.prepend(
+ Module.new {
+ def ==(other)
+ super && (location == other.location)
+ end
+ }
+)
+
+module Prism
+ class ParserTest < TestCase
+ # These files contain code that is being parsed incorrectly by the parser
+ # gem, and therefore we don't want to compare against our translation.
+ skip_incorrect = [
+ # https://github.com/whitequark/parser/issues/1017
+ "spanning_heredoc.txt",
+ "spanning_heredoc_newlines.txt",
+
+ # https://github.com/whitequark/parser/issues/1021
+ "seattlerb/heredoc_nested.txt",
+
+ # https://github.com/whitequark/parser/issues/1016
+ "whitequark/unary_num_pow_precedence.txt"
+ ]
+
+ # These files are either failing to parse or failing to translate, so we'll
+ # skip them for now.
+ skip_all = skip_incorrect | [
+ "regex.txt",
+ "unescaping.txt",
+ "seattlerb/bug190.txt",
+ "seattlerb/heredoc_with_extra_carriage_returns_windows.txt",
+ "seattlerb/heredoc_with_only_carriage_returns_windows.txt",
+ "seattlerb/heredoc_with_only_carriage_returns.txt",
+ "seattlerb/parse_line_heredoc_hardnewline.txt",
+ "seattlerb/pctW_lineno.txt",
+ "seattlerb/regexp_esc_C_slash.txt",
+ "unparser/corpus/literal/literal.txt",
+ "unparser/corpus/semantic/dstr.txt",
+ "whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt",
+ "whitequark/parser_slash_slash_n_escaping_in_literals.txt",
+ "whitequark/ruby_bug_11989.txt"
+ ]
+
+ # Not sure why these files are failing on JRuby, but skipping them for now.
+ if RUBY_ENGINE == "jruby"
+ skip_all.push("emoji_method_calls.txt", "symbols.txt")
+ end
+
+ # These files are failing to translate their lexer output into the lexer
+ # output expected by the parser gem, so we'll skip them for now.
+ skip_tokens = [
+ "comments.txt",
+ "dash_heredocs.txt",
+ "dos_endings.txt",
+ "embdoc_no_newline_at_end.txt",
+ "heredoc_with_comment.txt",
+ "heredocs_with_ignored_newlines.txt",
+ "indented_file_end.txt",
+ "methods.txt",
+ "strings.txt",
+ "tilde_heredocs.txt",
+ "xstring_with_backslash.txt",
+ "seattlerb/backticks_interpolation_line.txt",
+ "seattlerb/bug169.txt",
+ "seattlerb/case_in.txt",
+ "seattlerb/class_comments.txt",
+ "seattlerb/difficult4__leading_dots2.txt",
+ "seattlerb/difficult6__7.txt",
+ "seattlerb/difficult6__8.txt",
+ "seattlerb/dsym_esc_to_sym.txt",
+ "seattlerb/heredoc__backslash_dos_format.txt",
+ "seattlerb/heredoc_backslash_nl.txt",
+ "seattlerb/heredoc_comma_arg.txt",
+ "seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt",
+ "seattlerb/heredoc_squiggly_blank_lines.txt",
+ "seattlerb/heredoc_squiggly_interp.txt",
+ "seattlerb/heredoc_squiggly_tabs_extra.txt",
+ "seattlerb/heredoc_squiggly_tabs.txt",
+ "seattlerb/heredoc_squiggly_visually_blank_lines.txt",
+ "seattlerb/heredoc_squiggly.txt",
+ "seattlerb/heredoc_unicode.txt",
+ "seattlerb/heredoc_with_carriage_return_escapes_windows.txt",
+ "seattlerb/heredoc_with_carriage_return_escapes.txt",
+ "seattlerb/heredoc_with_interpolation_and_carriage_return_escapes_windows.txt",
+ "seattlerb/heredoc_with_interpolation_and_carriage_return_escapes.txt",
+ "seattlerb/interpolated_symbol_array_line_breaks.txt",
+ "seattlerb/interpolated_word_array_line_breaks.txt",
+ "seattlerb/label_vs_string.txt",
+ "seattlerb/module_comments.txt",
+ "seattlerb/non_interpolated_symbol_array_line_breaks.txt",
+ "seattlerb/non_interpolated_word_array_line_breaks.txt",
+ "seattlerb/parse_line_block_inline_comment_leading_newlines.txt",
+ "seattlerb/parse_line_block_inline_comment.txt",
+ "seattlerb/parse_line_block_inline_multiline_comment.txt",
+ "seattlerb/parse_line_dstr_escaped_newline.txt",
+ "seattlerb/parse_line_heredoc.txt",
+ "seattlerb/parse_line_multiline_str_literal_n.txt",
+ "seattlerb/parse_line_str_with_newline_escape.txt",
+ "seattlerb/pct_Q_backslash_nl.txt",
+ "seattlerb/pct_w_heredoc_interp_nested.txt",
+ "seattlerb/qsymbols_empty_space.txt",
+ "seattlerb/qw_escape_term.txt",
+ "seattlerb/qWords_space.txt",
+ "seattlerb/read_escape_unicode_curlies.txt",
+ "seattlerb/read_escape_unicode_h4.txt",
+ "seattlerb/required_kwarg_no_value.txt",
+ "seattlerb/slashy_newlines_within_string.txt",
+ "seattlerb/str_double_escaped_newline.txt",
+ "seattlerb/str_double_newline.txt",
+ "seattlerb/str_evstr_escape.txt",
+ "seattlerb/str_newline_hash_line_number.txt",
+ "seattlerb/str_single_newline.txt",
+ "seattlerb/symbol_empty.txt",
+ "seattlerb/symbols_empty_space.txt",
+ "seattlerb/TestRubyParserShared.txt",
+ "unparser/corpus/literal/assignment.txt",
+ "unparser/corpus/literal/dstr.txt",
+ "unparser/corpus/semantic/opasgn.txt",
+ "whitequark/args.txt",
+ "whitequark/beginless_erange_after_newline.txt",
+ "whitequark/beginless_irange_after_newline.txt",
+ "whitequark/bug_ascii_8bit_in_literal.txt",
+ "whitequark/bug_def_no_paren_eql_begin.txt",
+ "whitequark/dedenting_heredoc.txt",
+ "whitequark/dedenting_non_interpolating_heredoc_line_continuation.txt",
+ "whitequark/forward_arg_with_open_args.txt",
+ "whitequark/interp_digit_var.txt",
+ "whitequark/lbrace_arg_after_command_args.txt",
+ "whitequark/multiple_pattern_matches.txt",
+ "whitequark/newline_in_hash_argument.txt",
+ "whitequark/parser_bug_640.txt",
+ "whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt",
+ "whitequark/ruby_bug_11990.txt",
+ "whitequark/ruby_bug_14690.txt",
+ "whitequark/ruby_bug_9669.txt",
+ "whitequark/slash_newline_in_heredocs.txt",
+ "whitequark/space_args_arg_block.txt",
+ "whitequark/space_args_block.txt"
+ ]
+
+ Fixture.each do |fixture|
+ define_method(fixture.test_name) do
+ assert_equal_parses(
+ fixture,
+ compare_asts: !skip_all.include?(fixture.path),
+ compare_tokens: !skip_tokens.include?(fixture.path),
+ compare_comments: fixture.path != "embdoc_no_newline_at_end.txt"
+ )
+ end
+ end
+
+ private
+
+ def assert_equal_parses(fixture, compare_asts: true, compare_tokens: true, compare_comments: true)
+ buffer = Parser::Source::Buffer.new(fixture.path, 1)
+ buffer.source = fixture.read
+
+ parser = Parser::Ruby33.new
+ parser.diagnostics.consumer = ->(*) {}
+ parser.diagnostics.all_errors_are_fatal = true
+
+ expected_ast, expected_comments, expected_tokens =
+ begin
+ ignore_warnings { parser.tokenize(buffer) }
+ rescue ArgumentError, Parser::SyntaxError
+ return
+ end
+
+ actual_ast, actual_comments, actual_tokens =
+ ignore_warnings { Prism::Translation::Parser33.new.tokenize(buffer) }
+
+ if expected_ast == actual_ast
+ if !compare_asts
+ puts "#{fixture.path} is now passing"
+ end
+
+ assert_equal expected_ast, actual_ast, -> { assert_equal_asts_message(expected_ast, actual_ast) }
+ assert_equal_tokens(expected_tokens, actual_tokens) if compare_tokens
+ assert_equal_comments(expected_comments, actual_comments) if compare_comments
+ elsif compare_asts
+ assert_equal expected_ast, actual_ast, -> { assert_equal_asts_message(expected_ast, actual_ast) }
+ end
+ end
+
+ def assert_equal_asts_message(expected_ast, actual_ast)
+ queue = [[expected_ast, actual_ast]]
+
+ while (left, right = queue.shift)
+ if left.type != right.type
+ return "expected: #{left.type}\nactual: #{right.type}"
+ end
+
+ if left.location != right.location
+ return "expected:\n#{left.inspect}\n#{left.location.inspect}\nactual:\n#{right.inspect}\n#{right.location.inspect}"
+ end
+
+ if left.type == :str && left.children[0] != right.children[0]
+ return "expected: #{left.inspect}\nactual: #{right.inspect}"
+ end
+
+ left.children.zip(right.children).each do |left_child, right_child|
+ queue << [left_child, right_child] if left_child.is_a?(Parser::AST::Node)
+ end
+ end
+
+ "expected: #{expected_ast.inspect}\nactual: #{actual_ast.inspect}"
+ end
+
+ def assert_equal_tokens(expected_tokens, actual_tokens)
+ if expected_tokens != actual_tokens
+ expected_index = 0
+ actual_index = 0
+
+ while expected_index < expected_tokens.length
+ expected_token = expected_tokens[expected_index]
+ actual_token = actual_tokens[actual_index]
+
+ expected_index += 1
+ actual_index += 1
+
+ # The parser gem always has a space before a string end in list
+ # literals, but we don't. So we'll skip over the space.
+ if expected_token[0] == :tSPACE && actual_token[0] == :tSTRING_END
+ expected_index += 1
+ next
+ end
+
+ # There are a lot of tokens that have very specific meaning according
+ # to the context of the parser. We don't expose that information in
+ # prism, so we need to normalize these tokens a bit.
+ case actual_token[0]
+ when :kDO
+ actual_token[0] = expected_token[0] if %i[kDO_BLOCK kDO_LAMBDA].include?(expected_token[0])
+ when :tLPAREN
+ actual_token[0] = expected_token[0] if expected_token[0] == :tLPAREN2
+ when :tPOW
+ actual_token[0] = expected_token[0] if expected_token[0] == :tDSTAR
+ end
+
+ # Now we can assert that the tokens are actually equal.
+ assert_equal expected_token, actual_token, -> {
+ "expected: #{expected_token.inspect}\n" \
+ "actual: #{actual_token.inspect}"
+ }
+ end
+ end
+ end
+
+ def assert_equal_comments(expected_comments, actual_comments)
+ assert_equal expected_comments, actual_comments, -> {
+ "expected: #{expected_comments.inspect}\n" \
+ "actual: #{actual_comments.inspect}"
+ }
+ end
+ end
+end
diff --git a/test/prism/pattern_test.rb b/test/prism/ruby/pattern_test.rb
index e0aa079cb9..23f512fc1c 100644
--- a/test/prism/pattern_test.rb
+++ b/test/prism/ruby/pattern_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class PatternTest < TestCase
diff --git a/test/prism/reflection_test.rb b/test/prism/ruby/reflection_test.rb
index 869b68b1f8..3ac462e1ac 100644
--- a/test/prism/reflection_test.rb
+++ b/test/prism/ruby/reflection_test.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class ReflectionTest < TestCase
diff --git a/test/prism/ripper_test.rb b/test/prism/ruby/ripper_test.rb
index 07238fc3d5..8db47da3d3 100644
--- a/test/prism/ripper_test.rb
+++ b/test/prism/ruby/ripper_test.rb
@@ -2,13 +2,11 @@
return if RUBY_VERSION < "3.3"
-require_relative "test_helper"
+require_relative "../test_helper"
module Prism
class RipperTest < TestCase
- base = File.join(__dir__, "fixtures")
- relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
-
+ # Skip these tests that Ripper is reporting the wrong results for.
incorrect = [
# Ripper incorrectly attributes the block to the keyword.
"seattlerb/block_break.txt",
@@ -31,6 +29,7 @@ module Prism
"spanning_heredoc.txt"
]
+ # Skip these tests that we haven't implemented yet.
omitted = [
"dos_endings.txt",
"heredocs_with_ignored_newlines.txt",
@@ -50,30 +49,8 @@ module Prism
"whitequark/slash_newline_in_heredocs.txt"
]
- relatives.each do |relative|
- # Skip the tests that Ripper is reporting the wrong results for.
- next if incorrect.include?(relative)
-
- # Skip the tests we haven't implemented yet.
- next if omitted.include?(relative)
-
- filepath = File.join(__dir__, "fixtures", relative)
-
- define_method "test_ripper_#{relative}" do
- source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
-
- case relative
- when /break|next|redo|if|unless|rescue|control|keywords|retry/
- source = "-> do\nrescue\n#{source}\nend"
- end
-
- case source
- when /^ *yield/
- source = "def __invalid_yield__\n#{source}\nend"
- end
-
- assert_ripper(source)
- end
+ Fixture.each(except: incorrect | omitted) do |fixture|
+ define_method(fixture.test_name) { assert_ripper(fixture.read) }
end
private
diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb
new file mode 100644
index 0000000000..a13daeeb84
--- /dev/null
+++ b/test/prism/ruby/ruby_parser_test.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+return if RUBY_ENGINE == "jruby"
+
+require_relative "../test_helper"
+
+begin
+ require "ruby_parser"
+rescue LoadError
+ # In CRuby's CI, we're not going to test against the ruby_parser gem because
+ # we don't want to have to install it. So in this case we'll just skip this
+ # test.
+ return
+end
+
+# We want to also compare lines and files to make sure we're setting them
+# correctly.
+Sexp.prepend(
+ Module.new do
+ def ==(other)
+ super && line == other.line && file == other.file # && line_max == other.line_max
+ end
+ end
+)
+
+module Prism
+ class RubyParserTest < TestCase
+ todos = [
+ "newline_terminated.txt",
+ "regex_char_width.txt",
+ "seattlerb/bug169.txt",
+ "seattlerb/masgn_colon3.txt",
+ "seattlerb/messy_op_asgn_lineno.txt",
+ "seattlerb/op_asgn_primary_colon_const_command_call.txt",
+ "seattlerb/regexp_esc_C_slash.txt",
+ "seattlerb/str_lit_concat_bad_encodings.txt",
+ "unescaping.txt",
+ "unparser/corpus/literal/kwbegin.txt",
+ "unparser/corpus/literal/send.txt",
+ "whitequark/masgn_const.txt",
+ "whitequark/ruby_bug_12402.txt",
+ "whitequark/ruby_bug_14690.txt",
+ "whitequark/space_args_block.txt"
+ ]
+
+ # https://github.com/seattlerb/ruby_parser/issues/344
+ failures = [
+ "alias.txt",
+ "dos_endings.txt",
+ "heredocs_with_ignored_newlines.txt",
+ "method_calls.txt",
+ "methods.txt",
+ "multi_write.txt",
+ "not.txt",
+ "patterns.txt",
+ "regex.txt",
+ "seattlerb/and_multi.txt",
+ "seattlerb/heredoc__backslash_dos_format.txt",
+ "seattlerb/heredoc_bad_hex_escape.txt",
+ "seattlerb/heredoc_bad_oct_escape.txt",
+ "seattlerb/heredoc_with_extra_carriage_horrible_mix.txt",
+ "seattlerb/heredoc_with_extra_carriage_returns_windows.txt",
+ "seattlerb/heredoc_with_only_carriage_returns_windows.txt",
+ "seattlerb/heredoc_with_only_carriage_returns.txt",
+ "spanning_heredoc_newlines.txt",
+ "spanning_heredoc.txt",
+ "tilde_heredocs.txt",
+ "unparser/corpus/literal/literal.txt",
+ "while.txt",
+ "whitequark/cond_eflipflop.txt",
+ "whitequark/cond_iflipflop.txt",
+ "whitequark/cond_match_current_line.txt",
+ "whitequark/dedenting_heredoc.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",
+ "whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt",
+ "whitequark/pattern_matching_single_line.txt",
+ "whitequark/ruby_bug_11989.txt",
+ "whitequark/slash_newline_in_heredocs.txt"
+ ]
+
+ Fixture.each(except: failures) do |fixture|
+ define_method(fixture.test_name) do
+ assert_ruby_parser(fixture, todos.include?(fixture.path))
+ end
+ end
+
+ private
+
+ def assert_ruby_parser(fixture, allowed_failure)
+ source = fixture.read
+ expected = ignore_warnings { ::RubyParser.new.parse(source, fixture.path) }
+ actual = Prism::Translation::RubyParser.new.parse(source, fixture.path)
+
+ if !allowed_failure
+ assert_equal(expected, actual, -> { message(expected, actual) })
+ elsif expected == actual
+ puts "#{name} now passes"
+ end
+ end
+
+ def message(expected, actual)
+ if expected == actual
+ nil
+ elsif expected.is_a?(Sexp) && actual.is_a?(Sexp)
+ if expected.line != actual.line
+ "expected: (#{expected.inspect} line=#{expected.line}), actual: (#{actual.inspect} line=#{actual.line})"
+ elsif expected.file != actual.file
+ "expected: (#{expected.inspect} file=#{expected.file}), actual: (#{actual.inspect} file=#{actual.file})"
+ elsif expected.length != actual.length
+ "expected: (#{expected.inspect} length=#{expected.length}), actual: (#{actual.inspect} length=#{actual.length})"
+ else
+ expected.zip(actual).find do |expected_field, actual_field|
+ result = message(expected_field, actual_field)
+ break result if result
+ end
+ end
+ else
+ "expected: #{expected.inspect}, actual: #{actual.inspect}"
+ end
+ end
+ end
+end
diff --git a/test/prism/ruby/tunnel_test.rb b/test/prism/ruby/tunnel_test.rb
new file mode 100644
index 0000000000..0214681604
--- /dev/null
+++ b/test/prism/ruby/tunnel_test.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require_relative "../test_helper"
+
+module Prism
+ class TunnelTest < TestCase
+ def test_tunnel
+ program = Prism.parse("foo(1) +\n bar(2, 3) +\n baz(3, 4, 5)").value
+
+ tunnel = program.tunnel(1, 4).last
+ assert_kind_of IntegerNode, tunnel
+ assert_equal 1, tunnel.value
+
+ tunnel = program.tunnel(2, 6).last
+ assert_kind_of IntegerNode, tunnel
+ assert_equal 2, tunnel.value
+
+ tunnel = program.tunnel(3, 9).last
+ assert_kind_of IntegerNode, tunnel
+ assert_equal 4, tunnel.value
+
+ tunnel = program.tunnel(3, 8)
+ assert_equal [ProgramNode, StatementsNode, CallNode, ArgumentsNode, CallNode, ArgumentsNode], tunnel.map(&:class)
+ end
+ end
+end
diff --git a/test/prism/ruby_api_test.rb b/test/prism/ruby_api_test.rb
deleted file mode 100644
index 9e408d1edd..0000000000
--- a/test/prism/ruby_api_test.rb
+++ /dev/null
@@ -1,275 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "test_helper"
-
-module Prism
- class RubyAPITest < TestCase
- if !ENV["PRISM_BUILD_MINIMAL"]
- def test_ruby_api
- filepath = __FILE__
- source = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8)
-
- assert_equal Prism.lex(source, filepath: filepath).value, Prism.lex_file(filepath).value
- assert_equal Prism.dump(source, filepath: filepath), Prism.dump_file(filepath)
-
- serialized = Prism.dump(source, filepath: filepath)
- ast1 = Prism.load(source, serialized).value
- ast2 = Prism.parse(source, filepath: filepath).value
- ast3 = Prism.parse_file(filepath).value
-
- assert_equal_nodes ast1, ast2
- assert_equal_nodes ast2, ast3
- end
- end
-
- def test_parse_success?
- assert Prism.parse_success?("1")
- refute Prism.parse_success?("<>")
- end
-
- def test_parse_file_success?
- assert Prism.parse_file_success?(__FILE__)
- end
-
- def test_options
- assert_equal "", Prism.parse("__FILE__").value.statements.body[0].filepath
- assert_equal "foo.rb", Prism.parse("__FILE__", filepath: "foo.rb").value.statements.body[0].filepath
-
- assert_equal 1, Prism.parse("foo").value.statements.body[0].location.start_line
- assert_equal 10, Prism.parse("foo", line: 10).value.statements.body[0].location.start_line
-
- refute Prism.parse("\"foo\"").value.statements.body[0].frozen?
- assert Prism.parse("\"foo\"", frozen_string_literal: true).value.statements.body[0].frozen?
- refute Prism.parse("\"foo\"", frozen_string_literal: false).value.statements.body[0].frozen?
-
- assert_kind_of Prism::CallNode, Prism.parse("foo").value.statements.body[0]
- assert_kind_of Prism::LocalVariableReadNode, Prism.parse("foo", scopes: [[:foo]]).value.statements.body[0]
- assert_equal 1, Prism.parse("foo", scopes: [[:foo], []]).value.statements.body[0].depth
-
- assert_equal [:foo], Prism.parse("foo", scopes: [[:foo]]).value.locals
- end
-
- def test_literal_value_method
- assert_equal 123, parse_expression("123").value
- assert_equal 3.14, parse_expression("3.14").value
- assert_equal 42i, parse_expression("42i").value
- assert_equal 42.1ri, parse_expression("42.1ri").value
- assert_equal 3.14i, parse_expression("3.14i").value
- assert_equal 42r, parse_expression("42r").value
- assert_equal 0.5r, parse_expression("0.5r").value
- assert_equal 42ri, parse_expression("42ri").value
- assert_equal 0.5ri, parse_expression("0.5ri").value
- assert_equal 0xFFr, parse_expression("0xFFr").value
- assert_equal 0xFFri, parse_expression("0xFFri").value
- end
-
- def test_location_join
- recv, args_node, _ = parse_expression("1234 + 567").child_nodes
- arg = args_node.arguments[0]
-
- joined = recv.location.join(arg.location)
- assert_equal 0, joined.start_offset
- assert_equal 10, joined.length
-
- assert_raise RuntimeError, "Incompatible locations" do
- arg.location.join(recv.location)
- end
-
- other_arg = parse_expression("1234 + 567").arguments.arguments[0]
-
- assert_raise RuntimeError, "Incompatible sources" do
- other_arg.location.join(recv.location)
- end
-
- assert_raise RuntimeError, "Incompatible sources" do
- recv.location.join(other_arg.location)
- end
- end
-
- def test_location_character_offsets
- program = Prism.parse("😀 + 😀\n😍 ||= 😍").value
-
- # first 😀
- location = program.statements.body.first.receiver.location
- assert_equal 0, location.start_character_offset
- assert_equal 1, location.end_character_offset
- assert_equal 0, location.start_character_column
- assert_equal 1, location.end_character_column
-
- # second 😀
- location = program.statements.body.first.arguments.arguments.first.location
- assert_equal 4, location.start_character_offset
- assert_equal 5, location.end_character_offset
- assert_equal 4, location.start_character_column
- assert_equal 5, location.end_character_column
-
- # first 😍
- location = program.statements.body.last.name_loc
- assert_equal 6, location.start_character_offset
- assert_equal 7, location.end_character_offset
- assert_equal 0, location.start_character_column
- assert_equal 1, location.end_character_column
-
- # second 😍
- location = program.statements.body.last.value.location
- assert_equal 12, location.start_character_offset
- assert_equal 13, location.end_character_offset
- assert_equal 6, location.start_character_column
- assert_equal 7, location.end_character_column
- end
-
- def test_location_code_units
- program = Prism.parse("😀 + 😀\n😍 ||= 😍").value
-
- # first 😀
- location = program.statements.body.first.receiver.location
-
- assert_equal 0, location.start_code_units_offset(Encoding::UTF_8)
- assert_equal 0, location.start_code_units_offset(Encoding::UTF_16LE)
- assert_equal 0, location.start_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 1, location.end_code_units_offset(Encoding::UTF_8)
- assert_equal 2, location.end_code_units_offset(Encoding::UTF_16LE)
- assert_equal 1, location.end_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
- assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
- assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
-
- assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
- assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
- assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
-
- # second 😀
- location = program.statements.body.first.arguments.arguments.first.location
-
- assert_equal 4, location.start_code_units_offset(Encoding::UTF_8)
- assert_equal 5, location.start_code_units_offset(Encoding::UTF_16LE)
- assert_equal 4, location.start_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 5, location.end_code_units_offset(Encoding::UTF_8)
- assert_equal 7, location.end_code_units_offset(Encoding::UTF_16LE)
- assert_equal 5, location.end_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 4, location.start_code_units_column(Encoding::UTF_8)
- assert_equal 5, location.start_code_units_column(Encoding::UTF_16LE)
- assert_equal 4, location.start_code_units_column(Encoding::UTF_32LE)
-
- assert_equal 5, location.end_code_units_column(Encoding::UTF_8)
- assert_equal 7, location.end_code_units_column(Encoding::UTF_16LE)
- assert_equal 5, location.end_code_units_column(Encoding::UTF_32LE)
-
- # first 😍
- location = program.statements.body.last.name_loc
-
- assert_equal 6, location.start_code_units_offset(Encoding::UTF_8)
- assert_equal 8, location.start_code_units_offset(Encoding::UTF_16LE)
- assert_equal 6, location.start_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 7, location.end_code_units_offset(Encoding::UTF_8)
- assert_equal 10, location.end_code_units_offset(Encoding::UTF_16LE)
- assert_equal 7, location.end_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 0, location.start_code_units_column(Encoding::UTF_8)
- assert_equal 0, location.start_code_units_column(Encoding::UTF_16LE)
- assert_equal 0, location.start_code_units_column(Encoding::UTF_32LE)
-
- assert_equal 1, location.end_code_units_column(Encoding::UTF_8)
- assert_equal 2, location.end_code_units_column(Encoding::UTF_16LE)
- assert_equal 1, location.end_code_units_column(Encoding::UTF_32LE)
-
- # second 😍
- location = program.statements.body.last.value.location
-
- assert_equal 12, location.start_code_units_offset(Encoding::UTF_8)
- assert_equal 15, location.start_code_units_offset(Encoding::UTF_16LE)
- assert_equal 12, location.start_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 13, location.end_code_units_offset(Encoding::UTF_8)
- assert_equal 17, location.end_code_units_offset(Encoding::UTF_16LE)
- assert_equal 13, location.end_code_units_offset(Encoding::UTF_32LE)
-
- assert_equal 6, location.start_code_units_column(Encoding::UTF_8)
- assert_equal 7, location.start_code_units_column(Encoding::UTF_16LE)
- assert_equal 6, location.start_code_units_column(Encoding::UTF_32LE)
-
- assert_equal 7, location.end_code_units_column(Encoding::UTF_8)
- assert_equal 9, location.end_code_units_column(Encoding::UTF_16LE)
- assert_equal 7, location.end_code_units_column(Encoding::UTF_32LE)
- end
-
- def test_location_chop
- location = Prism.parse("foo").value.location
-
- assert_equal "fo", location.chop.slice
- assert_equal "", location.chop.chop.chop.slice
-
- # Check that we don't go negative.
- 10.times { location = location.chop }
- assert_equal "", location.slice
- end
-
- def test_location_slice_lines
- result = Prism.parse("\nprivate def foo\nend\n")
- method = result.value.statements.body.first.arguments.arguments.first
-
- assert_equal "private def foo\nend\n", method.slice_lines
- end
-
- def test_heredoc?
- refute parse_expression("\"foo\"").heredoc?
- refute parse_expression("\"foo \#{1}\"").heredoc?
- refute parse_expression("`foo`").heredoc?
- refute parse_expression("`foo \#{1}`").heredoc?
-
- assert parse_expression("<<~HERE\nfoo\nHERE\n").heredoc?
- assert parse_expression("<<~HERE\nfoo \#{1}\nHERE\n").heredoc?
- assert parse_expression("<<~`HERE`\nfoo\nHERE\n").heredoc?
- assert parse_expression("<<~`HERE`\nfoo \#{1}\nHERE\n").heredoc?
- end
-
- # Through some bit hackery, we want to allow consumers to use the integer
- # base flags as the base itself. It has a nice property that the current
- # alignment provides them in the correct order. So here we test that our
- # assumption holds so that it doesn't change out from under us.
- #
- # In C, this would look something like:
- #
- # ((flags & ~DECIMAL) << 1) || 10
- #
- # We have to do some other work in Ruby because 0 is truthy and ~ on an
- # integer doesn't have a fixed width.
- def test_integer_base_flags
- base = -> (node) do
- value = (node.send(:flags) & (0b1111 - IntegerBaseFlags::DECIMAL)) << 1
- value == 0 ? 10 : value
- end
-
- assert_equal 2, base[parse_expression("0b1")]
- assert_equal 8, base[parse_expression("0o1")]
- assert_equal 10, base[parse_expression("0d1")]
- assert_equal 16, base[parse_expression("0x1")]
- end
-
- def test_node_equality
- assert_operator parse_expression("1"), :===, parse_expression("1")
- assert_operator Prism.parse("1").value, :===, Prism.parse("1").value
-
- complex_source = "class Something; @var = something.else { _1 }; end"
- assert_operator parse_expression(complex_source), :===, parse_expression(complex_source)
-
- refute_operator parse_expression("1"), :===, parse_expression("2")
- refute_operator parse_expression("1"), :===, parse_expression("0x1")
-
- complex_source_1 = "class Something; @var = something.else { _1 }; end"
- complex_source_2 = "class Something; @var = something.else { _2 }; end"
- refute_operator parse_expression(complex_source_1), :===, parse_expression(complex_source_2)
- end
-
- private
-
- def parse_expression(source)
- Prism.parse(source).value.statements.body.first
- end
- end
-end
diff --git a/test/prism/ruby_parser_test.rb b/test/prism/ruby_parser_test.rb
deleted file mode 100644
index 8edeac4b4f..0000000000
--- a/test/prism/ruby_parser_test.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-# frozen_string_literal: true
-
-return if RUBY_ENGINE == "jruby"
-
-require_relative "test_helper"
-
-begin
- require "ruby_parser"
-rescue LoadError
- # In CRuby's CI, we're not going to test against the ruby_parser gem because
- # we don't want to have to install it. So in this case we'll just skip this
- # test.
- return
-end
-
-# We want to also compare lines and files to make sure we're setting them
-# correctly.
-Sexp.prepend(
- Module.new do
- def ==(other)
- super && line == other.line && line_max == other.line_max && file == other.file
- end
- end
-)
-
-module Prism
- class RubyParserTest < TestCase
- base = File.join(__dir__, "fixtures")
-
- todos = %w[
- heredocs_nested.txt
- newline_terminated.txt
- regex_char_width.txt
- seattlerb/bug169.txt
- seattlerb/dstr_evstr.txt
- seattlerb/heredoc_squiggly_interp.txt
- seattlerb/masgn_colon3.txt
- seattlerb/messy_op_asgn_lineno.txt
- seattlerb/op_asgn_primary_colon_const_command_call.txt
- seattlerb/parse_line_evstr_after_break.txt
- seattlerb/regexp_esc_C_slash.txt
- seattlerb/str_lit_concat_bad_encodings.txt
- seattlerb/str_pct_nested_nested.txt
- unescaping.txt
- unparser/corpus/literal/kwbegin.txt
- unparser/corpus/literal/send.txt
- unparser/corpus/semantic/dstr.txt
- whitequark/masgn_const.txt
- whitequark/ruby_bug_12402.txt
- whitequark/ruby_bug_14690.txt
- whitequark/space_args_block.txt
- whitequark/string_concat.txt
- ]
-
- # https://github.com/seattlerb/ruby_parser/issues/344
- failures = %w[
- alias.txt
- dos_endings.txt
- heredocs_with_ignored_newlines.txt
- method_calls.txt
- methods.txt
- multi_write.txt
- not.txt
- patterns.txt
- regex.txt
- seattlerb/and_multi.txt
- seattlerb/heredoc__backslash_dos_format.txt
- seattlerb/heredoc_bad_hex_escape.txt
- seattlerb/heredoc_bad_oct_escape.txt
- seattlerb/heredoc_with_extra_carriage_horrible_mix.txt
- seattlerb/heredoc_with_extra_carriage_returns_windows.txt
- seattlerb/heredoc_with_only_carriage_returns_windows.txt
- seattlerb/heredoc_with_only_carriage_returns.txt
- spanning_heredoc_newlines.txt
- spanning_heredoc.txt
- tilde_heredocs.txt
- unparser/corpus/literal/literal.txt
- while.txt
- whitequark/class_definition_in_while_cond.txt
- whitequark/cond_eflipflop.txt
- whitequark/cond_iflipflop.txt
- whitequark/cond_match_current_line.txt
- whitequark/dedenting_heredoc.txt
- 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
- whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
- whitequark/pattern_matching_single_line.txt
- whitequark/ruby_bug_11989.txt
- whitequark/slash_newline_in_heredocs.txt
- ]
-
- Dir["**/*.txt", base: base].each do |name|
- next if failures.include?(name)
-
- define_method("test_#{name}") do
- begin
- # Parsing with ruby parser tends to be noisy with warnings, so we're
- # turning those off.
- previous_verbose, $VERBOSE = $VERBOSE, nil
- assert_parse_file(base, name, todos.include?(name))
- ensure
- $VERBOSE = previous_verbose
- end
- end
- end
-
- private
-
- def assert_parse_file(base, name, allowed_failure)
- filepath = File.join(base, name)
- expected = ::RubyParser.new.parse(File.read(filepath), filepath)
- actual = Prism::Translation::RubyParser.parse_file(filepath)
-
- if !allowed_failure
- assert_equal_nodes expected, actual
- elsif expected == actual
- puts "#{name} now passes"
- end
- end
-
- def assert_equal_nodes(left, right)
- return if left == right
-
- if left.is_a?(Sexp) && right.is_a?(Sexp)
- if left.line != right.line
- assert_equal "(#{left.inspect} line=#{left.line})", "(#{right.inspect} line=#{right.line})"
- elsif left.file != right.file
- assert_equal "(#{left.inspect} file=#{left.file})", "(#{right.inspect} file=#{right.file})"
- elsif left.length != right.length
- assert_equal "(#{left.inspect} length=#{left.length})", "(#{right.inspect} length=#{right.length})"
- else
- left.zip(right).each { |l, r| assert_equal_nodes(l, r) }
- end
- else
- assert_equal left, right
- end
- end
- end
-end
diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt
index e8e53aacb9..90a4d8f3bb 100644
--- a/test/prism/snapshots/arrays.txt
+++ b/test/prism/snapshots/arrays.txt
@@ -960,8 +960,8 @@
│ ├── arguments: ∅
│ ├── closing_loc: (84,4)-(84,5) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (84,6)-(84,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (84,6)-(84,8) = "+="
│ └── value:
│ @ IntegerNode (location: (84,9)-(84,10))
│ ├── flags: decimal
@@ -1040,8 +1040,8 @@
│ ├── arguments: ∅
│ ├── closing_loc: (90,8)-(90,9) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (90,10)-(90,12) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (90,10)-(90,12) = "+="
│ └── value:
│ @ IntegerNode (location: (90,13)-(90,14))
│ ├── flags: decimal
@@ -1143,8 +1143,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (96,7)-(96,8) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (96,9)-(96,11) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (96,9)-(96,11) = "+="
│ └── value:
│ @ IntegerNode (location: (96,12)-(96,13))
│ ├── flags: decimal
@@ -1262,8 +1262,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (102,11)-(102,12) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (102,13)-(102,15) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (102,13)-(102,15) = "+="
│ └── value:
│ @ IntegerNode (location: (102,16)-(102,17))
│ ├── flags: decimal
@@ -1633,8 +1633,8 @@
│ │ │ └── expression: ∅
│ │ ├── closing_loc: (116,13)-(116,14) = "]"
│ │ ├── block: ∅
- │ │ ├── operator: :+
- │ │ ├── operator_loc: (116,15)-(116,17) = "+="
+ │ │ ├── binary_operator: :+
+ │ │ ├── binary_operator_loc: (116,15)-(116,17) = "+="
│ │ └── value:
│ │ @ IntegerNode (location: (116,18)-(116,19))
│ │ ├── flags: decimal
diff --git a/test/prism/snapshots/blocks.txt b/test/prism/snapshots/blocks.txt
index 0b1ec52e38..1c996ebd09 100644
--- a/test/prism/snapshots/blocks.txt
+++ b/test/prism/snapshots/blocks.txt
@@ -158,13 +158,13 @@
│ │ └── body: (length: 1)
│ │ └── @ LocalVariableOperatorWriteNode (location: (7,24)-(7,33))
│ │ ├── name_loc: (7,24)-(7,28) = "memo"
- │ │ ├── operator_loc: (7,29)-(7,31) = "+="
+ │ │ ├── binary_operator_loc: (7,29)-(7,31) = "+="
│ │ ├── value:
│ │ │ @ LocalVariableReadNode (location: (7,32)-(7,33))
│ │ │ ├── name: :x
│ │ │ └── depth: 0
│ │ ├── name: :memo
- │ │ ├── operator: :+
+ │ │ ├── binary_operator: :+
│ │ └── depth: 0
│ ├── opening_loc: (7,12)-(7,13) = "{"
│ └── closing_loc: (7,34)-(7,35) = "}"
diff --git a/test/prism/snapshots/boolean_operators.txt b/test/prism/snapshots/boolean_operators.txt
index ace8047e18..3bf33430c9 100644
--- a/test/prism/snapshots/boolean_operators.txt
+++ b/test/prism/snapshots/boolean_operators.txt
@@ -21,7 +21,7 @@
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,6))
│ ├── name_loc: (3,0)-(3,1) = "a"
- │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── binary_operator_loc: (3,2)-(3,4) = "+="
│ ├── value:
│ │ @ CallNode (location: (3,5)-(3,6))
│ │ ├── flags: variable_call, ignore_visibility
@@ -34,7 +34,7 @@
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
│ ├── name: :a
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
└── @ LocalVariableOrWriteNode (location: (5,0)-(5,7))
├── name_loc: (5,0)-(5,1) = "a"
diff --git a/test/prism/snapshots/break.txt b/test/prism/snapshots/break.txt
index c15a9e4675..7d5bf5e69a 100644
--- a/test/prism/snapshots/break.txt
+++ b/test/prism/snapshots/break.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (1,0)-(25,23))
+@ ProgramNode (location: (1,0)-(29,21))
├── locals: []
└── statements:
- @ StatementsNode (location: (1,0)-(25,23))
- └── body: (length: 11)
+ @ StatementsNode (location: (1,0)-(29,21))
+ └── body: (length: 13)
├── @ CallNode (location: (1,0)-(1,13))
│ ├── flags: ignore_visibility
│ ├── receiver: ∅
@@ -346,56 +346,102 @@
│ │ └── value: 42
│ ├── closing_loc: ∅
│ └── block: ∅
- └── @ CallNode (location: (25,0)-(25,23))
+ ├── @ CallNode (location: (25,0)-(25,23))
+ │ ├── flags: ∅
+ │ ├── receiver:
+ │ │ @ CallNode (location: (25,0)-(25,17))
+ │ │ ├── flags: ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :foo
+ │ │ ├── message_loc: (25,0)-(25,3) = "foo"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block:
+ │ │ @ BlockNode (location: (25,4)-(25,17))
+ │ │ ├── locals: [:a]
+ │ │ ├── parameters:
+ │ │ │ @ BlockParametersNode (location: (25,6)-(25,9))
+ │ │ │ ├── parameters:
+ │ │ │ │ @ ParametersNode (location: (25,7)-(25,8))
+ │ │ │ │ ├── requireds: (length: 1)
+ │ │ │ │ │ └── @ RequiredParameterNode (location: (25,7)-(25,8))
+ │ │ │ │ │ ├── flags: ∅
+ │ │ │ │ │ └── name: :a
+ │ │ │ │ ├── optionals: (length: 0)
+ │ │ │ │ ├── rest: ∅
+ │ │ │ │ ├── posts: (length: 0)
+ │ │ │ │ ├── keywords: (length: 0)
+ │ │ │ │ ├── keyword_rest: ∅
+ │ │ │ │ └── block: ∅
+ │ │ │ ├── locals: (length: 0)
+ │ │ │ ├── opening_loc: (25,6)-(25,7) = "|"
+ │ │ │ └── closing_loc: (25,8)-(25,9) = "|"
+ │ │ ├── body:
+ │ │ │ @ StatementsNode (location: (25,10)-(25,15))
+ │ │ │ └── body: (length: 1)
+ │ │ │ └── @ BreakNode (location: (25,10)-(25,15))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (25,10)-(25,15) = "break"
+ │ │ ├── opening_loc: (25,4)-(25,5) = "{"
+ │ │ └── closing_loc: (25,16)-(25,17) = "}"
+ │ ├── call_operator_loc: ∅
+ │ ├── name: :==
+ │ ├── message_loc: (25,18)-(25,20) = "=="
+ │ ├── opening_loc: ∅
+ │ ├── arguments:
+ │ │ @ ArgumentsNode (location: (25,21)-(25,23))
+ │ │ ├── flags: ∅
+ │ │ └── arguments: (length: 1)
+ │ │ └── @ IntegerNode (location: (25,21)-(25,23))
+ │ │ ├── flags: decimal
+ │ │ └── value: 42
+ │ ├── closing_loc: ∅
+ │ └── block: ∅
+ ├── @ WhileNode (location: (27,0)-(27,21))
+ │ ├── flags: ∅
+ │ ├── keyword_loc: (27,0)-(27,5) = "while"
+ │ ├── closing_loc: (27,18)-(27,21) = "end"
+ │ ├── predicate:
+ │ │ @ AndNode (location: (27,6)-(27,16))
+ │ │ ├── left:
+ │ │ │ @ CallNode (location: (27,6)-(27,7))
+ │ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ │ ├── receiver: ∅
+ │ │ │ ├── call_operator_loc: ∅
+ │ │ │ ├── name: :_
+ │ │ │ ├── message_loc: (27,6)-(27,7) = "_"
+ │ │ │ ├── opening_loc: ∅
+ │ │ │ ├── arguments: ∅
+ │ │ │ ├── closing_loc: ∅
+ │ │ │ └── block: ∅
+ │ │ ├── right:
+ │ │ │ @ BreakNode (location: (27,11)-(27,16))
+ │ │ │ ├── arguments: ∅
+ │ │ │ └── keyword_loc: (27,11)-(27,16) = "break"
+ │ │ └── operator_loc: (27,8)-(27,10) = "&&"
+ │ └── statements: ∅
+ └── @ UntilNode (location: (29,0)-(29,21))
├── flags: ∅
- ├── receiver:
- │ @ CallNode (location: (25,0)-(25,17))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :foo
- │ ├── message_loc: (25,0)-(25,3) = "foo"
- │ ├── opening_loc: ∅
- │ ├── arguments: ∅
- │ ├── closing_loc: ∅
- │ └── block:
- │ @ BlockNode (location: (25,4)-(25,17))
- │ ├── locals: [:a]
- │ ├── parameters:
- │ │ @ BlockParametersNode (location: (25,6)-(25,9))
- │ │ ├── parameters:
- │ │ │ @ ParametersNode (location: (25,7)-(25,8))
- │ │ │ ├── requireds: (length: 1)
- │ │ │ │ └── @ RequiredParameterNode (location: (25,7)-(25,8))
- │ │ │ │ ├── flags: ∅
- │ │ │ │ └── name: :a
- │ │ │ ├── optionals: (length: 0)
- │ │ │ ├── rest: ∅
- │ │ │ ├── posts: (length: 0)
- │ │ │ ├── keywords: (length: 0)
- │ │ │ ├── keyword_rest: ∅
- │ │ │ └── block: ∅
- │ │ ├── locals: (length: 0)
- │ │ ├── opening_loc: (25,6)-(25,7) = "|"
- │ │ └── closing_loc: (25,8)-(25,9) = "|"
- │ ├── body:
- │ │ @ StatementsNode (location: (25,10)-(25,15))
- │ │ └── body: (length: 1)
- │ │ └── @ BreakNode (location: (25,10)-(25,15))
- │ │ ├── arguments: ∅
- │ │ └── keyword_loc: (25,10)-(25,15) = "break"
- │ ├── opening_loc: (25,4)-(25,5) = "{"
- │ └── closing_loc: (25,16)-(25,17) = "}"
- ├── call_operator_loc: ∅
- ├── name: :==
- ├── message_loc: (25,18)-(25,20) = "=="
- ├── opening_loc: ∅
- ├── arguments:
- │ @ ArgumentsNode (location: (25,21)-(25,23))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ IntegerNode (location: (25,21)-(25,23))
- │ ├── flags: decimal
- │ └── value: 42
- ├── closing_loc: ∅
- └── block: ∅
+ ├── keyword_loc: (29,0)-(29,5) = "until"
+ ├── closing_loc: (29,18)-(29,21) = "end"
+ ├── predicate:
+ │ @ AndNode (location: (29,6)-(29,16))
+ │ ├── left:
+ │ │ @ CallNode (location: (29,6)-(29,7))
+ │ │ ├── flags: variable_call, ignore_visibility
+ │ │ ├── receiver: ∅
+ │ │ ├── call_operator_loc: ∅
+ │ │ ├── name: :_
+ │ │ ├── message_loc: (29,6)-(29,7) = "_"
+ │ │ ├── opening_loc: ∅
+ │ │ ├── arguments: ∅
+ │ │ ├── closing_loc: ∅
+ │ │ └── block: ∅
+ │ ├── right:
+ │ │ @ BreakNode (location: (29,11)-(29,16))
+ │ │ ├── arguments: ∅
+ │ │ └── keyword_loc: (29,11)-(29,16) = "break"
+ │ └── operator_loc: (29,8)-(29,10) = "&&"
+ └── statements: ∅
diff --git a/test/prism/snapshots/constants.txt b/test/prism/snapshots/constants.txt
index 59e234148a..1251833663 100644
--- a/test/prism/snapshots/constants.txt
+++ b/test/prism/snapshots/constants.txt
@@ -7,24 +7,21 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (1,0)-(1,1))
│ │ └── name: :A
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,3)-(1,4))
- │ │ └── name: :B
- │ └── delimiter_loc: (1,1)-(1,3) = "::"
+ │ ├── name: :B
+ │ ├── delimiter_loc: (1,1)-(1,3) = "::"
+ │ └── name_loc: (1,3)-(1,4) = "B"
├── @ ConstantPathNode (location: (3,0)-(3,7))
│ ├── parent:
│ │ @ ConstantPathNode (location: (3,0)-(3,4))
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (3,0)-(3,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (3,3)-(3,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (3,1)-(3,3) = "::"
- │ ├── child:
- │ │ @ ConstantReadNode (location: (3,6)-(3,7))
- │ │ └── name: :C
- │ └── delimiter_loc: (3,4)-(3,6) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (3,1)-(3,3) = "::"
+ │ │ └── name_loc: (3,3)-(3,4) = "B"
+ │ ├── name: :C
+ │ ├── delimiter_loc: (3,4)-(3,6) = "::"
+ │ └── name_loc: (3,6)-(3,7) = "C"
├── @ ConstantPathNode (location: (5,0)-(5,4))
│ ├── parent:
│ │ @ CallNode (location: (5,0)-(5,1))
@@ -37,20 +34,18 @@
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (5,3)-(5,4))
- │ │ └── name: :B
- │ └── delimiter_loc: (5,1)-(5,3) = "::"
+ │ ├── name: :B
+ │ ├── delimiter_loc: (5,1)-(5,3) = "::"
+ │ └── name_loc: (5,3)-(5,4) = "B"
├── @ ConstantPathWriteNode (location: (7,0)-(7,8))
│ ├── target:
│ │ @ ConstantPathNode (location: (7,0)-(7,4))
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (7,0)-(7,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (7,3)-(7,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (7,1)-(7,3) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (7,1)-(7,3) = "::"
+ │ │ └── name_loc: (7,3)-(7,4) = "B"
│ ├── operator_loc: (7,5)-(7,6) = "="
│ └── value:
│ @ IntegerNode (location: (7,7)-(7,8))
@@ -117,7 +112,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (17,4)-(17,9))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (17,4)-(17,9))
│ │ ├── flags: ∅
@@ -199,7 +194,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (23,9)-(23,14))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (23,9)-(23,14))
│ │ ├── flags: ∅
@@ -249,10 +244,9 @@
│ ├── receiver:
│ │ @ ConstantPathNode (location: (27,0)-(27,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (27,2)-(27,3))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (27,0)-(27,2) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (27,0)-(27,2) = "::"
+ │ │ └── name_loc: (27,2)-(27,3) = "A"
│ ├── call_operator_loc: (27,3)-(27,5) = "::"
│ ├── name: :foo
│ ├── message_loc: (27,5)-(27,8) = "foo"
@@ -264,10 +258,9 @@
│ ├── target:
│ │ @ ConstantPathNode (location: (29,0)-(29,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (29,2)-(29,3))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (29,0)-(29,2) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (29,0)-(29,2) = "::"
+ │ │ └── name_loc: (29,2)-(29,3) = "A"
│ ├── operator_loc: (29,4)-(29,5) = "="
│ └── value:
│ @ IntegerNode (location: (29,6)-(29,7))
@@ -279,14 +272,12 @@
│ │ ├── parent:
│ │ │ @ ConstantPathNode (location: (31,0)-(31,3))
│ │ │ ├── parent: ∅
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (31,2)-(31,3))
- │ │ │ │ └── name: :A
- │ │ │ └── delimiter_loc: (31,0)-(31,2) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (31,5)-(31,6))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (31,3)-(31,5) = "::"
+ │ │ │ ├── name: :A
+ │ │ │ ├── delimiter_loc: (31,0)-(31,2) = "::"
+ │ │ │ └── name_loc: (31,2)-(31,3) = "A"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (31,3)-(31,5) = "::"
+ │ │ └── name_loc: (31,5)-(31,6) = "B"
│ ├── operator_loc: (31,7)-(31,8) = "="
│ └── value:
│ @ IntegerNode (location: (31,9)-(31,10))
@@ -296,20 +287,17 @@
│ ├── parent:
│ │ @ ConstantPathNode (location: (33,0)-(33,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (33,2)-(33,3))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (33,0)-(33,2) = "::"
- │ ├── child:
- │ │ @ ConstantReadNode (location: (33,5)-(33,6))
- │ │ └── name: :B
- │ └── delimiter_loc: (33,3)-(33,5) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (33,0)-(33,2) = "::"
+ │ │ └── name_loc: (33,2)-(33,3) = "A"
+ │ ├── name: :B
+ │ ├── delimiter_loc: (33,3)-(33,5) = "::"
+ │ └── name_loc: (33,5)-(33,6) = "B"
├── @ ConstantPathNode (location: (35,0)-(35,3))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (35,2)-(35,3))
- │ │ └── name: :A
- │ └── delimiter_loc: (35,0)-(35,2) = "::"
+ │ ├── name: :A
+ │ ├── delimiter_loc: (35,0)-(35,2) = "::"
+ │ └── name_loc: (35,2)-(35,3) = "A"
├── @ CallNode (location: (37,0)-(37,8))
│ ├── flags: ∅
│ ├── receiver:
@@ -329,10 +317,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (39,0)-(39,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (39,3)-(39,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (39,1)-(39,3) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (39,1)-(39,3) = "::"
+ │ │ └── name_loc: (39,3)-(39,4) = "B"
│ ├── call_operator_loc: (39,4)-(39,6) = "::"
│ ├── name: :true
│ ├── message_loc: (39,6)-(39,10) = "true"
@@ -488,10 +475,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (65,0)-(65,1))
│ │ └── name: :A
- │ ├── child:
- │ │ @ ConstantReadNode (location: (67,0)-(67,1))
- │ │ └── name: :C
- │ └── delimiter_loc: (65,1)-(65,3) = "::"
+ │ ├── name: :C
+ │ ├── delimiter_loc: (65,1)-(65,3) = "::"
+ │ └── name_loc: (67,0)-(67,1) = "C"
├── @ CallNode (location: (69,0)-(69,8))
│ ├── flags: ∅
│ ├── receiver:
@@ -532,10 +518,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (75,0)-(75,1))
│ │ └── name: :A
- │ ├── child:
- │ │ @ ConstantReadNode (location: (75,3)-(75,8))
- │ │ └── name: :BEGIN
- │ └── delimiter_loc: (75,1)-(75,3) = "::"
+ │ ├── name: :BEGIN
+ │ ├── delimiter_loc: (75,1)-(75,3) = "::"
+ │ └── name_loc: (75,3)-(75,8) = "BEGIN"
├── @ CallNode (location: (77,0)-(77,8))
│ ├── flags: ∅
│ ├── receiver:
@@ -636,10 +621,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (93,0)-(93,1))
│ │ └── name: :A
- │ ├── child:
- │ │ @ ConstantReadNode (location: (93,3)-(93,6))
- │ │ └── name: :END
- │ └── delimiter_loc: (93,1)-(93,3) = "::"
+ │ ├── name: :END
+ │ ├── delimiter_loc: (93,1)-(93,3) = "::"
+ │ └── name_loc: (93,3)-(93,6) = "END"
├── @ CallNode (location: (95,0)-(95,9))
│ ├── flags: ∅
│ ├── receiver:
@@ -1207,10 +1191,9 @@
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (180,0)-(180,1))
- │ │ └── name: :C
- │ └── delimiter_loc: (179,4)-(179,6) = "::"
+ │ ├── name: :C
+ │ ├── delimiter_loc: (179,4)-(179,6) = "::"
+ │ └── name_loc: (180,0)-(180,1) = "C"
└── @ RangeNode (location: (182,0)-(184,10))
├── flags: ∅
├── left:
diff --git a/test/prism/snapshots/defined.txt b/test/prism/snapshots/defined.txt
index 53a5081811..c60173ff37 100644
--- a/test/prism/snapshots/defined.txt
+++ b/test/prism/snapshots/defined.txt
@@ -28,13 +28,13 @@
│ ├── value:
│ │ @ LocalVariableOperatorWriteNode (location: (3,9)-(3,15))
│ │ ├── name_loc: (3,9)-(3,10) = "x"
- │ │ ├── operator_loc: (3,11)-(3,13) = "%="
+ │ │ ├── binary_operator_loc: (3,11)-(3,13) = "%="
│ │ ├── value:
│ │ │ @ IntegerNode (location: (3,14)-(3,15))
│ │ │ ├── flags: decimal
│ │ │ └── value: 2
│ │ ├── name: :x
- │ │ ├── operator: :%
+ │ │ ├── binary_operator: :%
│ │ └── depth: 0
│ ├── rparen_loc: (3,15)-(3,16) = ")"
│ └── keyword_loc: (3,0)-(3,8) = "defined?"
diff --git a/test/prism/snapshots/if.txt b/test/prism/snapshots/if.txt
index 31c33d368f..4114d22722 100644
--- a/test/prism/snapshots/if.txt
+++ b/test/prism/snapshots/if.txt
@@ -307,7 +307,7 @@
│ │ ├── opening_loc: ∅
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (25,4)-(25,6))
- │ │ │ ├── flags: ∅
+ │ │ │ ├── flags: contains_keywords
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ KeywordHashNode (location: (25,4)-(25,6))
│ │ │ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/method_calls.txt b/test/prism/snapshots/method_calls.txt
index de9ba71ae0..6082b567f7 100644
--- a/test/prism/snapshots/method_calls.txt
+++ b/test/prism/snapshots/method_calls.txt
@@ -308,7 +308,7 @@
│ ├── opening_loc: (27,1)-(27,2) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (27,2)-(27,10))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (27,2)-(27,10))
│ │ ├── flags: ∅
@@ -779,7 +779,7 @@
│ ├── opening_loc: (60,3)-(60,4) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (60,4)-(60,32))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ SymbolNode (location: (60,4)-(60,6))
│ │ │ ├── flags: forced_us_ascii_encoding
@@ -912,7 +912,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (64,4)-(64,15))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ SymbolNode (location: (64,4)-(64,6))
│ │ │ ├── flags: forced_us_ascii_encoding
@@ -988,7 +988,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (66,3)-(66,17))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (66,3)-(66,17))
│ │ ├── flags: symbol_keys
@@ -1020,7 +1020,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (68,3)-(68,40))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (68,3)-(68,40))
│ │ ├── flags: ∅
@@ -1075,7 +1075,7 @@
│ ├── opening_loc: (70,2)-(70,3) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (70,3)-(70,40))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (70,3)-(70,40))
│ │ ├── flags: ∅
@@ -1178,7 +1178,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (74,3)-(74,20))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (74,3)-(74,20))
│ │ ├── flags: symbol_keys
@@ -1235,7 +1235,7 @@
│ ├── opening_loc: (80,3)-(80,4) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (81,0)-(82,5))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ SymbolNode (location: (81,0)-(81,2))
│ │ │ ├── flags: forced_us_ascii_encoding
@@ -1292,7 +1292,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (87,4)-(87,21))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (87,4)-(87,21))
│ │ ├── flags: symbol_keys
@@ -1339,7 +1339,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (89,10)-(89,21))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ IntegerNode (location: (89,10)-(89,11))
│ │ │ ├── flags: decimal
@@ -1443,10 +1443,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (97,0)-(97,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (97,3)-(97,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (97,1)-(97,3) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (97,1)-(97,3) = "::"
+ │ │ └── name_loc: (97,3)-(97,4) = "B"
│ ├── call_operator_loc: (97,4)-(97,6) = "::"
│ ├── name: :C
│ ├── message_loc: (97,6)-(97,7) = "C"
@@ -1470,10 +1469,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (99,0)-(99,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (99,3)-(99,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (99,1)-(99,3) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (99,1)-(99,3) = "::"
+ │ │ └── name_loc: (99,3)-(99,4) = "B"
│ ├── call_operator_loc: (99,4)-(99,6) = "::"
│ ├── name: :C
│ ├── message_loc: (99,6)-(99,7) = "C"
@@ -1497,10 +1495,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (101,0)-(101,1))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (101,3)-(101,4))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (101,1)-(101,3) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (101,1)-(101,3) = "::"
+ │ │ └── name_loc: (101,3)-(101,4) = "B"
│ ├── call_operator_loc: (101,4)-(101,6) = "::"
│ ├── name: :C
│ ├── message_loc: (101,6)-(101,7) = "C"
@@ -1532,7 +1529,7 @@
│ ├── opening_loc: (103,3)-(103,4) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (103,4)-(103,11))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (103,4)-(103,11))
│ │ ├── flags: symbol_keys
@@ -1561,7 +1558,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (105,4)-(105,28))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (105,4)-(105,28))
│ │ ├── flags: symbol_keys
@@ -1617,7 +1614,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (107,4)-(107,24))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (107,4)-(107,24))
│ │ ├── flags: symbol_keys
@@ -2417,7 +2414,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (156,5)-(156,19))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (156,5)-(156,19))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt
index 76c0361827..b38640399b 100644
--- a/test/prism/snapshots/methods.txt
+++ b/test/prism/snapshots/methods.txt
@@ -1296,7 +1296,7 @@
│ │ ├── opening_loc: ∅
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (139,11)-(139,30))
- │ │ │ ├── flags: contains_keyword_splat
+ │ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ │ └── arguments: (length: 1)
│ │ │ └── @ KeywordHashNode (location: (139,11)-(139,30))
│ │ │ ├── flags: ∅
diff --git a/test/prism/snapshots/modules.txt b/test/prism/snapshots/modules.txt
index 1a0eb6328a..de1ea8feeb 100644
--- a/test/prism/snapshots/modules.txt
+++ b/test/prism/snapshots/modules.txt
@@ -72,10 +72,9 @@
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (5,10)-(5,11))
- │ │ │ └── name: :M
- │ │ └── delimiter_loc: (5,8)-(5,10) = "::"
+ │ │ ├── name: :M
+ │ │ ├── delimiter_loc: (5,8)-(5,10) = "::"
+ │ │ └── name_loc: (5,10)-(5,11) = "M"
│ ├── body: ∅
│ ├── end_keyword_loc: (6,0)-(6,3) = "end"
│ └── name: :M
@@ -119,10 +118,9 @@
│ ├── constant_path:
│ │ @ ConstantPathNode (location: (11,7)-(11,10))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (11,9)-(11,10))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (11,7)-(11,9) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (11,7)-(11,9) = "::"
+ │ │ └── name_loc: (11,9)-(11,10) = "A"
│ ├── body: ∅
│ ├── end_keyword_loc: (12,0)-(12,3) = "end"
│ └── name: :A
@@ -144,10 +142,9 @@
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: (14,9)-(14,10) = "]"
│ │ │ └── block: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (14,12)-(14,13))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (14,10)-(14,12) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (14,10)-(14,12) = "::"
+ │ │ └── name_loc: (14,12)-(14,13) = "B"
│ ├── body: ∅
│ ├── end_keyword_loc: (15,0)-(15,3) = "end"
│ └── name: :B
@@ -175,10 +172,9 @@
│ │ │ └── value: 1
│ │ ├── closing_loc: (17,10)-(17,11) = "]"
│ │ └── block: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (17,13)-(17,14))
- │ │ └── name: :B
- │ └── delimiter_loc: (17,11)-(17,13) = "::"
+ │ ├── name: :B
+ │ ├── delimiter_loc: (17,11)-(17,13) = "::"
+ │ └── name_loc: (17,13)-(17,14) = "B"
├── body: ∅
├── end_keyword_loc: (18,0)-(18,3) = "end"
└── name: :B
diff --git a/test/prism/snapshots/numbers.txt b/test/prism/snapshots/numbers.txt
index 740f3f5a2a..58aea454fa 100644
--- a/test/prism/snapshots/numbers.txt
+++ b/test/prism/snapshots/numbers.txt
@@ -65,52 +65,48 @@
│ ├── flags: decimal
│ └── value: 1
├── @ RationalNode (location: (41,0)-(41,2))
- │ └── numeric:
- │ @ IntegerNode (location: (41,0)-(41,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ IntegerNode (location: (43,0)-(43,2))
│ ├── flags: decimal
│ └── value: -1
├── @ ImaginaryNode (location: (45,0)-(45,3))
│ └── numeric:
│ @ RationalNode (location: (45,0)-(45,2))
- │ └── numeric:
- │ @ IntegerNode (location: (45,0)-(45,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (47,0)-(47,4))
- │ └── numeric:
- │ @ FloatNode (location: (47,0)-(47,3))
- │ └── value: 1.2
+ │ ├── flags: decimal
+ │ ├── numerator: 6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (49,0)-(49,5))
│ └── numeric:
│ @ RationalNode (location: (49,0)-(49,4))
- │ └── numeric:
- │ @ FloatNode (location: (49,0)-(49,3))
- │ └── value: 1.2
+ │ ├── flags: decimal
+ │ ├── numerator: 6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (51,0)-(51,4))
│ └── numeric:
│ @ RationalNode (location: (51,0)-(51,3))
- │ └── numeric:
- │ @ IntegerNode (location: (51,0)-(51,2))
- │ ├── flags: decimal
- │ └── value: -1
+ │ ├── flags: decimal
+ │ ├── numerator: -1
+ │ └── denominator: 1
├── @ RationalNode (location: (53,0)-(53,5))
- │ └── numeric:
- │ @ FloatNode (location: (53,0)-(53,4))
- │ └── value: -1.2
+ │ ├── flags: decimal
+ │ ├── numerator: -6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (55,0)-(55,6))
│ └── numeric:
│ @ RationalNode (location: (55,0)-(55,5))
- │ └── numeric:
- │ @ FloatNode (location: (55,0)-(55,4))
- │ └── value: -1.2
+ │ ├── flags: decimal
+ │ ├── numerator: -6
+ │ └── denominator: 5
├── @ RationalNode (location: (57,0)-(57,4))
- │ └── numeric:
- │ @ IntegerNode (location: (57,0)-(57,3))
- │ ├── flags: octal
- │ └── value: 1
+ │ ├── flags: octal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ ImaginaryNode (location: (59,0)-(59,4))
│ └── numeric:
│ @ IntegerNode (location: (59,0)-(59,3))
@@ -119,15 +115,13 @@
├── @ ImaginaryNode (location: (61,0)-(61,5))
│ └── numeric:
│ @ RationalNode (location: (61,0)-(61,4))
- │ └── numeric:
- │ @ IntegerNode (location: (61,0)-(61,3))
- │ ├── flags: octal
- │ └── value: 1
+ │ ├── flags: octal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (63,0)-(63,4))
- │ └── numeric:
- │ @ IntegerNode (location: (63,0)-(63,3))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ ImaginaryNode (location: (65,0)-(65,4))
│ └── numeric:
│ @ IntegerNode (location: (65,0)-(65,3))
@@ -136,7 +130,6 @@
└── @ ImaginaryNode (location: (67,0)-(67,5))
└── numeric:
@ RationalNode (location: (67,0)-(67,4))
- └── numeric:
- @ IntegerNode (location: (67,0)-(67,3))
- ├── flags: binary
- └── value: 1
+ ├── flags: binary
+ ├── numerator: 1
+ └── denominator: 1
diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt
index 5662129dae..17aa23b4b9 100644
--- a/test/prism/snapshots/patterns.txt
+++ b/test/prism/snapshots/patterns.txt
@@ -86,10 +86,9 @@
│ │ └── block: ∅
│ ├── pattern:
│ │ @ RationalNode (location: (5,7)-(5,9))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (5,7)-(5,8))
- │ │ ├── flags: decimal
- │ │ └── value: 1
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ └── operator_loc: (5,4)-(5,6) = "=>"
├── @ MatchRequiredNode (location: (6,0)-(6,11))
│ ├── value:
@@ -598,16 +597,14 @@
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ RationalNode (location: (31,7)-(31,9))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (31,7)-(31,8))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ ├── right:
│ │ │ @ RationalNode (location: (31,13)-(31,15))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (31,13)-(31,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ └── operator_loc: (31,10)-(31,12) = ".."
│ └── operator_loc: (31,4)-(31,6) = "=>"
├── @ MatchRequiredNode (location: (32,0)-(32,19))
@@ -1454,14 +1451,12 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (64,7)-(64,10))
│ │ │ │ └── name: :Foo
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (64,12)-(64,15))
- │ │ │ │ └── name: :Bar
- │ │ │ └── delimiter_loc: (64,10)-(64,12) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (64,17)-(64,20))
- │ │ │ └── name: :Baz
- │ │ └── delimiter_loc: (64,15)-(64,17) = "::"
+ │ │ │ ├── name: :Bar
+ │ │ │ ├── delimiter_loc: (64,10)-(64,12) = "::"
+ │ │ │ └── name_loc: (64,12)-(64,15) = "Bar"
+ │ │ ├── name: :Baz
+ │ │ ├── delimiter_loc: (64,15)-(64,17) = "::"
+ │ │ └── name_loc: (64,17)-(64,20) = "Baz"
│ └── operator_loc: (64,4)-(64,6) = "=>"
├── @ MatchRequiredNode (location: (65,0)-(65,12))
│ ├── value:
@@ -1478,10 +1473,9 @@
│ ├── pattern:
│ │ @ ConstantPathNode (location: (65,7)-(65,12))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (65,9)-(65,12))
- │ │ │ └── name: :Foo
- │ │ └── delimiter_loc: (65,7)-(65,9) = "::"
+ │ │ ├── name: :Foo
+ │ │ ├── delimiter_loc: (65,7)-(65,9) = "::"
+ │ │ └── name_loc: (65,9)-(65,12) = "Foo"
│ └── operator_loc: (65,4)-(65,6) = "=>"
├── @ MatchRequiredNode (location: (66,0)-(66,22))
│ ├── value:
@@ -1502,18 +1496,15 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantPathNode (location: (66,7)-(66,12))
│ │ │ │ ├── parent: ∅
- │ │ │ │ ├── child:
- │ │ │ │ │ @ ConstantReadNode (location: (66,9)-(66,12))
- │ │ │ │ │ └── name: :Foo
- │ │ │ │ └── delimiter_loc: (66,7)-(66,9) = "::"
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (66,14)-(66,17))
- │ │ │ │ └── name: :Bar
- │ │ │ └── delimiter_loc: (66,12)-(66,14) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (66,19)-(66,22))
- │ │ │ └── name: :Baz
- │ │ └── delimiter_loc: (66,17)-(66,19) = "::"
+ │ │ │ │ ├── name: :Foo
+ │ │ │ │ ├── delimiter_loc: (66,7)-(66,9) = "::"
+ │ │ │ │ └── name_loc: (66,9)-(66,12) = "Foo"
+ │ │ │ ├── name: :Bar
+ │ │ │ ├── delimiter_loc: (66,12)-(66,14) = "::"
+ │ │ │ └── name_loc: (66,14)-(66,17) = "Bar"
+ │ │ ├── name: :Baz
+ │ │ ├── delimiter_loc: (66,17)-(66,19) = "::"
+ │ │ └── name_loc: (66,19)-(66,22) = "Baz"
│ └── operator_loc: (66,4)-(66,6) = "=>"
├── @ MatchRequiredNode (location: (68,0)-(68,12))
│ ├── value:
@@ -2467,10 +2458,9 @@
│ │ └── block: ∅
│ ├── pattern:
│ │ @ RationalNode (location: (108,7)-(108,9))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (108,7)-(108,8))
- │ │ ├── flags: decimal
- │ │ └── value: 1
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ └── operator_loc: (108,4)-(108,6) = "in"
├── @ MatchPredicateNode (location: (109,0)-(109,11))
│ ├── value:
@@ -3023,10 +3013,9 @@
│ │ └── @ InNode (location: (139,10)-(139,20))
│ │ ├── pattern:
│ │ │ @ RationalNode (location: (139,13)-(139,15))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (139,13)-(139,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ ├── statements: ∅
│ │ ├── in_loc: (139,10)-(139,12) = "in"
│ │ └── then_loc: (139,16)-(139,20) = "then"
@@ -3764,10 +3753,9 @@
│ │ │ │ @ StatementsNode (location: (166,13)-(166,15))
│ │ │ │ └── body: (length: 1)
│ │ │ │ └── @ RationalNode (location: (166,13)-(166,15))
- │ │ │ │ └── numeric:
- │ │ │ │ @ IntegerNode (location: (166,13)-(166,14))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ ├── numerator: 1
+ │ │ │ │ └── denominator: 1
│ │ │ ├── consequent: ∅
│ │ │ └── end_keyword_loc: ∅
│ │ ├── statements: ∅
diff --git a/test/prism/snapshots/rescue.txt b/test/prism/snapshots/rescue.txt
index 2bdbfdaff3..390b08ae0e 100644
--- a/test/prism/snapshots/rescue.txt
+++ b/test/prism/snapshots/rescue.txt
@@ -380,7 +380,7 @@
│ │ │ ├── opening_loc: ∅
│ │ │ ├── arguments:
│ │ │ │ @ ArgumentsNode (location: (29,4)-(29,6))
- │ │ │ │ ├── flags: ∅
+ │ │ │ │ ├── flags: contains_keywords
│ │ │ │ └── arguments: (length: 1)
│ │ │ │ └── @ KeywordHashNode (location: (29,4)-(29,6))
│ │ │ │ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/assoc_label.txt b/test/prism/snapshots/seattlerb/assoc_label.txt
index 923f5450f4..70490c0da4 100644
--- a/test/prism/snapshots/seattlerb/assoc_label.txt
+++ b/test/prism/snapshots/seattlerb/assoc_label.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,5))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,5))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/bug_249.txt b/test/prism/snapshots/seattlerb/bug_249.txt
index 569bea14c5..ad61501a07 100644
--- a/test/prism/snapshots/seattlerb/bug_249.txt
+++ b/test/prism/snapshots/seattlerb/bug_249.txt
@@ -12,7 +12,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (1,6)-(4,28))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ CallNode (location: (1,6)-(4,9))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args.txt b/test/prism/snapshots/seattlerb/bug_hash_args.txt
index 6f17e88714..e138db4d49 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,3)-(1,4) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,18))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ SymbolNode (location: (1,4)-(1,8))
│ │ ├── flags: forced_us_ascii_encoding
diff --git a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
index e7256b337b..fe2d7f73c9 100644
--- a/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/bug_hash_args_trailing_comma.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,3)-(1,4) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,18))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ SymbolNode (location: (1,4)-(1,8))
│ │ ├── flags: forced_us_ascii_encoding
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc.txt b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
index 27c19fd339..f489bc7f19 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,9))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ IntegerNode (location: (1,2)-(1,3))
│ │ ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
index 0193eb1dfc..5b191396de 100644
--- a/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_assoc_kwsplat.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,15))
- │ ├── flags: contains_keyword_splat
+ │ ├── flags: contains_keywords, contains_keyword_splat
│ └── arguments: (length: 2)
│ ├── @ IntegerNode (location: (1,2)-(1,3))
│ │ ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
index 91c7725525..f95b80cf7d 100644
--- a/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_arg_kwsplat.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,8))
- │ ├── flags: contains_keyword_splat
+ │ ├── flags: contains_keywords, contains_keyword_splat
│ └── arguments: (length: 2)
│ ├── @ CallNode (location: (1,2)-(1,3))
│ │ ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
index 2d6f81c818..8946206a3f 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_quoted.txt
@@ -12,7 +12,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,2)-(1,11))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,2)-(1,11))
│ │ ├── flags: ∅
@@ -55,7 +55,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (3,2)-(3,8))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (3,2)-(3,8))
│ │ ├── flags: symbol_keys
@@ -84,7 +84,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (5,2)-(5,8))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (5,2)-(5,8))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
index 312a1981a0..0ba5891cf6 100644
--- a/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_args_assoc_trailing_comma.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,9))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ IntegerNode (location: (1,2)-(1,3))
│ │ ├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/call_assoc.txt b/test/prism/snapshots/seattlerb/call_assoc.txt
index 438c256553..60784e6095 100644
--- a/test/prism/snapshots/seattlerb/call_assoc.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,6))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,6))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new.txt b/test/prism/snapshots/seattlerb/call_assoc_new.txt
index b4d7e0bf83..dc25fb2493 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,5))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,5))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
index 9587e2e074..b3d652e879 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_new_if_multiline.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(5,3))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(5,3))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
index 8d0b285172..b2012f0f75 100644
--- a/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/call_assoc_trailing_comma.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,6))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,6))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/call_kwsplat.txt b/test/prism/snapshots/seattlerb/call_kwsplat.txt
index 4199e97a44..e0620dc5f0 100644
--- a/test/prism/snapshots/seattlerb/call_kwsplat.txt
+++ b/test/prism/snapshots/seattlerb/call_kwsplat.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,1)-(1,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,5))
- │ ├── flags: contains_keyword_splat
+ │ ├── flags: contains_keywords, contains_keyword_splat
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,5))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/case_in_86.txt b/test/prism/snapshots/seattlerb/case_in_86.txt
index 5889137844..082aa74eca 100644
--- a/test/prism/snapshots/seattlerb/case_in_86.txt
+++ b/test/prism/snapshots/seattlerb/case_in_86.txt
@@ -30,10 +30,9 @@
│ │ ├── requireds: (length: 1)
│ │ │ └── @ ConstantPathNode (location: (2,3)-(2,13))
│ │ │ ├── parent: ∅
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (2,5)-(2,13))
- │ │ │ │ └── name: :NilClass
- │ │ │ └── delimiter_loc: (2,3)-(2,5) = "::"
+ │ │ │ ├── name: :NilClass
+ │ │ │ ├── delimiter_loc: (2,3)-(2,5) = "::"
+ │ │ │ └── name_loc: (2,5)-(2,13) = "NilClass"
│ │ ├── rest:
│ │ │ @ SplatNode (location: (2,15)-(2,16))
│ │ │ ├── operator_loc: (2,15)-(2,16) = "*"
diff --git a/test/prism/snapshots/seattlerb/case_in_86_2.txt b/test/prism/snapshots/seattlerb/case_in_86_2.txt
index 18ce70ae93..346264f907 100644
--- a/test/prism/snapshots/seattlerb/case_in_86_2.txt
+++ b/test/prism/snapshots/seattlerb/case_in_86_2.txt
@@ -35,10 +35,9 @@
│ │ ├── posts: (length: 1)
│ │ │ └── @ ConstantPathNode (location: (2,6)-(2,16))
│ │ │ ├── parent: ∅
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (2,8)-(2,16))
- │ │ │ │ └── name: :NilClass
- │ │ │ └── delimiter_loc: (2,6)-(2,8) = "::"
+ │ │ │ ├── name: :NilClass
+ │ │ │ ├── delimiter_loc: (2,6)-(2,8) = "::"
+ │ │ │ └── name_loc: (2,8)-(2,16) = "NilClass"
│ │ ├── opening_loc: ∅
│ │ └── closing_loc: ∅
│ ├── statements:
diff --git a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt
index c783af9ce5..d6fb80ef90 100644
--- a/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt
+++ b/test/prism/snapshots/seattlerb/case_in_array_pat_const2.txt
@@ -20,10 +20,9 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
│ │ │ │ └── name: :B
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7))
- │ │ │ │ └── name: :C
- │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ │ ├── name: :C
+ │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ │ └── name_loc: (2,6)-(2,7) = "C"
│ │ ├── requireds: (length: 1)
│ │ │ └── @ LocalVariableTargetNode (location: (2,8)-(2,9))
│ │ │ ├── name: :d
diff --git a/test/prism/snapshots/seattlerb/case_in_multiple.txt b/test/prism/snapshots/seattlerb/case_in_multiple.txt
index d8597c4bfa..eba0084f96 100644
--- a/test/prism/snapshots/seattlerb/case_in_multiple.txt
+++ b/test/prism/snapshots/seattlerb/case_in_multiple.txt
@@ -18,10 +18,9 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (2,3)-(2,4))
│ │ │ │ └── name: :A
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (2,6)-(2,7))
- │ │ │ │ └── name: :B
- │ │ │ └── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ │ ├── name: :B
+ │ │ │ ├── delimiter_loc: (2,4)-(2,6) = "::"
+ │ │ │ └── name_loc: (2,6)-(2,7) = "B"
│ │ ├── statements:
│ │ │ @ StatementsNode (location: (3,2)-(3,4))
│ │ │ └── body: (length: 1)
@@ -39,10 +38,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (4,3)-(4,4))
│ │ │ └── name: :D
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (4,6)-(4,7))
- │ │ │ └── name: :E
- │ │ └── delimiter_loc: (4,4)-(4,6) = "::"
+ │ │ ├── name: :E
+ │ │ ├── delimiter_loc: (4,4)-(4,6) = "::"
+ │ │ └── name_loc: (4,6)-(4,7) = "E"
│ ├── statements:
│ │ @ StatementsNode (location: (5,2)-(5,4))
│ │ └── body: (length: 1)
diff --git a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt
index b018ac48d4..e09eed7d2f 100644
--- a/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt
+++ b/test/prism/snapshots/seattlerb/const_2_op_asgn_or2.txt
@@ -9,14 +9,12 @@
│ ├── parent:
│ │ @ ConstantPathNode (location: (1,0)-(1,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ │ └── name: :X
- │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,5)-(1,6))
- │ │ └── name: :Y
- │ └── delimiter_loc: (1,3)-(1,5) = "::"
+ │ │ ├── name: :X
+ │ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ └── name_loc: (1,2)-(1,3) = "X"
+ │ ├── name: :Y
+ │ ├── delimiter_loc: (1,3)-(1,5) = "::"
+ │ └── name_loc: (1,5)-(1,6) = "Y"
├── operator_loc: (1,7)-(1,10) = "||="
└── value:
@ IntegerNode (location: (1,11)-(1,12))
diff --git a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt
index 8d9d94931b..398af888a8 100644
--- a/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt
+++ b/test/prism/snapshots/seattlerb/const_3_op_asgn_or.txt
@@ -7,10 +7,9 @@
├── target:
│ @ ConstantPathNode (location: (1,0)-(1,3))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ └── name: :X
- │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ ├── name: :X
+ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ └── name_loc: (1,2)-(1,3) = "X"
├── operator_loc: (1,4)-(1,7) = "||="
└── value:
@ IntegerNode (location: (1,8)-(1,9))
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt
index b1d61b3752..f9792aebb3 100644
--- a/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_and1.txt
@@ -7,13 +7,12 @@
├── target:
│ @ ConstantPathNode (location: (1,0)-(1,3))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ └── name: :X
- │ └── delimiter_loc: (1,0)-(1,2) = "::"
- ├── operator_loc: (1,4)-(1,6) = "&="
+ │ ├── name: :X
+ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ └── name_loc: (1,2)-(1,3) = "X"
+ ├── binary_operator_loc: (1,4)-(1,6) = "&="
├── value:
│ @ IntegerNode (location: (1,7)-(1,8))
│ ├── flags: decimal
│ └── value: 1
- └── operator: :&
+ └── binary_operator: :&
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt
index 22f6682534..146455d327 100644
--- a/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_and2.txt
@@ -7,10 +7,9 @@
├── target:
│ @ ConstantPathNode (location: (1,0)-(1,3))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ └── name: :X
- │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ ├── name: :X
+ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ └── name_loc: (1,2)-(1,3) = "X"
├── operator_loc: (1,4)-(1,7) = "&&="
└── value:
@ IntegerNode (location: (1,8)-(1,9))
diff --git a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt
index 067e0fbb93..5e9dd39604 100644
--- a/test/prism/snapshots/seattlerb/const_op_asgn_or.txt
+++ b/test/prism/snapshots/seattlerb/const_op_asgn_or.txt
@@ -9,10 +9,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (1,0)-(1,1))
│ │ └── name: :X
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,3)-(1,4))
- │ │ └── name: :Y
- │ └── delimiter_loc: (1,1)-(1,3) = "::"
+ │ ├── name: :Y
+ │ ├── delimiter_loc: (1,1)-(1,3) = "::"
+ │ └── name_loc: (1,3)-(1,4) = "Y"
├── operator_loc: (1,5)-(1,8) = "||="
└── value:
@ IntegerNode (location: (1,9)-(1,10))
diff --git a/test/prism/snapshots/seattlerb/dasgn_icky2.txt b/test/prism/snapshots/seattlerb/dasgn_icky2.txt
deleted file mode 100644
index e118362f87..0000000000
--- a/test/prism/snapshots/seattlerb/dasgn_icky2.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-@ ProgramNode (location: (1,0)-(8,3))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(8,3))
- └── body: (length: 1)
- └── @ CallNode (location: (1,0)-(8,3))
- ├── flags: ignore_visibility
- ├── receiver: ∅
- ├── call_operator_loc: ∅
- ├── name: :a
- ├── message_loc: (1,0)-(1,1) = "a"
- ├── opening_loc: ∅
- ├── arguments: ∅
- ├── closing_loc: ∅
- └── block:
- @ BlockNode (location: (1,2)-(8,3))
- ├── locals: [:v]
- ├── parameters: ∅
- ├── body:
- │ @ StatementsNode (location: (2,2)-(7,5))
- │ └── body: (length: 2)
- │ ├── @ LocalVariableWriteNode (location: (2,2)-(2,9))
- │ │ ├── name: :v
- │ │ ├── depth: 0
- │ │ ├── name_loc: (2,2)-(2,3) = "v"
- │ │ ├── value:
- │ │ │ @ NilNode (location: (2,6)-(2,9))
- │ │ └── operator_loc: (2,4)-(2,5) = "="
- │ └── @ BeginNode (location: (3,2)-(7,5))
- │ ├── begin_keyword_loc: (3,2)-(3,7) = "begin"
- │ ├── statements:
- │ │ @ StatementsNode (location: (4,4)-(4,9))
- │ │ └── body: (length: 1)
- │ │ └── @ YieldNode (location: (4,4)-(4,9))
- │ │ ├── keyword_loc: (4,4)-(4,9) = "yield"
- │ │ ├── lparen_loc: ∅
- │ │ ├── arguments: ∅
- │ │ └── rparen_loc: ∅
- │ ├── rescue_clause:
- │ │ @ RescueNode (location: (5,2)-(6,9))
- │ │ ├── keyword_loc: (5,2)-(5,8) = "rescue"
- │ │ ├── exceptions: (length: 1)
- │ │ │ └── @ ConstantReadNode (location: (5,9)-(5,18))
- │ │ │ └── name: :Exception
- │ │ ├── operator_loc: (5,19)-(5,21) = "=>"
- │ │ ├── reference:
- │ │ │ @ LocalVariableTargetNode (location: (5,22)-(5,23))
- │ │ │ ├── name: :v
- │ │ │ └── depth: 0
- │ │ ├── statements:
- │ │ │ @ StatementsNode (location: (6,4)-(6,9))
- │ │ │ └── body: (length: 1)
- │ │ │ └── @ BreakNode (location: (6,4)-(6,9))
- │ │ │ ├── arguments: ∅
- │ │ │ └── keyword_loc: (6,4)-(6,9) = "break"
- │ │ └── consequent: ∅
- │ ├── else_clause: ∅
- │ ├── ensure_clause: ∅
- │ └── end_keyword_loc: (7,2)-(7,5) = "end"
- ├── opening_loc: (1,2)-(1,4) = "do"
- └── closing_loc: (8,0)-(8,3) = "end"
diff --git a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
index f420420fc3..2aadedd964 100644
--- a/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
+++ b/test/prism/snapshots/seattlerb/defn_kwarg_env.txt
@@ -33,7 +33,7 @@
│ ├── opening_loc: (1,30)-(1,31) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,31)-(1,40))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,31)-(1,40))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/difficult2_.txt b/test/prism/snapshots/seattlerb/difficult2_.txt
index a9b3736fe3..b53d4cad3f 100644
--- a/test/prism/snapshots/seattlerb/difficult2_.txt
+++ b/test/prism/snapshots/seattlerb/difficult2_.txt
@@ -52,7 +52,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (2,2)-(2,6))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (2,2)-(2,6))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/index_0_opasgn.txt b/test/prism/snapshots/seattlerb/index_0_opasgn.txt
index 239a549253..322eae9907 100644
--- a/test/prism/snapshots/seattlerb/index_0_opasgn.txt
+++ b/test/prism/snapshots/seattlerb/index_0_opasgn.txt
@@ -21,8 +21,8 @@
├── arguments: ∅
├── closing_loc: (1,2)-(1,3) = "]"
├── block: ∅
- ├── operator: :+
- ├── operator_loc: (1,4)-(1,6) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,4)-(1,6) = "+="
└── value:
@ CallNode (location: (1,7)-(1,8))
├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/masgn_colon2.txt b/test/prism/snapshots/seattlerb/masgn_colon2.txt
index 73ce8a71da..a0dfe72ffc 100644
--- a/test/prism/snapshots/seattlerb/masgn_colon2.txt
+++ b/test/prism/snapshots/seattlerb/masgn_colon2.txt
@@ -20,10 +20,9 @@
│ │ ├── arguments: ∅
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,6)-(1,7))
- │ │ └── name: :C
- │ └── delimiter_loc: (1,4)-(1,6) = "::"
+ │ ├── name: :C
+ │ ├── delimiter_loc: (1,4)-(1,6) = "::"
+ │ └── name_loc: (1,6)-(1,7) = "C"
├── rest: ∅
├── rights: (length: 0)
├── lparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/masgn_colon3.txt b/test/prism/snapshots/seattlerb/masgn_colon3.txt
index 0cf4f8626d..f28ed7ecee 100644
--- a/test/prism/snapshots/seattlerb/masgn_colon3.txt
+++ b/test/prism/snapshots/seattlerb/masgn_colon3.txt
@@ -7,16 +7,14 @@
├── lefts: (length: 2)
│ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ └── name_loc: (1,2)-(1,3) = "A"
│ └── @ ConstantPathTargetNode (location: (1,5)-(1,8))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,7)-(1,8))
- │ │ └── name: :B
- │ └── delimiter_loc: (1,5)-(1,7) = "::"
+ │ ├── name: :B
+ │ ├── delimiter_loc: (1,5)-(1,7) = "::"
+ │ └── name_loc: (1,7)-(1,8) = "B"
├── rest: ∅
├── rights: (length: 0)
├── lparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt
index 7a3e9affb5..edef23044a 100644
--- a/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt
+++ b/test/prism/snapshots/seattlerb/messy_op_asgn_lineno.txt
@@ -24,11 +24,10 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (1,3)-(1,4))
│ │ │ │ └── name: :B
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (1,6)-(1,7))
- │ │ │ │ └── name: :C
- │ │ │ └── delimiter_loc: (1,4)-(1,6) = "::"
- │ │ ├── operator_loc: (1,8)-(1,10) = "*="
+ │ │ │ ├── name: :C
+ │ │ │ ├── delimiter_loc: (1,4)-(1,6) = "::"
+ │ │ │ └── name_loc: (1,6)-(1,7) = "C"
+ │ │ ├── binary_operator_loc: (1,8)-(1,10) = "*="
│ │ ├── value:
│ │ │ @ CallNode (location: (1,11)-(1,14))
│ │ │ ├── flags: ignore_visibility
@@ -53,7 +52,7 @@
│ │ │ │ └── block: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
- │ │ └── operator: :*
+ │ │ └── binary_operator: :*
│ ├── opening_loc: (1,2)-(1,3) = "("
│ └── closing_loc: (1,14)-(1,15) = ")"
├── closing_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
index da10a474c3..1bb8bd0bc1 100644
--- a/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
+++ b/test/prism/snapshots/seattlerb/method_call_assoc_trailing_comma.txt
@@ -22,7 +22,7 @@
├── opening_loc: (1,3)-(1,4) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,8))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,4)-(1,8))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
index 79b0ef5d23..ff28a1798b 100644
--- a/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
+++ b/test/prism/snapshots/seattlerb/multiline_hash_declaration.txt
@@ -12,7 +12,7 @@
│ ├── opening_loc: (1,1)-(1,2) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,2)-(3,1))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,2)-(3,1))
│ │ ├── flags: symbol_keys
@@ -42,7 +42,7 @@
│ ├── opening_loc: (5,1)-(5,2) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (5,2)-(6,1))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (5,2)-(6,1))
│ │ ├── flags: symbol_keys
@@ -72,7 +72,7 @@
├── opening_loc: (8,1)-(8,2) = "("
├── arguments:
│ @ ArgumentsNode (location: (8,2)-(8,11))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (8,2)-(8,11))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
index 8e6df3e812..523ccde455 100644
--- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_const_command_call.txt
@@ -9,11 +9,10 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (1,0)-(1,1))
│ │ └── name: :A
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,3)-(1,4))
- │ │ └── name: :B
- │ └── delimiter_loc: (1,1)-(1,3) = "::"
- ├── operator_loc: (1,5)-(1,7) = "*="
+ │ ├── name: :B
+ │ ├── delimiter_loc: (1,1)-(1,3) = "::"
+ │ └── name_loc: (1,3)-(1,4) = "B"
+ ├── binary_operator_loc: (1,5)-(1,7) = "*="
├── value:
│ @ CallNode (location: (1,8)-(1,11))
│ ├── flags: ignore_visibility
@@ -38,4 +37,4 @@
│ │ └── block: ∅
│ ├── closing_loc: ∅
│ └── block: ∅
- └── operator: :*
+ └── binary_operator: :*
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
index 0daadcf6ff..b9d00edc30 100644
--- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier1.txt
@@ -12,8 +12,8 @@
├── message_loc: (1,3)-(1,4) = "b"
├── read_name: :b
├── write_name: :b=
- ├── operator: :+
- ├── operator_loc: (1,5)-(1,7) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,5)-(1,7) = "+="
└── value:
@ IntegerNode (location: (1,8)-(1,9))
├── flags: decimal
diff --git a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
index ea8603165b..c12ea3983c 100644
--- a/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
+++ b/test/prism/snapshots/seattlerb/op_asgn_primary_colon_identifier_command_call.txt
@@ -12,8 +12,8 @@
├── message_loc: (1,3)-(1,4) = "b"
├── read_name: :b
├── write_name: :b=
- ├── operator: :*
- ├── operator_loc: (1,5)-(1,7) = "*="
+ ├── binary_operator: :*
+ ├── binary_operator_loc: (1,5)-(1,7) = "*="
└── value:
@ CallNode (location: (1,8)-(1,11))
├── flags: ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt
index 8199eb2449..84eef70b25 100644
--- a/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt
+++ b/test/prism/snapshots/seattlerb/parse_line_defn_complex.txt
@@ -40,13 +40,13 @@
│ │ └── block: ∅
│ ├── @ LocalVariableOperatorWriteNode (location: (3,2)-(3,8))
│ │ ├── name_loc: (3,2)-(3,3) = "y"
- │ │ ├── operator_loc: (3,4)-(3,6) = "*="
+ │ │ ├── binary_operator_loc: (3,4)-(3,6) = "*="
│ │ ├── value:
│ │ │ @ IntegerNode (location: (3,7)-(3,8))
│ │ │ ├── flags: decimal
│ │ │ └── value: 2
│ │ ├── name: :y
- │ │ ├── operator: :*
+ │ │ ├── binary_operator: :*
│ │ └── depth: 0
│ └── @ ReturnNode (location: (4,2)-(4,10))
│ ├── flags: redundant
diff --git a/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt
index 5c2eb2da3c..d113f2af9d 100644
--- a/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt
+++ b/test/prism/snapshots/seattlerb/parse_line_op_asgn.txt
@@ -5,7 +5,7 @@
└── body: (length: 2)
├── @ LocalVariableOperatorWriteNode (location: (1,6)-(2,11))
│ ├── name_loc: (1,6)-(1,9) = "foo"
- │ ├── operator_loc: (1,10)-(1,12) = "+="
+ │ ├── binary_operator_loc: (1,10)-(1,12) = "+="
│ ├── value:
│ │ @ CallNode (location: (2,8)-(2,11))
│ │ ├── flags: variable_call, ignore_visibility
@@ -18,7 +18,7 @@
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
│ ├── name: :foo
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
└── @ CallNode (location: (3,6)-(3,9))
├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
index b767a5c17e..dc11e2ca3d 100644
--- a/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
+++ b/test/prism/snapshots/seattlerb/parse_opt_call_args_assocs_comma.txt
@@ -15,7 +15,7 @@
├── opening_loc: (1,1)-(1,2) = "["
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,6))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,2)-(1,6))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt b/test/prism/snapshots/seattlerb/pct_Q_backslash_nl.txt
index c8a990c672..31cc0941ee 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: " "
+ └── unescaped: " \\\n"
diff --git a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
index 64caf51bcb..bbc19d50ef 100644
--- a/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
+++ b/test/prism/snapshots/seattlerb/quoted_symbol_hash_arg.txt
@@ -12,7 +12,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (1,5)-(1,12))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,5)-(1,12))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/seattlerb/return_call_assocs.txt b/test/prism/snapshots/seattlerb/return_call_assocs.txt
index 66b3cb2f82..8948f7879b 100644
--- a/test/prism/snapshots/seattlerb/return_call_assocs.txt
+++ b/test/prism/snapshots/seattlerb/return_call_assocs.txt
@@ -8,7 +8,7 @@
│ ├── keyword_loc: (1,0)-(1,6) = "return"
│ └── arguments:
│ @ ArgumentsNode (location: (1,7)-(1,17))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ IntegerNode (location: (1,7)-(1,8))
│ │ ├── flags: decimal
@@ -34,7 +34,7 @@
│ ├── keyword_loc: (3,0)-(3,6) = "return"
│ └── arguments:
│ @ ArgumentsNode (location: (3,7)-(3,26))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ IntegerNode (location: (3,7)-(3,8))
│ │ ├── flags: decimal
@@ -84,7 +84,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (5,9)-(5,14))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (5,9)-(5,14))
│ │ ├── flags: symbol_keys
@@ -120,7 +120,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (7,9)-(7,12))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (7,9)-(7,12))
│ │ ├── flags: symbol_keys
@@ -156,7 +156,7 @@
│ ├── opening_loc: (9,8)-(9,9) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (9,9)-(9,12))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (9,9)-(9,12))
│ │ ├── flags: symbol_keys
@@ -192,7 +192,7 @@
├── opening_loc: (11,8)-(11,9) = "("
├── arguments:
│ @ ArgumentsNode (location: (11,9)-(11,13))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (11,9)-(11,13))
│ ├── flags: ∅
diff --git a/test/prism/snapshots/seattlerb/ruby21_numbers.txt b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
index 34a3452d1b..e7eec943f8 100644
--- a/test/prism/snapshots/seattlerb/ruby21_numbers.txt
+++ b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
@@ -12,16 +12,14 @@
│ │ ├── flags: decimal
│ │ └── value: 1
│ ├── @ RationalNode (location: (1,5)-(1,7))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (1,5)-(1,6))
- │ │ ├── flags: decimal
- │ │ └── value: 2
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 2
+ │ │ └── denominator: 1
│ └── @ ImaginaryNode (location: (1,9)-(1,12))
│ └── numeric:
│ @ RationalNode (location: (1,9)-(1,11))
- │ └── numeric:
- │ @ IntegerNode (location: (1,9)-(1,10))
- │ ├── flags: decimal
- │ └── value: 3
+ │ ├── flags: decimal
+ │ ├── numerator: 3
+ │ └── denominator: 1
├── opening_loc: (1,0)-(1,1) = "["
└── closing_loc: (1,12)-(1,13) = "]"
diff --git a/test/prism/snapshots/seattlerb/safe_op_asgn.txt b/test/prism/snapshots/seattlerb/safe_op_asgn.txt
index 7a9fd2b7f7..ebcedd6b5e 100644
--- a/test/prism/snapshots/seattlerb/safe_op_asgn.txt
+++ b/test/prism/snapshots/seattlerb/safe_op_asgn.txt
@@ -20,8 +20,8 @@
├── message_loc: (1,3)-(1,4) = "b"
├── read_name: :b
├── write_name: :b=
- ├── operator: :+
- ├── operator_loc: (1,5)-(1,7) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,5)-(1,7) = "+="
└── value:
@ CallNode (location: (1,8)-(1,11))
├── flags: ignore_visibility
diff --git a/test/prism/snapshots/seattlerb/yield_arg.txt b/test/prism/snapshots/seattlerb/yield_arg.txt
deleted file mode 100644
index 22e0c14f83..0000000000
--- a/test/prism/snapshots/seattlerb/yield_arg.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,8))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,8))
- └── body: (length: 1)
- └── @ YieldNode (location: (1,0)-(1,8))
- ├── keyword_loc: (1,0)-(1,5) = "yield"
- ├── lparen_loc: ∅
- ├── arguments:
- │ @ ArgumentsNode (location: (1,6)-(1,8))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ IntegerNode (location: (1,6)-(1,8))
- │ ├── flags: decimal
- │ └── value: 42
- └── rparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/yield_call_assocs.txt b/test/prism/snapshots/seattlerb/yield_call_assocs.txt
deleted file mode 100644
index c04273f5aa..0000000000
--- a/test/prism/snapshots/seattlerb/yield_call_assocs.txt
+++ /dev/null
@@ -1,224 +0,0 @@
-@ ProgramNode (location: (1,0)-(11,13))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(11,13))
- └── body: (length: 6)
- ├── @ YieldNode (location: (1,0)-(1,16))
- │ ├── keyword_loc: (1,0)-(1,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,6)-(1,16))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 2)
- │ │ ├── @ IntegerNode (location: (1,6)-(1,7))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── @ KeywordHashNode (location: (1,9)-(1,16))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (1,9)-(1,16))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (1,9)-(1,11))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (1,9)-(1,10) = ":"
- │ │ │ ├── value_loc: (1,10)-(1,11) = "z"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "z"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (1,15)-(1,16))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (1,12)-(1,14) = "=>"
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (3,0)-(3,25))
- │ ├── keyword_loc: (3,0)-(3,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,6)-(3,25))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 2)
- │ │ ├── @ IntegerNode (location: (3,6)-(3,7))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── @ KeywordHashNode (location: (3,9)-(3,25))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 2)
- │ │ ├── @ AssocNode (location: (3,9)-(3,16))
- │ │ │ ├── key:
- │ │ │ │ @ SymbolNode (location: (3,9)-(3,11))
- │ │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ │ ├── opening_loc: (3,9)-(3,10) = ":"
- │ │ │ │ ├── value_loc: (3,10)-(3,11) = "z"
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── unescaped: "z"
- │ │ │ ├── value:
- │ │ │ │ @ IntegerNode (location: (3,15)-(3,16))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── operator_loc: (3,12)-(3,14) = "=>"
- │ │ └── @ AssocNode (location: (3,18)-(3,25))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (3,18)-(3,20))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (3,18)-(3,19) = ":"
- │ │ │ ├── value_loc: (3,19)-(3,20) = "w"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "w"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (3,24)-(3,25))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 2
- │ │ └── operator_loc: (3,21)-(3,23) = "=>"
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (5,0)-(5,13))
- │ ├── keyword_loc: (5,0)-(5,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,6)-(5,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (5,6)-(5,13))
- │ │ ├── flags: ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :y
- │ │ ├── message_loc: (5,6)-(5,7) = "y"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments:
- │ │ │ @ ArgumentsNode (location: (5,8)-(5,13))
- │ │ │ ├── flags: ∅
- │ │ │ └── arguments: (length: 1)
- │ │ │ └── @ KeywordHashNode (location: (5,8)-(5,13))
- │ │ │ ├── flags: symbol_keys
- │ │ │ └── elements: (length: 1)
- │ │ │ └── @ AssocNode (location: (5,8)-(5,13))
- │ │ │ ├── key:
- │ │ │ │ @ SymbolNode (location: (5,8)-(5,10))
- │ │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ │ ├── opening_loc: (5,8)-(5,9) = ":"
- │ │ │ │ ├── value_loc: (5,9)-(5,10) = "z"
- │ │ │ │ ├── closing_loc: ∅
- │ │ │ │ └── unescaped: "z"
- │ │ │ ├── value:
- │ │ │ │ @ IntegerNode (location: (5,12)-(5,13))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── operator_loc: (5,10)-(5,12) = "=>"
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (7,0)-(7,11))
- │ ├── keyword_loc: (7,0)-(7,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (7,6)-(7,11))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (7,6)-(7,11))
- │ │ ├── flags: ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :y
- │ │ ├── message_loc: (7,6)-(7,7) = "y"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments:
- │ │ │ @ ArgumentsNode (location: (7,8)-(7,11))
- │ │ │ ├── flags: ∅
- │ │ │ └── arguments: (length: 1)
- │ │ │ └── @ KeywordHashNode (location: (7,8)-(7,11))
- │ │ │ ├── flags: symbol_keys
- │ │ │ └── elements: (length: 1)
- │ │ │ └── @ AssocNode (location: (7,8)-(7,11))
- │ │ │ ├── key:
- │ │ │ │ @ SymbolNode (location: (7,8)-(7,10))
- │ │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── value_loc: (7,8)-(7,9) = "z"
- │ │ │ │ ├── closing_loc: (7,9)-(7,10) = ":"
- │ │ │ │ └── unescaped: "z"
- │ │ │ ├── value:
- │ │ │ │ @ IntegerNode (location: (7,10)-(7,11))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── operator_loc: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (9,0)-(9,12))
- │ ├── keyword_loc: (9,0)-(9,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (9,6)-(9,12))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (9,6)-(9,12))
- │ │ ├── flags: ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :y
- │ │ ├── message_loc: (9,6)-(9,7) = "y"
- │ │ ├── opening_loc: (9,7)-(9,8) = "("
- │ │ ├── arguments:
- │ │ │ @ ArgumentsNode (location: (9,8)-(9,11))
- │ │ │ ├── flags: ∅
- │ │ │ └── arguments: (length: 1)
- │ │ │ └── @ KeywordHashNode (location: (9,8)-(9,11))
- │ │ │ ├── flags: symbol_keys
- │ │ │ └── elements: (length: 1)
- │ │ │ └── @ AssocNode (location: (9,8)-(9,11))
- │ │ │ ├── key:
- │ │ │ │ @ SymbolNode (location: (9,8)-(9,10))
- │ │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ │ ├── opening_loc: ∅
- │ │ │ │ ├── value_loc: (9,8)-(9,9) = "z"
- │ │ │ │ ├── closing_loc: (9,9)-(9,10) = ":"
- │ │ │ │ └── unescaped: "z"
- │ │ │ ├── value:
- │ │ │ │ @ IntegerNode (location: (9,10)-(9,11))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
- │ │ │ └── operator_loc: ∅
- │ │ ├── closing_loc: (9,11)-(9,12) = ")"
- │ │ └── block: ∅
- │ └── rparen_loc: ∅
- └── @ YieldNode (location: (11,0)-(11,13))
- ├── keyword_loc: (11,0)-(11,5) = "yield"
- ├── lparen_loc: ∅
- ├── arguments:
- │ @ ArgumentsNode (location: (11,6)-(11,13))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ CallNode (location: (11,6)-(11,13))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :y
- │ ├── message_loc: (11,6)-(11,7) = "y"
- │ ├── opening_loc: (11,7)-(11,8) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (11,8)-(11,12))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (11,8)-(11,12))
- │ │ ├── flags: ∅
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (11,8)-(11,12))
- │ │ ├── key:
- │ │ │ @ CallNode (location: (11,8)-(11,9))
- │ │ │ ├── flags: variable_call, ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :z
- │ │ │ ├── message_loc: (11,8)-(11,9) = "z"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block: ∅
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (11,11)-(11,12))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (11,9)-(11,11) = "=>"
- │ ├── closing_loc: (11,12)-(11,13) = ")"
- │ └── block: ∅
- └── rparen_loc: ∅
diff --git a/test/prism/snapshots/seattlerb/yield_empty_parens.txt b/test/prism/snapshots/seattlerb/yield_empty_parens.txt
deleted file mode 100644
index 5ecd89823f..0000000000
--- a/test/prism/snapshots/seattlerb/yield_empty_parens.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-@ ProgramNode (location: (1,0)-(1,7))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(1,7))
- └── body: (length: 1)
- └── @ YieldNode (location: (1,0)-(1,7))
- ├── keyword_loc: (1,0)-(1,5) = "yield"
- ├── lparen_loc: (1,5)-(1,6) = "("
- ├── arguments: ∅
- └── rparen_loc: (1,6)-(1,7) = ")"
diff --git a/test/prism/snapshots/symbols.txt b/test/prism/snapshots/symbols.txt
index dbd3a4d030..48ff0d634f 100644
--- a/test/prism/snapshots/symbols.txt
+++ b/test/prism/snapshots/symbols.txt
@@ -146,10 +146,9 @@
│ │ ├── @ FloatNode (location: (29,4)-(29,7))
│ │ │ └── value: 1.0
│ │ ├── @ RationalNode (location: (29,9)-(29,11))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (29,9)-(29,10))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ └── @ ImaginaryNode (location: (29,13)-(29,15))
│ │ └── numeric:
│ │ @ IntegerNode (location: (29,13)-(29,14))
diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
index 99c8daf34c..7d3cc389c6 100644
--- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt
@@ -469,18 +469,16 @@
│ ├── target:
│ │ @ ConstantPathNode (location: (18,0)-(18,5))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (18,2)-(18,5))
- │ │ │ └── name: :Foo
- │ │ └── delimiter_loc: (18,0)-(18,2) = "::"
+ │ │ ├── name: :Foo
+ │ │ ├── delimiter_loc: (18,0)-(18,2) = "::"
+ │ │ └── name_loc: (18,2)-(18,5) = "Foo"
│ ├── operator_loc: (18,6)-(18,7) = "="
│ └── value:
│ @ ConstantPathNode (location: (18,8)-(18,13))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (18,10)-(18,13))
- │ │ └── name: :Bar
- │ └── delimiter_loc: (18,8)-(18,10) = "::"
+ │ ├── name: :Bar
+ │ ├── delimiter_loc: (18,8)-(18,10) = "::"
+ │ └── name_loc: (18,10)-(18,13) = "Bar"
├── @ ClassVariableWriteNode (location: (19,0)-(19,7))
│ ├── name: :@@a
│ ├── name_loc: (19,0)-(19,3) = "@@a"
@@ -513,14 +511,12 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (22,0)-(22,4))
│ │ │ │ └── name: :Name
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (22,6)-(22,12))
- │ │ │ │ └── name: :Spaced
- │ │ │ └── delimiter_loc: (22,4)-(22,6) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (22,14)-(22,19))
- │ │ │ └── name: :CONST
- │ │ └── delimiter_loc: (22,12)-(22,14) = "::"
+ │ │ │ ├── name: :Spaced
+ │ │ │ ├── delimiter_loc: (22,4)-(22,6) = "::"
+ │ │ │ └── name_loc: (22,6)-(22,12) = "Spaced"
+ │ │ ├── name: :CONST
+ │ │ ├── delimiter_loc: (22,12)-(22,14) = "::"
+ │ │ └── name_loc: (22,14)-(22,19) = "CONST"
│ ├── operator_loc: (22,20)-(22,21) = "="
│ └── value:
│ @ IntegerNode (location: (22,22)-(22,23))
diff --git a/test/prism/snapshots/unparser/corpus/literal/class.txt b/test/prism/snapshots/unparser/corpus/literal/class.txt
index 34eb03edb3..5306888398 100644
--- a/test/prism/snapshots/unparser/corpus/literal/class.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/class.txt
@@ -68,10 +68,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (11,6)-(11,7))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (11,9)-(11,10))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (11,7)-(11,9) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (11,7)-(11,9) = "::"
+ │ │ └── name_loc: (11,9)-(11,10) = "B"
│ ├── inheritance_operator_loc: ∅
│ ├── superclass: ∅
│ ├── body: ∅
@@ -87,14 +86,12 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (14,6)-(14,7))
│ │ │ │ └── name: :A
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (14,9)-(14,10))
- │ │ │ │ └── name: :B
- │ │ │ └── delimiter_loc: (14,7)-(14,9) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (14,12)-(14,13))
- │ │ │ └── name: :C
- │ │ └── delimiter_loc: (14,10)-(14,12) = "::"
+ │ │ │ ├── name: :B
+ │ │ │ ├── delimiter_loc: (14,7)-(14,9) = "::"
+ │ │ │ └── name_loc: (14,9)-(14,10) = "B"
+ │ │ ├── name: :C
+ │ │ ├── delimiter_loc: (14,10)-(14,12) = "::"
+ │ │ └── name_loc: (14,12)-(14,13) = "C"
│ ├── inheritance_operator_loc: ∅
│ ├── superclass: ∅
│ ├── body: ∅
@@ -125,10 +122,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (20,10)-(20,11))
│ │ │ └── name: :B
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (20,13)-(20,14))
- │ │ │ └── name: :C
- │ │ └── delimiter_loc: (20,11)-(20,13) = "::"
+ │ │ ├── name: :C
+ │ │ ├── delimiter_loc: (20,11)-(20,13) = "::"
+ │ │ └── name_loc: (20,13)-(20,14) = "C"
│ ├── body: ∅
│ ├── end_keyword_loc: (21,0)-(21,3) = "end"
│ └── name: :A
@@ -140,20 +136,18 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (23,6)-(23,7))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (23,9)-(23,10))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (23,7)-(23,9) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (23,7)-(23,9) = "::"
+ │ │ └── name_loc: (23,9)-(23,10) = "B"
│ ├── inheritance_operator_loc: (23,11)-(23,12) = "<"
│ ├── superclass:
│ │ @ ConstantPathNode (location: (23,13)-(23,17))
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (23,13)-(23,14))
│ │ │ └── name: :C
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (23,16)-(23,17))
- │ │ │ └── name: :D
- │ │ └── delimiter_loc: (23,14)-(23,16) = "::"
+ │ │ ├── name: :D
+ │ │ ├── delimiter_loc: (23,14)-(23,16) = "::"
+ │ │ └── name_loc: (23,16)-(23,17) = "D"
│ ├── body: ∅
│ ├── end_keyword_loc: (24,0)-(24,3) = "end"
│ └── name: :B
@@ -222,10 +216,9 @@
├── constant_path:
│ @ ConstantPathNode (location: (34,6)-(34,9))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (34,8)-(34,9))
- │ │ └── name: :A
- │ └── delimiter_loc: (34,6)-(34,8) = "::"
+ │ ├── name: :A
+ │ ├── delimiter_loc: (34,6)-(34,8) = "::"
+ │ └── name_loc: (34,8)-(34,9) = "A"
├── inheritance_operator_loc: ∅
├── superclass: ∅
├── body: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/defs.txt b/test/prism/snapshots/unparser/corpus/literal/defs.txt
index 431843cc19..7858877172 100644
--- a/test/prism/snapshots/unparser/corpus/literal/defs.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/defs.txt
@@ -225,10 +225,9 @@
│ │ │ │ ├── parent:
│ │ │ │ │ @ ConstantReadNode (location: (26,5)-(26,8))
│ │ │ │ │ └── name: :Foo
- │ │ │ │ ├── child:
- │ │ │ │ │ @ ConstantReadNode (location: (26,10)-(26,13))
- │ │ │ │ │ └── name: :Bar
- │ │ │ │ └── delimiter_loc: (26,8)-(26,10) = "::"
+ │ │ │ │ ├── name: :Bar
+ │ │ │ │ ├── delimiter_loc: (26,8)-(26,10) = "::"
+ │ │ │ │ └── name_loc: (26,10)-(26,13) = "Bar"
│ │ │ ├── call_operator_loc: (26,13)-(26,14) = "."
│ │ │ ├── name: :baz
│ │ │ ├── message_loc: (26,14)-(26,17) = "baz"
@@ -269,10 +268,9 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (30,5)-(30,8))
│ │ │ │ └── name: :Foo
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (30,10)-(30,13))
- │ │ │ │ └── name: :Bar
- │ │ │ └── delimiter_loc: (30,8)-(30,10) = "::"
+ │ │ │ ├── name: :Bar
+ │ │ │ ├── delimiter_loc: (30,8)-(30,10) = "::"
+ │ │ │ └── name_loc: (30,10)-(30,13) = "Bar"
│ │ ├── opening_loc: (30,4)-(30,5) = "("
│ │ └── closing_loc: (30,13)-(30,14) = ")"
│ ├── parameters: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt
index 98b88e11ce..ddb10456bf 100644
--- a/test/prism/snapshots/unparser/corpus/literal/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt
@@ -316,18 +316,17 @@
│ ├── flags: decimal
│ └── value: 1
├── @ RationalNode (location: (19,0)-(19,2))
- │ └── numeric:
- │ @ IntegerNode (location: (19,0)-(19,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (20,0)-(20,4))
- │ └── numeric:
- │ @ FloatNode (location: (20,0)-(20,3))
- │ └── value: 1.5
+ │ ├── flags: decimal
+ │ ├── numerator: 3
+ │ └── denominator: 2
├── @ RationalNode (location: (21,0)-(21,4))
- │ └── numeric:
- │ @ FloatNode (location: (21,0)-(21,3))
- │ └── value: 1.3
+ │ ├── flags: decimal
+ │ ├── numerator: 13
+ │ └── denominator: 10
├── @ ImaginaryNode (location: (22,0)-(22,2))
│ └── numeric:
│ @ IntegerNode (location: (22,0)-(22,1))
@@ -354,10 +353,9 @@
├── @ ImaginaryNode (location: (27,0)-(27,3))
│ └── numeric:
│ @ RationalNode (location: (27,0)-(27,2))
- │ └── numeric:
- │ @ IntegerNode (location: (27,0)-(27,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ InterpolatedStringNode (location: (28,0)-(28,11))
│ ├── flags: ∅
│ ├── opening_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/module.txt b/test/prism/snapshots/unparser/corpus/literal/module.txt
index 5dd8c03b51..6428aeea82 100644
--- a/test/prism/snapshots/unparser/corpus/literal/module.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/module.txt
@@ -20,10 +20,9 @@
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (4,7)-(4,8))
│ │ │ └── name: :A
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (4,10)-(4,11))
- │ │ │ └── name: :B
- │ │ └── delimiter_loc: (4,8)-(4,10) = "::"
+ │ │ ├── name: :B
+ │ │ ├── delimiter_loc: (4,8)-(4,10) = "::"
+ │ │ └── name_loc: (4,10)-(4,11) = "B"
│ ├── body: ∅
│ ├── end_keyword_loc: (5,0)-(5,3) = "end"
│ └── name: :B
@@ -37,14 +36,12 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (7,7)-(7,8))
│ │ │ │ └── name: :A
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (7,10)-(7,11))
- │ │ │ │ └── name: :B
- │ │ │ └── delimiter_loc: (7,8)-(7,10) = "::"
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (7,13)-(7,14))
- │ │ │ └── name: :C
- │ │ └── delimiter_loc: (7,11)-(7,13) = "::"
+ │ │ │ ├── name: :B
+ │ │ │ ├── delimiter_loc: (7,8)-(7,10) = "::"
+ │ │ │ └── name_loc: (7,10)-(7,11) = "B"
+ │ │ ├── name: :C
+ │ │ ├── delimiter_loc: (7,11)-(7,13) = "::"
+ │ │ └── name_loc: (7,13)-(7,14) = "C"
│ ├── body: ∅
│ ├── end_keyword_loc: (8,0)-(8,3) = "end"
│ └── name: :C
diff --git a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
index 8dc0849638..0761b47348 100644
--- a/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/opasgn.txt
@@ -5,53 +5,53 @@
└── body: (length: 24)
├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,6))
│ ├── name_loc: (1,0)-(1,1) = "a"
- │ ├── operator_loc: (1,2)-(1,4) = "+="
+ │ ├── binary_operator_loc: (1,2)-(1,4) = "+="
│ ├── value:
│ │ @ IntegerNode (location: (1,5)-(1,6))
│ │ ├── flags: decimal
│ │ └── value: 2
│ ├── name: :a
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (2,0)-(2,6))
│ ├── name_loc: (2,0)-(2,1) = "a"
- │ ├── operator_loc: (2,2)-(2,4) = "-="
+ │ ├── binary_operator_loc: (2,2)-(2,4) = "-="
│ ├── value:
│ │ @ IntegerNode (location: (2,5)-(2,6))
│ │ ├── flags: decimal
│ │ └── value: 2
│ ├── name: :a
- │ ├── operator: :-
+ │ ├── binary_operator: :-
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,7))
│ ├── name_loc: (3,0)-(3,1) = "a"
- │ ├── operator_loc: (3,2)-(3,5) = "**="
+ │ ├── binary_operator_loc: (3,2)-(3,5) = "**="
│ ├── value:
│ │ @ IntegerNode (location: (3,6)-(3,7))
│ │ ├── flags: decimal
│ │ └── value: 2
│ ├── name: :a
- │ ├── operator: :**
+ │ ├── binary_operator: :**
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (4,0)-(4,6))
│ ├── name_loc: (4,0)-(4,1) = "a"
- │ ├── operator_loc: (4,2)-(4,4) = "*="
+ │ ├── binary_operator_loc: (4,2)-(4,4) = "*="
│ ├── value:
│ │ @ IntegerNode (location: (4,5)-(4,6))
│ │ ├── flags: decimal
│ │ └── value: 2
│ ├── name: :a
- │ ├── operator: :*
+ │ ├── binary_operator: :*
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6))
│ ├── name_loc: (5,0)-(5,1) = "a"
- │ ├── operator_loc: (5,2)-(5,4) = "/="
+ │ ├── binary_operator_loc: (5,2)-(5,4) = "/="
│ ├── value:
│ │ @ IntegerNode (location: (5,5)-(5,6))
│ │ ├── flags: decimal
│ │ └── value: 2
│ ├── name: :a
- │ ├── operator: :/
+ │ ├── binary_operator: :/
│ └── depth: 0
├── @ LocalVariableAndWriteNode (location: (6,0)-(6,7))
│ ├── name_loc: (6,0)-(6,1) = "a"
@@ -162,8 +162,8 @@
│ ├── message_loc: (10,2)-(10,3) = "b"
│ ├── read_name: :b
│ ├── write_name: :b=
- │ ├── operator: :+
- │ ├── operator_loc: (10,4)-(10,6) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (10,4)-(10,6) = "+="
│ └── value:
│ @ IntegerNode (location: (10,7)-(10,8))
│ ├── flags: decimal
@@ -178,8 +178,8 @@
│ ├── message_loc: (11,2)-(11,3) = "b"
│ ├── read_name: :b
│ ├── write_name: :b=
- │ ├── operator: :-
- │ ├── operator_loc: (11,4)-(11,6) = "-="
+ │ ├── binary_operator: :-
+ │ ├── binary_operator_loc: (11,4)-(11,6) = "-="
│ └── value:
│ @ IntegerNode (location: (11,7)-(11,8))
│ ├── flags: decimal
@@ -194,8 +194,8 @@
│ ├── message_loc: (12,2)-(12,3) = "b"
│ ├── read_name: :b
│ ├── write_name: :b=
- │ ├── operator: :**
- │ ├── operator_loc: (12,4)-(12,7) = "**="
+ │ ├── binary_operator: :**
+ │ ├── binary_operator_loc: (12,4)-(12,7) = "**="
│ └── value:
│ @ IntegerNode (location: (12,8)-(12,9))
│ ├── flags: decimal
@@ -210,8 +210,8 @@
│ ├── message_loc: (13,2)-(13,3) = "b"
│ ├── read_name: :b
│ ├── write_name: :b=
- │ ├── operator: :*
- │ ├── operator_loc: (13,4)-(13,6) = "*="
+ │ ├── binary_operator: :*
+ │ ├── binary_operator_loc: (13,4)-(13,6) = "*="
│ └── value:
│ @ IntegerNode (location: (13,7)-(13,8))
│ ├── flags: decimal
@@ -226,8 +226,8 @@
│ ├── message_loc: (14,2)-(14,3) = "b"
│ ├── read_name: :b
│ ├── write_name: :b=
- │ ├── operator: :/
- │ ├── operator_loc: (14,4)-(14,6) = "/="
+ │ ├── binary_operator: :/
+ │ ├── binary_operator_loc: (14,4)-(14,6) = "/="
│ └── value:
│ @ IntegerNode (location: (14,7)-(14,8))
│ ├── flags: decimal
@@ -293,8 +293,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (17,3)-(17,4) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (17,5)-(17,7) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (17,5)-(17,7) = "+="
│ └── value:
│ @ IntegerNode (location: (17,8)-(17,9))
│ ├── flags: decimal
@@ -323,8 +323,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (18,3)-(18,4) = "]"
│ ├── block: ∅
- │ ├── operator: :-
- │ ├── operator_loc: (18,5)-(18,7) = "-="
+ │ ├── binary_operator: :-
+ │ ├── binary_operator_loc: (18,5)-(18,7) = "-="
│ └── value:
│ @ IntegerNode (location: (18,8)-(18,9))
│ ├── flags: decimal
@@ -353,8 +353,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (19,3)-(19,4) = "]"
│ ├── block: ∅
- │ ├── operator: :**
- │ ├── operator_loc: (19,5)-(19,8) = "**="
+ │ ├── binary_operator: :**
+ │ ├── binary_operator_loc: (19,5)-(19,8) = "**="
│ └── value:
│ @ IntegerNode (location: (19,9)-(19,10))
│ ├── flags: decimal
@@ -383,8 +383,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (20,3)-(20,4) = "]"
│ ├── block: ∅
- │ ├── operator: :*
- │ ├── operator_loc: (20,5)-(20,7) = "*="
+ │ ├── binary_operator: :*
+ │ ├── binary_operator_loc: (20,5)-(20,7) = "*="
│ └── value:
│ @ IntegerNode (location: (20,8)-(20,9))
│ ├── flags: decimal
@@ -413,8 +413,8 @@
│ │ └── block: ∅
│ ├── closing_loc: (21,3)-(21,4) = "]"
│ ├── block: ∅
- │ ├── operator: :/
- │ ├── operator_loc: (21,5)-(21,7) = "/="
+ │ ├── binary_operator: :/
+ │ ├── binary_operator_loc: (21,5)-(21,7) = "/="
│ └── value:
│ @ IntegerNode (location: (21,8)-(21,9))
│ ├── flags: decimal
@@ -501,8 +501,8 @@
├── message_loc: (24,4)-(24,5) = "A"
├── read_name: :A
├── write_name: :A=
- ├── operator: :+
- ├── operator_loc: (24,6)-(24,8) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (24,6)-(24,8) = "+="
└── value:
@ IntegerNode (location: (24,9)-(24,10))
├── flags: decimal
diff --git a/test/prism/snapshots/unparser/corpus/literal/send.txt b/test/prism/snapshots/unparser/corpus/literal/send.txt
index b7eb064717..3fd7f719a1 100644
--- a/test/prism/snapshots/unparser/corpus/literal/send.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/send.txt
@@ -1251,7 +1251,7 @@
│ ├── opening_loc: (63,7)-(63,8) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (63,8)-(63,16))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (63,8)-(63,16))
│ │ ├── flags: symbol_keys
@@ -1297,7 +1297,7 @@
│ ├── opening_loc: (64,7)-(64,8) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (64,8)-(64,25))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ CallNode (location: (64,8)-(64,11))
│ │ │ ├── flags: variable_call, ignore_visibility
@@ -1571,7 +1571,7 @@
│ ├── opening_loc: (70,3)-(70,4) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (70,4)-(70,8))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (70,4)-(70,8))
│ │ ├── flags: symbol_keys
@@ -1617,7 +1617,7 @@
│ ├── opening_loc: (71,5)-(71,6) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (71,6)-(71,10))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (71,6)-(71,10))
│ │ ├── flags: symbol_keys
@@ -1663,7 +1663,7 @@
│ ├── opening_loc: (72,5)-(72,6) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (72,6)-(72,9))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (72,6)-(72,9))
│ │ ├── flags: ∅
@@ -2082,7 +2082,7 @@
│ ├── opening_loc: (81,1)-(81,2) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (81,2)-(81,7))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (81,2)-(81,7))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/unparser/corpus/literal/since/32.txt b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
index e72be6d8b7..2b28be2fa8 100644
--- a/test/prism/snapshots/unparser/corpus/literal/since/32.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/since/32.txt
@@ -36,7 +36,7 @@
│ │ ├── opening_loc: (2,5)-(2,6) = "("
│ │ ├── arguments:
│ │ │ @ ArgumentsNode (location: (2,6)-(2,18))
- │ │ │ ├── flags: contains_keyword_splat
+ │ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ │ └── arguments: (length: 2)
│ │ │ ├── @ LocalVariableReadNode (location: (2,6)-(2,14))
│ │ │ │ ├── name: :argument
diff --git a/test/prism/snapshots/unparser/corpus/literal/variables.txt b/test/prism/snapshots/unparser/corpus/literal/variables.txt
index bf79de385c..9ae8ad1207 100644
--- a/test/prism/snapshots/unparser/corpus/literal/variables.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/variables.txt
@@ -29,25 +29,21 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (8,0)-(8,6))
│ │ └── name: :SCOPED
- │ ├── child:
- │ │ @ ConstantReadNode (location: (8,8)-(8,13))
- │ │ └── name: :CONST
- │ └── delimiter_loc: (8,6)-(8,8) = "::"
+ │ ├── name: :CONST
+ │ ├── delimiter_loc: (8,6)-(8,8) = "::"
+ │ └── name_loc: (8,8)-(8,13) = "CONST"
├── @ ConstantPathNode (location: (9,0)-(9,10))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (9,2)-(9,10))
- │ │ └── name: :TOPLEVEL
- │ └── delimiter_loc: (9,0)-(9,2) = "::"
+ │ ├── name: :TOPLEVEL
+ │ ├── delimiter_loc: (9,0)-(9,2) = "::"
+ │ └── name_loc: (9,2)-(9,10) = "TOPLEVEL"
└── @ ConstantPathNode (location: (10,0)-(10,17))
├── parent:
│ @ ConstantPathNode (location: (10,0)-(10,10))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (10,2)-(10,10))
- │ │ └── name: :TOPLEVEL
- │ └── delimiter_loc: (10,0)-(10,2) = "::"
- ├── child:
- │ @ ConstantReadNode (location: (10,12)-(10,17))
- │ └── name: :CONST
- └── delimiter_loc: (10,10)-(10,12) = "::"
+ │ ├── name: :TOPLEVEL
+ │ ├── delimiter_loc: (10,0)-(10,2) = "::"
+ │ └── name_loc: (10,2)-(10,10) = "TOPLEVEL"
+ ├── name: :CONST
+ ├── delimiter_loc: (10,10)-(10,12) = "::"
+ └── name_loc: (10,12)-(10,17) = "CONST"
diff --git a/test/prism/snapshots/unparser/corpus/literal/yield.txt b/test/prism/snapshots/unparser/corpus/literal/yield.txt
deleted file mode 100644
index 4d2b272e35..0000000000
--- a/test/prism/snapshots/unparser/corpus/literal/yield.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-@ ProgramNode (location: (1,0)-(3,11))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(3,11))
- └── body: (length: 3)
- ├── @ YieldNode (location: (1,0)-(1,5))
- │ ├── keyword_loc: (1,0)-(1,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (2,0)-(2,8))
- │ ├── keyword_loc: (2,0)-(2,5) = "yield"
- │ ├── lparen_loc: (2,5)-(2,6) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (2,6)-(2,7))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ CallNode (location: (2,6)-(2,7))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :a
- │ │ ├── message_loc: (2,6)-(2,7) = "a"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── rparen_loc: (2,7)-(2,8) = ")"
- └── @ YieldNode (location: (3,0)-(3,11))
- ├── keyword_loc: (3,0)-(3,5) = "yield"
- ├── lparen_loc: (3,5)-(3,6) = "("
- ├── arguments:
- │ @ ArgumentsNode (location: (3,6)-(3,10))
- │ ├── flags: ∅
- │ └── arguments: (length: 2)
- │ ├── @ CallNode (location: (3,6)-(3,7))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :a
- │ │ ├── message_loc: (3,6)-(3,7) = "a"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── @ CallNode (location: (3,9)-(3,10))
- │ ├── flags: variable_call, ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :b
- │ ├── message_loc: (3,9)-(3,10) = "b"
- │ ├── opening_loc: ∅
- │ ├── arguments: ∅
- │ ├── closing_loc: ∅
- │ └── block: ∅
- └── rparen_loc: (3,10)-(3,11) = ")"
diff --git a/test/prism/snapshots/unparser/corpus/semantic/literal.txt b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
index ef666890be..448207f5d3 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
@@ -4,14 +4,13 @@
@ StatementsNode (location: (1,0)-(14,10))
└── body: (length: 14)
├── @ RationalNode (location: (1,0)-(1,4))
- │ └── numeric:
- │ @ FloatNode (location: (1,0)-(1,3))
- │ └── value: 1.0
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (2,0)-(2,3))
- │ └── numeric:
- │ @ IntegerNode (location: (2,0)-(2,2))
- │ ├── flags: decimal
- │ └── value: 0
+ │ ├── flags: decimal
+ │ ├── numerator: 0
+ │ └── denominator: 1
├── @ IntegerNode (location: (3,0)-(3,3))
│ ├── flags: hexadecimal
│ └── value: 1
diff --git a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
index e100dd8ecb..7dd26a38dc 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/opasgn.txt
@@ -44,8 +44,8 @@
│ └── closing_loc: (1,10)-(1,11) = "\""
├── closing_loc: (1,11)-(1,12) = "]"
├── block: ∅
- ├── operator: :+
- ├── operator_loc: (1,13)-(1,15) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,13)-(1,15) = "+="
└── value:
@ InterpolatedStringNode (location: (1,16)-(1,25))
├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/args_args_assocs.txt b/test/prism/snapshots/whitequark/args_args_assocs.txt
index 6297f212d8..d257a885ce 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs.txt
@@ -12,7 +12,7 @@
│ ├── opening_loc: (1,3)-(1,4) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,4)-(1,18))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 2)
│ │ ├── @ CallNode (location: (1,4)-(1,7))
│ │ │ ├── flags: variable_call, ignore_visibility
@@ -51,7 +51,7 @@
├── opening_loc: (3,3)-(3,4) = "("
├── arguments:
│ @ ArgumentsNode (location: (3,4)-(3,18))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ CallNode (location: (3,4)-(3,7))
│ │ ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
index 969514a511..2d986dd90a 100644
--- a/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_args_assocs_comma.txt
@@ -22,7 +22,7 @@
├── opening_loc: (1,3)-(1,4) = "["
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,18))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ CallNode (location: (1,4)-(1,7))
│ │ ├── flags: variable_call, ignore_visibility
diff --git a/test/prism/snapshots/whitequark/args_assocs.txt b/test/prism/snapshots/whitequark/args_assocs.txt
deleted file mode 100644
index 47cb68d899..0000000000
--- a/test/prism/snapshots/whitequark/args_assocs.txt
+++ /dev/null
@@ -1,195 +0,0 @@
-@ ProgramNode (location: (1,0)-(11,17))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(11,17))
- └── body: (length: 6)
- ├── @ CallNode (location: (1,0)-(1,14))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (1,0)-(1,3) = "fun"
- │ ├── opening_loc: (1,3)-(1,4) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,4)-(1,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (1,4)-(1,13))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (1,4)-(1,13))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (1,4)-(1,8))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
- │ │ │ ├── value_loc: (1,5)-(1,8) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (1,12)-(1,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (1,9)-(1,11) = "=>"
- │ ├── closing_loc: (1,13)-(1,14) = ")"
- │ └── block: ∅
- ├── @ CallNode (location: (3,0)-(3,19))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (3,0)-(3,3) = "fun"
- │ ├── opening_loc: (3,3)-(3,4) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,4)-(3,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (3,4)-(3,13))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (3,4)-(3,13))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (3,4)-(3,8))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (3,4)-(3,5) = ":"
- │ │ │ ├── value_loc: (3,5)-(3,8) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (3,12)-(3,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (3,9)-(3,11) = "=>"
- │ ├── closing_loc: (3,19)-(3,20) = ")"
- │ └── block:
- │ @ BlockArgumentNode (location: (3,15)-(3,19))
- │ ├── expression:
- │ │ @ CallNode (location: (3,16)-(3,19))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :baz
- │ │ ├── message_loc: (3,16)-(3,19) = "baz"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── operator_loc: (3,15)-(3,16) = "&"
- ├── @ CallNode (location: (5,0)-(5,21))
- │ ├── flags: ignore_visibility
- │ ├── receiver:
- │ │ @ SelfNode (location: (5,0)-(5,4))
- │ ├── call_operator_loc: (5,4)-(5,5) = "."
- │ ├── name: :[]=
- │ ├── message_loc: (5,5)-(5,8) = "[]="
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,9)-(5,21))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 2)
- │ │ ├── @ CallNode (location: (5,9)-(5,12))
- │ │ │ ├── flags: variable_call, ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :foo
- │ │ │ ├── message_loc: (5,9)-(5,12) = "foo"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block: ∅
- │ │ └── @ KeywordHashNode (location: (5,14)-(5,21))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (5,14)-(5,21))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (5,14)-(5,16))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (5,14)-(5,15) = ":"
- │ │ │ ├── value_loc: (5,15)-(5,16) = "a"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "a"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (5,20)-(5,21))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (5,17)-(5,19) = "=>"
- │ ├── closing_loc: ∅
- │ └── block: ∅
- ├── @ CallNode (location: (7,0)-(7,15))
- │ ├── flags: ignore_visibility
- │ ├── receiver:
- │ │ @ SelfNode (location: (7,0)-(7,4))
- │ ├── call_operator_loc: ∅
- │ ├── name: :[]
- │ ├── message_loc: (7,4)-(7,15) = "[:bar => 1]"
- │ ├── opening_loc: (7,4)-(7,5) = "["
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (7,5)-(7,14))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (7,5)-(7,14))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (7,5)-(7,14))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (7,5)-(7,9))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (7,5)-(7,6) = ":"
- │ │ │ ├── value_loc: (7,6)-(7,9) = "bar"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "bar"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (7,13)-(7,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (7,10)-(7,12) = "=>"
- │ ├── closing_loc: (7,14)-(7,15) = "]"
- │ └── block: ∅
- ├── @ SuperNode (location: (9,0)-(9,17))
- │ ├── keyword_loc: (9,0)-(9,5) = "super"
- │ ├── lparen_loc: (9,5)-(9,6) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (9,6)-(9,16))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (9,6)-(9,16))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (9,6)-(9,16))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (9,6)-(9,10))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
- │ │ │ ├── value_loc: (9,7)-(9,10) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (9,14)-(9,16))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 42
- │ │ └── operator_loc: (9,11)-(9,13) = "=>"
- │ ├── rparen_loc: (9,16)-(9,17) = ")"
- │ └── block: ∅
- └── @ YieldNode (location: (11,0)-(11,17))
- ├── keyword_loc: (11,0)-(11,5) = "yield"
- ├── lparen_loc: (11,5)-(11,6) = "("
- ├── arguments:
- │ @ ArgumentsNode (location: (11,6)-(11,16))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ KeywordHashNode (location: (11,6)-(11,16))
- │ ├── flags: symbol_keys
- │ └── elements: (length: 1)
- │ └── @ AssocNode (location: (11,6)-(11,16))
- │ ├── key:
- │ │ @ SymbolNode (location: (11,6)-(11,10))
- │ │ ├── flags: forced_us_ascii_encoding
- │ │ ├── opening_loc: (11,6)-(11,7) = ":"
- │ │ ├── value_loc: (11,7)-(11,10) = "foo"
- │ │ ├── closing_loc: ∅
- │ │ └── unescaped: "foo"
- │ ├── value:
- │ │ @ IntegerNode (location: (11,14)-(11,16))
- │ │ ├── flags: decimal
- │ │ └── value: 42
- │ └── operator_loc: (11,11)-(11,13) = "=>"
- └── rparen_loc: (11,16)-(11,17) = ")"
diff --git a/test/prism/snapshots/whitequark/args_assocs_comma.txt b/test/prism/snapshots/whitequark/args_assocs_comma.txt
index b1b9fbeefe..64a25bbc45 100644
--- a/test/prism/snapshots/whitequark/args_assocs_comma.txt
+++ b/test/prism/snapshots/whitequark/args_assocs_comma.txt
@@ -22,7 +22,7 @@
├── opening_loc: (1,3)-(1,4) = "["
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,13))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,4)-(1,13))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/args_assocs_legacy.txt b/test/prism/snapshots/whitequark/args_assocs_legacy.txt
deleted file mode 100644
index 47cb68d899..0000000000
--- a/test/prism/snapshots/whitequark/args_assocs_legacy.txt
+++ /dev/null
@@ -1,195 +0,0 @@
-@ ProgramNode (location: (1,0)-(11,17))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(11,17))
- └── body: (length: 6)
- ├── @ CallNode (location: (1,0)-(1,14))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (1,0)-(1,3) = "fun"
- │ ├── opening_loc: (1,3)-(1,4) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (1,4)-(1,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (1,4)-(1,13))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (1,4)-(1,13))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (1,4)-(1,8))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (1,4)-(1,5) = ":"
- │ │ │ ├── value_loc: (1,5)-(1,8) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (1,12)-(1,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (1,9)-(1,11) = "=>"
- │ ├── closing_loc: (1,13)-(1,14) = ")"
- │ └── block: ∅
- ├── @ CallNode (location: (3,0)-(3,19))
- │ ├── flags: ignore_visibility
- │ ├── receiver: ∅
- │ ├── call_operator_loc: ∅
- │ ├── name: :fun
- │ ├── message_loc: (3,0)-(3,3) = "fun"
- │ ├── opening_loc: (3,3)-(3,4) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (3,4)-(3,13))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (3,4)-(3,13))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (3,4)-(3,13))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (3,4)-(3,8))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (3,4)-(3,5) = ":"
- │ │ │ ├── value_loc: (3,5)-(3,8) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (3,12)-(3,13))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (3,9)-(3,11) = "=>"
- │ ├── closing_loc: (3,19)-(3,20) = ")"
- │ └── block:
- │ @ BlockArgumentNode (location: (3,15)-(3,19))
- │ ├── expression:
- │ │ @ CallNode (location: (3,16)-(3,19))
- │ │ ├── flags: variable_call, ignore_visibility
- │ │ ├── receiver: ∅
- │ │ ├── call_operator_loc: ∅
- │ │ ├── name: :baz
- │ │ ├── message_loc: (3,16)-(3,19) = "baz"
- │ │ ├── opening_loc: ∅
- │ │ ├── arguments: ∅
- │ │ ├── closing_loc: ∅
- │ │ └── block: ∅
- │ └── operator_loc: (3,15)-(3,16) = "&"
- ├── @ CallNode (location: (5,0)-(5,21))
- │ ├── flags: ignore_visibility
- │ ├── receiver:
- │ │ @ SelfNode (location: (5,0)-(5,4))
- │ ├── call_operator_loc: (5,4)-(5,5) = "."
- │ ├── name: :[]=
- │ ├── message_loc: (5,5)-(5,8) = "[]="
- │ ├── opening_loc: ∅
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (5,9)-(5,21))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 2)
- │ │ ├── @ CallNode (location: (5,9)-(5,12))
- │ │ │ ├── flags: variable_call, ignore_visibility
- │ │ │ ├── receiver: ∅
- │ │ │ ├── call_operator_loc: ∅
- │ │ │ ├── name: :foo
- │ │ │ ├── message_loc: (5,9)-(5,12) = "foo"
- │ │ │ ├── opening_loc: ∅
- │ │ │ ├── arguments: ∅
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── block: ∅
- │ │ └── @ KeywordHashNode (location: (5,14)-(5,21))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (5,14)-(5,21))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (5,14)-(5,16))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (5,14)-(5,15) = ":"
- │ │ │ ├── value_loc: (5,15)-(5,16) = "a"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "a"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (5,20)-(5,21))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (5,17)-(5,19) = "=>"
- │ ├── closing_loc: ∅
- │ └── block: ∅
- ├── @ CallNode (location: (7,0)-(7,15))
- │ ├── flags: ignore_visibility
- │ ├── receiver:
- │ │ @ SelfNode (location: (7,0)-(7,4))
- │ ├── call_operator_loc: ∅
- │ ├── name: :[]
- │ ├── message_loc: (7,4)-(7,15) = "[:bar => 1]"
- │ ├── opening_loc: (7,4)-(7,5) = "["
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (7,5)-(7,14))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (7,5)-(7,14))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (7,5)-(7,14))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (7,5)-(7,9))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (7,5)-(7,6) = ":"
- │ │ │ ├── value_loc: (7,6)-(7,9) = "bar"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "bar"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (7,13)-(7,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
- │ │ └── operator_loc: (7,10)-(7,12) = "=>"
- │ ├── closing_loc: (7,14)-(7,15) = "]"
- │ └── block: ∅
- ├── @ SuperNode (location: (9,0)-(9,17))
- │ ├── keyword_loc: (9,0)-(9,5) = "super"
- │ ├── lparen_loc: (9,5)-(9,6) = "("
- │ ├── arguments:
- │ │ @ ArgumentsNode (location: (9,6)-(9,16))
- │ │ ├── flags: ∅
- │ │ └── arguments: (length: 1)
- │ │ └── @ KeywordHashNode (location: (9,6)-(9,16))
- │ │ ├── flags: symbol_keys
- │ │ └── elements: (length: 1)
- │ │ └── @ AssocNode (location: (9,6)-(9,16))
- │ │ ├── key:
- │ │ │ @ SymbolNode (location: (9,6)-(9,10))
- │ │ │ ├── flags: forced_us_ascii_encoding
- │ │ │ ├── opening_loc: (9,6)-(9,7) = ":"
- │ │ │ ├── value_loc: (9,7)-(9,10) = "foo"
- │ │ │ ├── closing_loc: ∅
- │ │ │ └── unescaped: "foo"
- │ │ ├── value:
- │ │ │ @ IntegerNode (location: (9,14)-(9,16))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 42
- │ │ └── operator_loc: (9,11)-(9,13) = "=>"
- │ ├── rparen_loc: (9,16)-(9,17) = ")"
- │ └── block: ∅
- └── @ YieldNode (location: (11,0)-(11,17))
- ├── keyword_loc: (11,0)-(11,5) = "yield"
- ├── lparen_loc: (11,5)-(11,6) = "("
- ├── arguments:
- │ @ ArgumentsNode (location: (11,6)-(11,16))
- │ ├── flags: ∅
- │ └── arguments: (length: 1)
- │ └── @ KeywordHashNode (location: (11,6)-(11,16))
- │ ├── flags: symbol_keys
- │ └── elements: (length: 1)
- │ └── @ AssocNode (location: (11,6)-(11,16))
- │ ├── key:
- │ │ @ SymbolNode (location: (11,6)-(11,10))
- │ │ ├── flags: forced_us_ascii_encoding
- │ │ ├── opening_loc: (11,6)-(11,7) = ":"
- │ │ ├── value_loc: (11,7)-(11,10) = "foo"
- │ │ ├── closing_loc: ∅
- │ │ └── unescaped: "foo"
- │ ├── value:
- │ │ @ IntegerNode (location: (11,14)-(11,16))
- │ │ ├── flags: decimal
- │ │ └── value: 42
- │ └── operator_loc: (11,11)-(11,13) = "=>"
- └── rparen_loc: (11,16)-(11,17) = ")"
diff --git a/test/prism/snapshots/whitequark/bug_cmdarg.txt b/test/prism/snapshots/whitequark/bug_cmdarg.txt
index 509dd7e818..32d05746a7 100644
--- a/test/prism/snapshots/whitequark/bug_cmdarg.txt
+++ b/test/prism/snapshots/whitequark/bug_cmdarg.txt
@@ -12,7 +12,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,7)-(1,15))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,7)-(1,15))
│ │ ├── flags: symbol_keys
@@ -62,7 +62,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (5,2)-(5,26))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (5,2)-(5,26))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/casgn_scoped.txt b/test/prism/snapshots/whitequark/casgn_scoped.txt
index 4e3fd6fe44..42b90be061 100644
--- a/test/prism/snapshots/whitequark/casgn_scoped.txt
+++ b/test/prism/snapshots/whitequark/casgn_scoped.txt
@@ -9,10 +9,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (1,0)-(1,3))
│ │ └── name: :Bar
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,5)-(1,8))
- │ │ └── name: :Foo
- │ └── delimiter_loc: (1,3)-(1,5) = "::"
+ │ ├── name: :Foo
+ │ ├── delimiter_loc: (1,3)-(1,5) = "::"
+ │ └── name_loc: (1,5)-(1,8) = "Foo"
├── operator_loc: (1,9)-(1,10) = "="
└── value:
@ IntegerNode (location: (1,11)-(1,13))
diff --git a/test/prism/snapshots/whitequark/casgn_toplevel.txt b/test/prism/snapshots/whitequark/casgn_toplevel.txt
index 11facfefb3..070d90a46b 100644
--- a/test/prism/snapshots/whitequark/casgn_toplevel.txt
+++ b/test/prism/snapshots/whitequark/casgn_toplevel.txt
@@ -7,10 +7,9 @@
├── target:
│ @ ConstantPathNode (location: (1,0)-(1,5))
│ ├── parent: ∅
- │ ├── child:
- │ │ @ ConstantReadNode (location: (1,2)-(1,5))
- │ │ └── name: :Foo
- │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ ├── name: :Foo
+ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ └── name_loc: (1,2)-(1,5) = "Foo"
├── operator_loc: (1,6)-(1,7) = "="
└── value:
@ IntegerNode (location: (1,8)-(1,10))
diff --git a/test/prism/snapshots/whitequark/complex.txt b/test/prism/snapshots/whitequark/complex.txt
index e688585a5f..bc748db09b 100644
--- a/test/prism/snapshots/whitequark/complex.txt
+++ b/test/prism/snapshots/whitequark/complex.txt
@@ -10,9 +10,9 @@
├── @ ImaginaryNode (location: (3,0)-(3,6))
│ └── numeric:
│ @ RationalNode (location: (3,0)-(3,5))
- │ └── numeric:
- │ @ FloatNode (location: (3,0)-(3,4))
- │ └── value: 42.1
+ │ ├── flags: decimal
+ │ ├── numerator: 421
+ │ └── denominator: 10
├── @ ImaginaryNode (location: (5,0)-(5,3))
│ └── numeric:
│ @ IntegerNode (location: (5,0)-(5,2))
@@ -21,7 +21,6 @@
└── @ ImaginaryNode (location: (7,0)-(7,4))
└── numeric:
@ RationalNode (location: (7,0)-(7,3))
- └── numeric:
- @ IntegerNode (location: (7,0)-(7,2))
- ├── flags: decimal
- └── value: 42
+ ├── flags: decimal
+ ├── numerator: 42
+ └── denominator: 1
diff --git a/test/prism/snapshots/whitequark/const_op_asgn.txt b/test/prism/snapshots/whitequark/const_op_asgn.txt
index 4985f3e54b..71df6208d2 100644
--- a/test/prism/snapshots/whitequark/const_op_asgn.txt
+++ b/test/prism/snapshots/whitequark/const_op_asgn.txt
@@ -7,41 +7,39 @@
│ ├── target:
│ │ @ ConstantPathNode (location: (1,0)-(1,3))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
- │ ├── operator_loc: (1,4)-(1,6) = "+="
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ └── name_loc: (1,2)-(1,3) = "A"
+ │ ├── binary_operator_loc: (1,4)-(1,6) = "+="
│ ├── value:
│ │ @ IntegerNode (location: (1,7)-(1,8))
│ │ ├── flags: decimal
│ │ └── value: 1
- │ └── operator: :+
+ │ └── binary_operator: :+
├── @ ConstantOperatorWriteNode (location: (3,0)-(3,6))
│ ├── name: :A
│ ├── name_loc: (3,0)-(3,1) = "A"
- │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── binary_operator_loc: (3,2)-(3,4) = "+="
│ ├── value:
│ │ @ IntegerNode (location: (3,5)-(3,6))
│ │ ├── flags: decimal
│ │ └── value: 1
- │ └── operator: :+
+ │ └── binary_operator: :+
├── @ ConstantPathOperatorWriteNode (location: (5,0)-(5,9))
│ ├── target:
│ │ @ ConstantPathNode (location: (5,0)-(5,4))
│ │ ├── parent:
│ │ │ @ ConstantReadNode (location: (5,0)-(5,1))
│ │ │ └── name: :B
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (5,3)-(5,4))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (5,1)-(5,3) = "::"
- │ ├── operator_loc: (5,5)-(5,7) = "+="
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (5,1)-(5,3) = "::"
+ │ │ └── name_loc: (5,3)-(5,4) = "A"
+ │ ├── binary_operator_loc: (5,5)-(5,7) = "+="
│ ├── value:
│ │ @ IntegerNode (location: (5,8)-(5,9))
│ │ ├── flags: decimal
│ │ └── value: 1
- │ └── operator: :+
+ │ └── binary_operator: :+
├── @ DefNode (location: (7,0)-(7,21))
│ ├── name: :x
│ ├── name_loc: (7,4)-(7,5) = "x"
@@ -54,10 +52,9 @@
│ │ ├── target:
│ │ │ @ ConstantPathNode (location: (7,7)-(7,10))
│ │ │ ├── parent: ∅
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (7,9)-(7,10))
- │ │ │ │ └── name: :A
- │ │ │ └── delimiter_loc: (7,7)-(7,9) = "::"
+ │ │ │ ├── name: :A
+ │ │ │ ├── delimiter_loc: (7,7)-(7,9) = "::"
+ │ │ │ └── name_loc: (7,9)-(7,10) = "A"
│ │ ├── operator_loc: (7,11)-(7,14) = "||="
│ │ └── value:
│ │ @ IntegerNode (location: (7,15)-(7,16))
@@ -83,10 +80,9 @@
│ │ @ ConstantPathNode (location: (9,7)-(9,14))
│ │ ├── parent:
│ │ │ @ SelfNode (location: (9,7)-(9,11))
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (9,13)-(9,14))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (9,11)-(9,13) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (9,11)-(9,13) = "::"
+ │ │ └── name_loc: (9,13)-(9,14) = "A"
│ ├── operator_loc: (9,15)-(9,18) = "||="
│ └── value:
│ @ IntegerNode (location: (9,19)-(9,20))
diff --git a/test/prism/snapshots/whitequark/const_scoped.txt b/test/prism/snapshots/whitequark/const_scoped.txt
index 1e2bccef96..83af4b187b 100644
--- a/test/prism/snapshots/whitequark/const_scoped.txt
+++ b/test/prism/snapshots/whitequark/const_scoped.txt
@@ -7,7 +7,6 @@
├── parent:
│ @ ConstantReadNode (location: (1,0)-(1,3))
│ └── name: :Bar
- ├── child:
- │ @ ConstantReadNode (location: (1,5)-(1,8))
- │ └── name: :Foo
- └── delimiter_loc: (1,3)-(1,5) = "::"
+ ├── name: :Foo
+ ├── delimiter_loc: (1,3)-(1,5) = "::"
+ └── name_loc: (1,5)-(1,8) = "Foo"
diff --git a/test/prism/snapshots/whitequark/const_toplevel.txt b/test/prism/snapshots/whitequark/const_toplevel.txt
index b54b069d06..3d7df5defc 100644
--- a/test/prism/snapshots/whitequark/const_toplevel.txt
+++ b/test/prism/snapshots/whitequark/const_toplevel.txt
@@ -5,7 +5,6 @@
└── body: (length: 1)
└── @ ConstantPathNode (location: (1,0)-(1,5))
├── parent: ∅
- ├── child:
- │ @ ConstantReadNode (location: (1,2)-(1,5))
- │ └── name: :Foo
- └── delimiter_loc: (1,0)-(1,2) = "::"
+ ├── name: :Foo
+ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ └── name_loc: (1,2)-(1,5) = "Foo"
diff --git a/test/prism/snapshots/whitequark/cpath.txt b/test/prism/snapshots/whitequark/cpath.txt
index b892525646..e801456bf7 100644
--- a/test/prism/snapshots/whitequark/cpath.txt
+++ b/test/prism/snapshots/whitequark/cpath.txt
@@ -9,10 +9,9 @@
│ ├── constant_path:
│ │ @ ConstantPathNode (location: (1,7)-(1,12))
│ │ ├── parent: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (1,9)-(1,12))
- │ │ │ └── name: :Foo
- │ │ └── delimiter_loc: (1,7)-(1,9) = "::"
+ │ │ ├── name: :Foo
+ │ │ ├── delimiter_loc: (1,7)-(1,9) = "::"
+ │ │ └── name_loc: (1,9)-(1,12) = "Foo"
│ ├── body: ∅
│ ├── end_keyword_loc: (1,14)-(1,17) = "end"
│ └── name: :Foo
@@ -24,10 +23,9 @@
│ ├── parent:
│ │ @ ConstantReadNode (location: (3,7)-(3,10))
│ │ └── name: :Bar
- │ ├── child:
- │ │ @ ConstantReadNode (location: (3,12)-(3,15))
- │ │ └── name: :Foo
- │ └── delimiter_loc: (3,10)-(3,12) = "::"
+ │ ├── name: :Foo
+ │ ├── delimiter_loc: (3,10)-(3,12) = "::"
+ │ └── name_loc: (3,12)-(3,15) = "Foo"
├── body: ∅
├── end_keyword_loc: (3,17)-(3,20) = "end"
└── name: :Foo
diff --git a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
index c06818a98b..acaf9c052d 100644
--- a/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_argument_with_kwrestarg.txt
@@ -36,7 +36,7 @@
│ ├── opening_loc: (1,26)-(1,27) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,27)-(1,39))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 2)
│ │ ├── @ LocalVariableReadNode (location: (1,27)-(1,35))
│ │ │ ├── name: :argument
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
index adaf111fd7..b4235fb20a 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg.txt
@@ -33,7 +33,7 @@
│ ├── opening_loc: (1,16)-(1,17) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,17)-(1,19))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,17)-(1,19))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
index a03421455c..33779abcc1 100644
--- a/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
+++ b/test/prism/snapshots/whitequark/forwarded_kwrestarg_with_additional_kwarg.txt
@@ -33,7 +33,7 @@
│ ├── opening_loc: (1,16)-(1,17) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,17)-(1,35))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,17)-(1,35))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/keyword_argument_omission.txt b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
index 62e8fecf4e..446b45b56b 100644
--- a/test/prism/snapshots/whitequark/keyword_argument_omission.txt
+++ b/test/prism/snapshots/whitequark/keyword_argument_omission.txt
@@ -12,7 +12,7 @@
├── opening_loc: (1,3)-(1,4) = "("
├── arguments:
│ @ ArgumentsNode (location: (1,4)-(1,10))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,4)-(1,10))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
index 6d0bdfb817..db281e2f0d 100644
--- a/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
+++ b/test/prism/snapshots/whitequark/kwoptarg_with_kwrestarg_and_forwarded_args.txt
@@ -39,7 +39,7 @@
│ ├── opening_loc: (1,20)-(1,21) = "("
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,21)-(1,23))
- │ │ ├── flags: contains_keyword_splat
+ │ │ ├── flags: contains_keywords, contains_keyword_splat
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,21)-(1,23))
│ │ ├── flags: ∅
diff --git a/test/prism/snapshots/whitequark/masgn_const.txt b/test/prism/snapshots/whitequark/masgn_const.txt
index 56169deb17..ddfccb0d8b 100644
--- a/test/prism/snapshots/whitequark/masgn_const.txt
+++ b/test/prism/snapshots/whitequark/masgn_const.txt
@@ -7,10 +7,9 @@
│ ├── lefts: (length: 2)
│ │ ├── @ ConstantPathTargetNode (location: (1,0)-(1,3))
│ │ │ ├── parent: ∅
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (1,2)-(1,3))
- │ │ │ │ └── name: :A
- │ │ │ └── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ │ ├── name: :A
+ │ │ │ ├── delimiter_loc: (1,0)-(1,2) = "::"
+ │ │ │ └── name_loc: (1,2)-(1,3) = "A"
│ │ └── @ LocalVariableTargetNode (location: (1,5)-(1,8))
│ │ ├── name: :foo
│ │ └── depth: 0
@@ -28,10 +27,9 @@
│ ├── @ ConstantPathTargetNode (location: (3,0)-(3,7))
│ │ ├── parent:
│ │ │ @ SelfNode (location: (3,0)-(3,4))
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (3,6)-(3,7))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (3,4)-(3,6) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (3,4)-(3,6) = "::"
+ │ │ └── name_loc: (3,6)-(3,7) = "A"
│ └── @ LocalVariableTargetNode (location: (3,9)-(3,12))
│ ├── name: :foo
│ └── depth: 0
diff --git a/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt b/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt
new file mode 100644
index 0000000000..963686b73e
--- /dev/null
+++ b/test/prism/snapshots/whitequark/method_definition_in_while_cond.txt
@@ -0,0 +1,199 @@
+@ 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/newline_in_hash_argument.txt b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
index d5e89d87f9..7ef006645b 100644
--- a/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
+++ b/test/prism/snapshots/whitequark/newline_in_hash_argument.txt
@@ -102,7 +102,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (10,8)-(11,1))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (10,8)-(11,1))
│ │ ├── flags: symbol_keys
@@ -141,7 +141,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (13,8)-(14,1))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (13,8)-(14,1))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/op_asgn.txt b/test/prism/snapshots/whitequark/op_asgn.txt
index 8f24a35ad0..f726617904 100644
--- a/test/prism/snapshots/whitequark/op_asgn.txt
+++ b/test/prism/snapshots/whitequark/op_asgn.txt
@@ -20,8 +20,8 @@
│ ├── message_loc: (1,4)-(1,5) = "A"
│ ├── read_name: :A
│ ├── write_name: :A=
- │ ├── operator: :+
- │ ├── operator_loc: (1,6)-(1,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (1,6)-(1,8) = "+="
│ └── value:
│ @ IntegerNode (location: (1,9)-(1,10))
│ ├── flags: decimal
@@ -43,8 +43,8 @@
│ ├── message_loc: (3,4)-(3,5) = "a"
│ ├── read_name: :a
│ ├── write_name: :a=
- │ ├── operator: :+
- │ ├── operator_loc: (3,6)-(3,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (3,6)-(3,8) = "+="
│ └── value:
│ @ IntegerNode (location: (3,9)-(3,10))
│ ├── flags: decimal
@@ -66,8 +66,8 @@
├── message_loc: (5,5)-(5,6) = "a"
├── read_name: :a
├── write_name: :a=
- ├── operator: :+
- ├── operator_loc: (5,7)-(5,9) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (5,7)-(5,9) = "+="
└── value:
@ IntegerNode (location: (5,10)-(5,11))
├── flags: decimal
diff --git a/test/prism/snapshots/whitequark/op_asgn_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_cmd.txt
index 109c2fd2ed..d2d86b1bf9 100644
--- a/test/prism/snapshots/whitequark/op_asgn_cmd.txt
+++ b/test/prism/snapshots/whitequark/op_asgn_cmd.txt
@@ -20,8 +20,8 @@
│ ├── message_loc: (1,4)-(1,5) = "A"
│ ├── read_name: :A
│ ├── write_name: :A=
- │ ├── operator: :+
- │ ├── operator_loc: (1,6)-(1,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (1,6)-(1,8) = "+="
│ └── value:
│ @ CallNode (location: (1,9)-(1,14))
│ ├── flags: ignore_visibility
@@ -63,8 +63,8 @@
│ ├── message_loc: (3,4)-(3,5) = "a"
│ ├── read_name: :a
│ ├── write_name: :a=
- │ ├── operator: :+
- │ ├── operator_loc: (3,6)-(3,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (3,6)-(3,8) = "+="
│ └── value:
│ @ CallNode (location: (3,9)-(3,14))
│ ├── flags: ignore_visibility
@@ -103,11 +103,10 @@
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (5,5)-(5,6))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (5,3)-(5,5) = "::"
- │ ├── operator_loc: (5,7)-(5,9) = "+="
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (5,3)-(5,5) = "::"
+ │ │ └── name_loc: (5,5)-(5,6) = "A"
+ │ ├── binary_operator_loc: (5,7)-(5,9) = "+="
│ ├── value:
│ │ @ CallNode (location: (5,10)-(5,15))
│ │ ├── flags: ignore_visibility
@@ -132,7 +131,7 @@
│ │ │ └── block: ∅
│ │ ├── closing_loc: ∅
│ │ └── block: ∅
- │ └── operator: :+
+ │ └── binary_operator: :+
└── @ CallOperatorWriteNode (location: (7,0)-(7,15))
├── flags: ∅
├── receiver:
@@ -150,8 +149,8 @@
├── message_loc: (7,5)-(7,6) = "a"
├── read_name: :a
├── write_name: :a=
- ├── operator: :+
- ├── operator_loc: (7,7)-(7,9) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (7,7)-(7,9) = "+="
└── value:
@ CallNode (location: (7,10)-(7,15))
├── flags: ignore_visibility
diff --git a/test/prism/snapshots/whitequark/op_asgn_index.txt b/test/prism/snapshots/whitequark/op_asgn_index.txt
index fe077fae13..b302abdafe 100644
--- a/test/prism/snapshots/whitequark/op_asgn_index.txt
+++ b/test/prism/snapshots/whitequark/op_asgn_index.txt
@@ -30,8 +30,8 @@
│ └── value: 1
├── closing_loc: (1,8)-(1,9) = "]"
├── block: ∅
- ├── operator: :+
- ├── operator_loc: (1,10)-(1,12) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,10)-(1,12) = "+="
└── value:
@ IntegerNode (location: (1,13)-(1,14))
├── flags: decimal
diff --git a/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt
index 87082aad94..319ed1a51a 100644
--- a/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt
+++ b/test/prism/snapshots/whitequark/op_asgn_index_cmd.txt
@@ -30,8 +30,8 @@
│ └── value: 1
├── closing_loc: (1,8)-(1,9) = "]"
├── block: ∅
- ├── operator: :+
- ├── operator_loc: (1,10)-(1,12) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (1,10)-(1,12) = "+="
└── value:
@ CallNode (location: (1,13)-(1,18))
├── flags: ignore_visibility
diff --git a/test/prism/snapshots/whitequark/parser_bug_525.txt b/test/prism/snapshots/whitequark/parser_bug_525.txt
index a69b8a207f..3a31a97cdc 100644
--- a/test/prism/snapshots/whitequark/parser_bug_525.txt
+++ b/test/prism/snapshots/whitequark/parser_bug_525.txt
@@ -12,7 +12,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (1,3)-(1,11))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 1)
│ └── @ KeywordHashNode (location: (1,3)-(1,11))
│ ├── flags: symbol_keys
diff --git a/test/prism/snapshots/whitequark/rational.txt b/test/prism/snapshots/whitequark/rational.txt
index 90bbd17929..e8c8eed508 100644
--- a/test/prism/snapshots/whitequark/rational.txt
+++ b/test/prism/snapshots/whitequark/rational.txt
@@ -4,11 +4,10 @@
@ StatementsNode (location: (1,0)-(3,3))
└── body: (length: 2)
├── @ RationalNode (location: (1,0)-(1,5))
- │ └── numeric:
- │ @ FloatNode (location: (1,0)-(1,4))
- │ └── value: 42.1
+ │ ├── flags: decimal
+ │ ├── numerator: 421
+ │ └── denominator: 10
└── @ RationalNode (location: (3,0)-(3,3))
- └── numeric:
- @ IntegerNode (location: (3,0)-(3,2))
- ├── flags: decimal
- └── value: 42
+ ├── flags: decimal
+ ├── numerator: 42
+ └── denominator: 1
diff --git a/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt
index b269104f30..840e5a4fc0 100644
--- a/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt
+++ b/test/prism/snapshots/whitequark/rescue_mod_op_assign.txt
@@ -5,7 +5,7 @@
└── body: (length: 1)
└── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,22))
├── name_loc: (1,0)-(1,3) = "foo"
- ├── operator_loc: (1,4)-(1,6) = "+="
+ ├── binary_operator_loc: (1,4)-(1,6) = "+="
├── value:
│ @ RescueModifierNode (location: (1,7)-(1,22))
│ ├── expression:
@@ -32,5 +32,5 @@
│ ├── closing_loc: ∅
│ └── block: ∅
├── name: :foo
- ├── operator: :+
+ ├── binary_operator: :+
└── depth: 0
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11380.txt b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
index b7a7ef9a98..d5ec10b06c 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11380.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11380.txt
@@ -12,7 +12,7 @@
├── opening_loc: ∅
├── arguments:
│ @ ArgumentsNode (location: (1,2)-(1,21))
- │ ├── flags: ∅
+ │ ├── flags: contains_keywords
│ └── arguments: (length: 2)
│ ├── @ LambdaNode (location: (1,2)-(1,15))
│ │ ├── locals: []
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
index 93418e6448..831d57e30d 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
@@ -225,9 +225,9 @@
│ │ │ ├── closing_loc: (7,7)-(7,8) = ")"
│ │ │ └── block: ∅
│ │ └── @ RationalNode (location: (7,10)-(7,14))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (7,10)-(7,13))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (7,15)-(7,21))
@@ -519,9 +519,9 @@
│ │ │ ├── closing_loc: (17,8)-(17,9) = ")"
│ │ │ └── block: ∅
│ │ └── @ RationalNode (location: (17,11)-(17,15))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (17,11)-(17,14))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (17,16)-(17,22))
@@ -833,9 +833,9 @@
│ │ │ ├── opening_loc: (27,3)-(27,4) = "{"
│ │ │ └── closing_loc: (27,7)-(27,8) = "}"
│ │ └── @ RationalNode (location: (27,10)-(27,14))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (27,10)-(27,13))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (27,15)-(27,21))
@@ -1152,9 +1152,9 @@
│ │ │ ├── opening_loc: (37,3)-(37,4) = "{"
│ │ │ └── closing_loc: (37,8)-(37,9) = "}"
│ │ └── @ RationalNode (location: (37,11)-(37,15))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (37,11)-(37,14))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (37,16)-(37,22))
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12073.txt b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
index 9db30f6fec..2b4d3eab2a 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_12073.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_12073.txt
@@ -21,7 +21,7 @@
│ ├── opening_loc: ∅
│ ├── arguments:
│ │ @ ArgumentsNode (location: (1,9)-(1,13))
- │ │ ├── flags: ∅
+ │ │ ├── flags: contains_keywords
│ │ └── arguments: (length: 1)
│ │ └── @ KeywordHashNode (location: (1,9)-(1,13))
│ │ ├── flags: symbol_keys
@@ -75,10 +75,9 @@
│ │ │ ├── parent:
│ │ │ │ @ ConstantReadNode (location: (3,21)-(3,22))
│ │ │ │ └── name: :A
- │ │ │ ├── child:
- │ │ │ │ @ ConstantReadNode (location: (3,24)-(3,25))
- │ │ │ │ └── name: :B
- │ │ │ └── delimiter_loc: (3,22)-(3,24) = "::"
+ │ │ │ ├── name: :B
+ │ │ │ ├── delimiter_loc: (3,22)-(3,24) = "::"
+ │ │ │ └── name_loc: (3,24)-(3,25) = "B"
│ │ └── @ StringNode (location: (3,27)-(3,29))
│ │ ├── flags: ∅
│ │ ├── opening_loc: (3,27)-(3,28) = "'"
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12402.txt b/test/prism/snapshots/whitequark/ruby_bug_12402.txt
index df5aea00c3..4935007f8a 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_12402.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_12402.txt
@@ -5,7 +5,7 @@
└── body: (length: 14)
├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,27))
│ ├── name_loc: (1,0)-(1,3) = "foo"
- │ ├── operator_loc: (1,4)-(1,6) = "+="
+ │ ├── binary_operator_loc: (1,4)-(1,6) = "+="
│ ├── value:
│ │ @ RescueModifierNode (location: (1,7)-(1,27))
│ │ ├── expression:
@@ -36,11 +36,11 @@
│ │ └── rescue_expression:
│ │ @ NilNode (location: (1,24)-(1,27))
│ ├── name: :foo
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,28))
│ ├── name_loc: (3,0)-(3,3) = "foo"
- │ ├── operator_loc: (3,4)-(3,6) = "+="
+ │ ├── binary_operator_loc: (3,4)-(3,6) = "+="
│ ├── value:
│ │ @ RescueModifierNode (location: (3,7)-(3,28))
│ │ ├── expression:
@@ -71,7 +71,7 @@
│ │ └── rescue_expression:
│ │ @ NilNode (location: (3,25)-(3,28))
│ ├── name: :foo
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
├── @ LocalVariableWriteNode (location: (5,0)-(5,26))
│ ├── name: :foo
@@ -151,8 +151,8 @@
│ ├── message_loc: (9,4)-(9,5) = "C"
│ ├── read_name: :C
│ ├── write_name: :C=
- │ ├── operator: :+
- │ ├── operator_loc: (9,6)-(9,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (9,6)-(9,8) = "+="
│ └── value:
│ @ RescueModifierNode (location: (9,9)-(9,29))
│ ├── expression:
@@ -192,8 +192,8 @@
│ ├── message_loc: (11,4)-(11,5) = "C"
│ ├── read_name: :C
│ ├── write_name: :C=
- │ ├── operator: :+
- │ ├── operator_loc: (11,6)-(11,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (11,6)-(11,8) = "+="
│ └── value:
│ @ RescueModifierNode (location: (11,9)-(11,30))
│ ├── expression:
@@ -233,8 +233,8 @@
│ ├── message_loc: (13,4)-(13,5) = "m"
│ ├── read_name: :m
│ ├── write_name: :m=
- │ ├── operator: :+
- │ ├── operator_loc: (13,6)-(13,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (13,6)-(13,8) = "+="
│ └── value:
│ @ RescueModifierNode (location: (13,9)-(13,29))
│ ├── expression:
@@ -274,8 +274,8 @@
│ ├── message_loc: (15,4)-(15,5) = "m"
│ ├── read_name: :m
│ ├── write_name: :m=
- │ ├── operator: :+
- │ ├── operator_loc: (15,6)-(15,8) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (15,6)-(15,8) = "+="
│ └── value:
│ @ RescueModifierNode (location: (15,9)-(15,30))
│ ├── expression:
@@ -312,10 +312,9 @@
│ │ │ @ LocalVariableReadNode (location: (17,0)-(17,3))
│ │ │ ├── name: :foo
│ │ │ └── depth: 0
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (17,5)-(17,6))
- │ │ │ └── name: :C
- │ │ └── delimiter_loc: (17,3)-(17,5) = "::"
+ │ │ ├── name: :C
+ │ │ ├── delimiter_loc: (17,3)-(17,5) = "::"
+ │ │ └── name_loc: (17,5)-(17,6) = "C"
│ ├── operator_loc: (17,7)-(17,10) = "||="
│ └── value:
│ @ RescueModifierNode (location: (17,11)-(17,31))
@@ -353,10 +352,9 @@
│ │ │ @ LocalVariableReadNode (location: (19,0)-(19,3))
│ │ │ ├── name: :foo
│ │ │ └── depth: 0
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (19,5)-(19,6))
- │ │ │ └── name: :C
- │ │ └── delimiter_loc: (19,3)-(19,5) = "::"
+ │ │ ├── name: :C
+ │ │ ├── delimiter_loc: (19,3)-(19,5) = "::"
+ │ │ └── name_loc: (19,5)-(19,6) = "C"
│ ├── operator_loc: (19,7)-(19,10) = "||="
│ └── value:
│ @ RescueModifierNode (location: (19,11)-(19,32))
@@ -397,8 +395,8 @@
│ ├── message_loc: (21,5)-(21,6) = "m"
│ ├── read_name: :m
│ ├── write_name: :m=
- │ ├── operator: :+
- │ ├── operator_loc: (21,7)-(21,9) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (21,7)-(21,9) = "+="
│ └── value:
│ @ RescueModifierNode (location: (21,10)-(21,30))
│ ├── expression:
@@ -438,8 +436,8 @@
│ ├── message_loc: (23,5)-(23,6) = "m"
│ ├── read_name: :m
│ ├── write_name: :m=
- │ ├── operator: :+
- │ ├── operator_loc: (23,7)-(23,9) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (23,7)-(23,9) = "+="
│ └── value:
│ @ RescueModifierNode (location: (23,10)-(23,31))
│ ├── expression:
@@ -486,8 +484,8 @@
│ │ └── value: 0
│ ├── closing_loc: (25,5)-(25,6) = "]"
│ ├── block: ∅
- │ ├── operator: :+
- │ ├── operator_loc: (25,7)-(25,9) = "+="
+ │ ├── binary_operator: :+
+ │ ├── binary_operator_loc: (25,7)-(25,9) = "+="
│ └── value:
│ @ RescueModifierNode (location: (25,10)-(25,30))
│ ├── expression:
@@ -534,8 +532,8 @@
│ └── value: 0
├── closing_loc: (27,5)-(27,6) = "]"
├── block: ∅
- ├── operator: :+
- ├── operator_loc: (27,7)-(27,9) = "+="
+ ├── binary_operator: :+
+ ├── binary_operator_loc: (27,7)-(27,9) = "+="
└── value:
@ RescueModifierNode (location: (27,10)-(27,31))
├── expression:
diff --git a/test/prism/snapshots/whitequark/ruby_bug_12669.txt b/test/prism/snapshots/whitequark/ruby_bug_12669.txt
index 86b021351b..6151143ba8 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_12669.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_12669.txt
@@ -5,11 +5,11 @@
└── body: (length: 4)
├── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,18))
│ ├── name_loc: (1,0)-(1,1) = "a"
- │ ├── operator_loc: (1,2)-(1,4) = "+="
+ │ ├── binary_operator_loc: (1,2)-(1,4) = "+="
│ ├── value:
│ │ @ LocalVariableOperatorWriteNode (location: (1,5)-(1,18))
│ │ ├── name_loc: (1,5)-(1,6) = "b"
- │ │ ├── operator_loc: (1,7)-(1,9) = "+="
+ │ │ ├── binary_operator_loc: (1,7)-(1,9) = "+="
│ │ ├── value:
│ │ │ @ CallNode (location: (1,10)-(1,18))
│ │ │ ├── flags: ignore_visibility
@@ -31,14 +31,14 @@
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
│ │ ├── name: :b
- │ │ ├── operator: :+
+ │ │ ├── binary_operator: :+
│ │ └── depth: 0
│ ├── name: :a
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
├── @ LocalVariableOperatorWriteNode (location: (3,0)-(3,17))
│ ├── name_loc: (3,0)-(3,1) = "a"
- │ ├── operator_loc: (3,2)-(3,4) = "+="
+ │ ├── binary_operator_loc: (3,2)-(3,4) = "+="
│ ├── value:
│ │ @ LocalVariableWriteNode (location: (3,5)-(3,17))
│ │ ├── name: :b
@@ -66,7 +66,7 @@
│ │ │ └── block: ∅
│ │ └── operator_loc: (3,7)-(3,8) = "="
│ ├── name: :a
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
├── @ LocalVariableWriteNode (location: (5,0)-(5,17))
│ ├── name: :a
@@ -75,7 +75,7 @@
│ ├── value:
│ │ @ LocalVariableOperatorWriteNode (location: (5,4)-(5,17))
│ │ ├── name_loc: (5,4)-(5,5) = "b"
- │ │ ├── operator_loc: (5,6)-(5,8) = "+="
+ │ │ ├── binary_operator_loc: (5,6)-(5,8) = "+="
│ │ ├── value:
│ │ │ @ CallNode (location: (5,9)-(5,17))
│ │ │ ├── flags: ignore_visibility
@@ -97,7 +97,7 @@
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
│ │ ├── name: :b
- │ │ ├── operator: :+
+ │ │ ├── binary_operator: :+
│ │ └── depth: 0
│ └── operator_loc: (5,2)-(5,3) = "="
└── @ LocalVariableWriteNode (location: (7,0)-(7,16))
diff --git a/test/prism/snapshots/whitequark/send_attr_asgn.txt b/test/prism/snapshots/whitequark/send_attr_asgn.txt
index 392ae5587e..1d09daa4a6 100644
--- a/test/prism/snapshots/whitequark/send_attr_asgn.txt
+++ b/test/prism/snapshots/whitequark/send_attr_asgn.txt
@@ -69,10 +69,9 @@
│ │ │ ├── arguments: ∅
│ │ │ ├── closing_loc: ∅
│ │ │ └── block: ∅
- │ │ ├── child:
- │ │ │ @ ConstantReadNode (location: (5,5)-(5,6))
- │ │ │ └── name: :A
- │ │ └── delimiter_loc: (5,3)-(5,5) = "::"
+ │ │ ├── name: :A
+ │ │ ├── delimiter_loc: (5,3)-(5,5) = "::"
+ │ │ └── name_loc: (5,5)-(5,6) = "A"
│ ├── operator_loc: (5,7)-(5,8) = "="
│ └── value:
│ @ IntegerNode (location: (5,9)-(5,10))
diff --git a/test/prism/snapshots/whitequark/var_op_asgn.txt b/test/prism/snapshots/whitequark/var_op_asgn.txt
index f423a62dee..f20f612fa2 100644
--- a/test/prism/snapshots/whitequark/var_op_asgn.txt
+++ b/test/prism/snapshots/whitequark/var_op_asgn.txt
@@ -6,30 +6,30 @@
├── @ ClassVariableOperatorWriteNode (location: (1,0)-(1,11))
│ ├── name: :@@var
│ ├── name_loc: (1,0)-(1,5) = "@@var"
- │ ├── operator_loc: (1,6)-(1,8) = "|="
+ │ ├── binary_operator_loc: (1,6)-(1,8) = "|="
│ ├── value:
│ │ @ IntegerNode (location: (1,9)-(1,11))
│ │ ├── flags: decimal
│ │ └── value: 10
- │ └── operator: :|
+ │ └── binary_operator: :|
├── @ InstanceVariableOperatorWriteNode (location: (3,0)-(3,7))
│ ├── name: :@a
│ ├── name_loc: (3,0)-(3,2) = "@a"
- │ ├── operator_loc: (3,3)-(3,5) = "|="
+ │ ├── binary_operator_loc: (3,3)-(3,5) = "|="
│ ├── value:
│ │ @ IntegerNode (location: (3,6)-(3,7))
│ │ ├── flags: decimal
│ │ └── value: 1
- │ └── operator: :|
+ │ └── binary_operator: :|
├── @ LocalVariableOperatorWriteNode (location: (5,0)-(5,6))
│ ├── name_loc: (5,0)-(5,1) = "a"
- │ ├── operator_loc: (5,2)-(5,4) = "+="
+ │ ├── binary_operator_loc: (5,2)-(5,4) = "+="
│ ├── value:
│ │ @ IntegerNode (location: (5,5)-(5,6))
│ │ ├── flags: decimal
│ │ └── value: 1
│ ├── name: :a
- │ ├── operator: :+
+ │ ├── binary_operator: :+
│ └── depth: 0
└── @ DefNode (location: (7,0)-(7,23))
├── name: :a
@@ -42,12 +42,12 @@
│ └── @ ClassVariableOperatorWriteNode (location: (7,7)-(7,18))
│ ├── name: :@@var
│ ├── name_loc: (7,7)-(7,12) = "@@var"
- │ ├── operator_loc: (7,13)-(7,15) = "|="
+ │ ├── binary_operator_loc: (7,13)-(7,15) = "|="
│ ├── value:
│ │ @ IntegerNode (location: (7,16)-(7,18))
│ │ ├── flags: decimal
│ │ └── value: 10
- │ └── operator: :|
+ │ └── binary_operator: :|
├── locals: []
├── def_keyword_loc: (7,0)-(7,3) = "def"
├── operator_loc: ∅
diff --git a/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt
index d56c099c7e..0bfa06d5b7 100644
--- a/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt
+++ b/test/prism/snapshots/whitequark/var_op_asgn_cmd.txt
@@ -5,7 +5,7 @@
└── body: (length: 1)
└── @ LocalVariableOperatorWriteNode (location: (1,0)-(1,12))
├── name_loc: (1,0)-(1,3) = "foo"
- ├── operator_loc: (1,4)-(1,6) = "+="
+ ├── binary_operator_loc: (1,4)-(1,6) = "+="
├── value:
│ @ CallNode (location: (1,7)-(1,12))
│ ├── flags: ignore_visibility
@@ -24,5 +24,5 @@
│ ├── closing_loc: ∅
│ └── block: ∅
├── name: :foo
- ├── operator: :+
+ ├── binary_operator: :+
└── depth: 0
diff --git a/test/prism/snapshots/whitequark/yield.txt b/test/prism/snapshots/whitequark/yield.txt
deleted file mode 100644
index 2b37dd479f..0000000000
--- a/test/prism/snapshots/whitequark/yield.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-@ ProgramNode (location: (1,0)-(7,10))
-├── locals: []
-└── statements:
- @ StatementsNode (location: (1,0)-(7,10))
- └── body: (length: 4)
- ├── @ YieldNode (location: (1,0)-(1,5))
- │ ├── keyword_loc: (1,0)-(1,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── arguments: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (3,0)-(3,9))
- │ ├── keyword_loc: (3,0)-(3,5) = "yield"
- │ ├── lparen_loc: ∅
- │ ├── 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: ∅
- │ └── rparen_loc: ∅
- ├── @ YieldNode (location: (5,0)-(5,7))
- │ ├── keyword_loc: (5,0)-(5,5) = "yield"
- │ ├── lparen_loc: (5,5)-(5,6) = "("
- │ ├── arguments: ∅
- │ └── rparen_loc: (5,6)-(5,7) = ")"
- └── @ YieldNode (location: (7,0)-(7,10))
- ├── keyword_loc: (7,0)-(7,5) = "yield"
- ├── lparen_loc: (7,5)-(7,6) = "("
- ├── arguments:
- │ @ ArgumentsNode (location: (7,6)-(7,9))
- │ ├── flags: ∅
- │ └── arguments: (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: ∅
- └── rparen_loc: (7,9)-(7,10) = ")"
diff --git a/test/prism/snapshots_test.rb b/test/prism/snapshots_test.rb
new file mode 100644
index 0000000000..0744eafad3
--- /dev/null
+++ b/test/prism/snapshots_test.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class SnapshotsTest < TestCase
+ # When we pretty-print the trees to compare against the snapshots, we want
+ # to be certain that we print with the same external encoding. This is
+ # because methods like Symbol#inspect take into account external encoding
+ # and it could change how the snapshot is generated. On machines with
+ # certain settings (like LANG=C or -Eascii-8bit) this could have been
+ # changed. So here we're going to force it to be UTF-8 to keep the snapshots
+ # consistent.
+ def setup
+ @previous_default_external = Encoding.default_external
+ ignore_warnings { Encoding.default_external = Encoding::UTF_8 }
+ end
+
+ def teardown
+ ignore_warnings { Encoding.default_external = @previous_default_external }
+ end
+
+ except = []
+
+ # These fail on TruffleRuby due to a difference in Symbol#inspect:
+ # :测试 vs :"测试"
+ if RUBY_ENGINE == "truffleruby"
+ except.push(
+ "emoji_method_calls.txt",
+ "seattlerb/bug202.txt",
+ "seattlerb/magic_encoding_comment.txt"
+ )
+ end
+
+ Fixture.each(except: except) do |fixture|
+ define_method(fixture.test_name) { assert_snapshot(fixture) }
+ end
+
+ private
+
+ def assert_snapshot(fixture)
+ source = fixture.read
+
+ result = Prism.parse(source, filepath: fixture.path)
+ assert result.success?
+
+ printed = PP.pp(result.value, +"", 79)
+ snapshot = fixture.snapshot_path
+
+ if File.exist?(snapshot)
+ saved = File.read(snapshot)
+
+ # If the snapshot file exists, but the printed value does not match the
+ # snapshot, then update the snapshot file.
+ if printed != saved
+ File.write(snapshot, printed)
+ warn("Updated snapshot at #{snapshot}.")
+ end
+
+ # If the snapshot file exists, then assert that the printed value
+ # matches the snapshot.
+ assert_equal(saved, printed)
+ else
+ # If the snapshot file does not yet exist, then write it out now.
+ directory = File.dirname(snapshot)
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
+
+ File.write(snapshot, printed)
+ warn("Created snapshot at #{snapshot}.")
+ end
+ end
+ end
+end
diff --git a/test/prism/snippets_test.rb b/test/prism/snippets_test.rb
new file mode 100644
index 0000000000..26847da184
--- /dev/null
+++ b/test/prism/snippets_test.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require_relative "test_helper"
+
+module Prism
+ class SnippetsTest < TestCase
+ except = [
+ "newline_terminated.txt",
+ "seattlerb/begin_rescue_else_ensure_no_bodies.txt",
+ "seattlerb/case_in.txt",
+ "seattlerb/parse_line_defn_no_parens.txt",
+ "seattlerb/pct_nl.txt",
+ "seattlerb/str_heredoc_interp.txt",
+ "spanning_heredoc_newlines.txt",
+ "unparser/corpus/semantic/dstr.txt",
+ "whitequark/dedenting_heredoc.txt",
+ "whitequark/multiple_pattern_matches.txt"
+ ]
+
+ Fixture.each(except: except) do |fixture|
+ define_method(fixture.test_name) { assert_snippets(fixture) }
+ end
+
+ private
+
+ # We test every snippet (separated by \n\n) in isolation to ensure the
+ # parser does not try to read bytes further than the end of each snippet.
+ def assert_snippets(fixture)
+ fixture.read.split(/(?<=\S)\n\n(?=\S)/).each do |snippet|
+ snippet = snippet.rstrip
+
+ result = Prism.parse(snippet, filepath: fixture.path)
+ assert result.success?
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ dumped = Prism.dump(snippet, filepath: fixture.path)
+ assert_equal_nodes(result.value, Prism.load(snippet, dumped).value)
+ end
+ end
+ end
+ end
+end
diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb
index 77af7e7b45..d6d0abf548 100644
--- a/test/prism/test_helper.rb
+++ b/test/prism/test_helper.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
require "prism"
-require "ripper"
require "pp"
+require "ripper"
+require "stringio"
require "test/unit"
require "tempfile"
@@ -16,19 +17,202 @@ if defined?(Test::Unit::Assertions::AssertionMessage)
end
module Prism
+ # A convenience method for retrieving the first statement in the source string
+ # parsed by Prism.
+ def self.parse_statement(source, **options)
+ parse(source, **options).value.statements.body.first
+ end
+
+ class ParseResult < Result
+ # Returns the first statement in the body of the parsed source.
+ def statement
+ value.statements.body.first
+ end
+ end
+
class TestCase < ::Test::Unit::TestCase
+ # We have a set of fixtures that we use to test various aspects of the
+ # parser. They are all represented as .txt files under the
+ # test/prism/fixtures directory. Typically in test files you will find calls
+ # to Fixture.each which yields Fixture objects to the given block. These
+ # are used to define test methods that assert against each fixture in some
+ # way.
+ class Fixture
+ BASE = File.join(__dir__, "fixtures")
+
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ end
+
+ def read
+ File.read(full_path, binmode: true, external_encoding: Encoding::UTF_8)
+ end
+
+ def full_path
+ File.join(BASE, path)
+ end
+
+ def snapshot_path
+ File.join(__dir__, "snapshots", path)
+ end
+
+ def test_name
+ :"test_#{path}"
+ end
+
+ def self.each(except: [], &block)
+ paths = Dir[ENV.fetch("FOCUS") { File.join("**", "*.txt") }, base: BASE] - except
+ paths.each { |path| yield Fixture.new(path) }
+ end
+ end
+
+ # Yield each encoding that we want to test, along with a range of the
+ # codepoints that should be tested.
+ def self.each_encoding
+ codepoints_1byte = 0...0x100
+
+ yield Encoding::ASCII_8BIT, codepoints_1byte
+ yield Encoding::US_ASCII, codepoints_1byte
+
+ if !ENV["PRISM_BUILD_MINIMAL"]
+ yield Encoding::Windows_1253, codepoints_1byte
+ end
+
+ # By default we don't test every codepoint in these encodings because it
+ # takes a very long time.
+ return unless ENV["PRISM_TEST_ALL_ENCODINGS"]
+
+ yield Encoding::CP850, codepoints_1byte
+ yield Encoding::CP852, codepoints_1byte
+ yield Encoding::CP855, codepoints_1byte
+ yield Encoding::GB1988, codepoints_1byte
+ yield Encoding::IBM437, codepoints_1byte
+ yield Encoding::IBM720, codepoints_1byte
+ yield Encoding::IBM737, codepoints_1byte
+ yield Encoding::IBM775, codepoints_1byte
+ yield Encoding::IBM852, codepoints_1byte
+ yield Encoding::IBM855, codepoints_1byte
+ yield Encoding::IBM857, codepoints_1byte
+ yield Encoding::IBM860, codepoints_1byte
+ yield Encoding::IBM861, codepoints_1byte
+ yield Encoding::IBM862, codepoints_1byte
+ yield Encoding::IBM863, codepoints_1byte
+ yield Encoding::IBM864, codepoints_1byte
+ yield Encoding::IBM865, codepoints_1byte
+ yield Encoding::IBM866, codepoints_1byte
+ yield Encoding::IBM869, codepoints_1byte
+ yield Encoding::ISO_8859_1, codepoints_1byte
+ yield Encoding::ISO_8859_2, codepoints_1byte
+ yield Encoding::ISO_8859_3, codepoints_1byte
+ yield Encoding::ISO_8859_4, codepoints_1byte
+ yield Encoding::ISO_8859_5, codepoints_1byte
+ yield Encoding::ISO_8859_6, codepoints_1byte
+ yield Encoding::ISO_8859_7, codepoints_1byte
+ yield Encoding::ISO_8859_8, codepoints_1byte
+ yield Encoding::ISO_8859_9, codepoints_1byte
+ yield Encoding::ISO_8859_10, codepoints_1byte
+ yield Encoding::ISO_8859_11, codepoints_1byte
+ yield Encoding::ISO_8859_13, codepoints_1byte
+ yield Encoding::ISO_8859_14, codepoints_1byte
+ yield Encoding::ISO_8859_15, codepoints_1byte
+ yield Encoding::ISO_8859_16, codepoints_1byte
+ yield Encoding::KOI8_R, codepoints_1byte
+ yield Encoding::KOI8_U, codepoints_1byte
+ yield Encoding::MACCENTEURO, codepoints_1byte
+ yield Encoding::MACCROATIAN, codepoints_1byte
+ yield Encoding::MACCYRILLIC, codepoints_1byte
+ yield Encoding::MACGREEK, codepoints_1byte
+ yield Encoding::MACICELAND, codepoints_1byte
+ yield Encoding::MACROMAN, codepoints_1byte
+ yield Encoding::MACROMANIA, codepoints_1byte
+ yield Encoding::MACTHAI, codepoints_1byte
+ yield Encoding::MACTURKISH, codepoints_1byte
+ yield Encoding::MACUKRAINE, codepoints_1byte
+ yield Encoding::TIS_620, codepoints_1byte
+ yield Encoding::Windows_1250, codepoints_1byte
+ yield Encoding::Windows_1251, codepoints_1byte
+ yield Encoding::Windows_1252, codepoints_1byte
+ yield Encoding::Windows_1254, codepoints_1byte
+ yield Encoding::Windows_1255, codepoints_1byte
+ yield Encoding::Windows_1256, codepoints_1byte
+ yield Encoding::Windows_1257, codepoints_1byte
+ yield Encoding::Windows_1258, codepoints_1byte
+ yield Encoding::Windows_874, codepoints_1byte
+
+ codepoints_2bytes = 0...0x10000
+
+ yield Encoding::Big5, codepoints_2bytes
+ yield Encoding::Big5_HKSCS, codepoints_2bytes
+ yield Encoding::Big5_UAO, codepoints_2bytes
+ yield Encoding::CP949, codepoints_2bytes
+ yield Encoding::CP950, codepoints_2bytes
+ yield Encoding::CP951, codepoints_2bytes
+ yield Encoding::EUC_KR, codepoints_2bytes
+ yield Encoding::GBK, codepoints_2bytes
+ yield Encoding::GB12345, codepoints_2bytes
+ yield Encoding::GB2312, codepoints_2bytes
+ yield Encoding::MACJAPANESE, codepoints_2bytes
+ yield Encoding::Shift_JIS, codepoints_2bytes
+ yield Encoding::SJIS_DoCoMo, codepoints_2bytes
+ yield Encoding::SJIS_KDDI, codepoints_2bytes
+ yield Encoding::SJIS_SoftBank, codepoints_2bytes
+ yield Encoding::Windows_31J, codepoints_2bytes
+
+ codepoints_unicode = (0...0x110000)
+
+ yield Encoding::UTF_8, codepoints_unicode
+ yield Encoding::UTF8_MAC, codepoints_unicode
+ yield Encoding::UTF8_DoCoMo, codepoints_unicode
+ yield Encoding::UTF8_KDDI, codepoints_unicode
+ yield Encoding::UTF8_SoftBank, codepoints_unicode
+ yield Encoding::CESU_8, codepoints_unicode
+
+ codepoints_eucjp = [
+ *(0...0x10000),
+ *(0...0x10000).map { |bytes| bytes | 0x8F0000 }
+ ]
+
+ yield Encoding::CP51932, codepoints_eucjp
+ yield Encoding::EUC_JP, codepoints_eucjp
+ yield Encoding::EUCJP_MS, codepoints_eucjp
+ yield Encoding::EUC_JIS_2004, codepoints_eucjp
+
+ codepoints_emacs_mule = [
+ *(0...0x80),
+ *((0x81...0x90).flat_map { |byte1| (0x90...0x100).map { |byte2| byte1 << 8 | byte2 } }),
+ *((0x90...0x9C).flat_map { |byte1| (0xA0...0x100).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| byte1 << 16 | byte2 << 8 | byte3 } } }),
+ *((0xF0...0xF5).flat_map { |byte2| (0xA0...0x100).flat_map { |byte3| (0xA0...0x100).flat_map { |byte4| 0x9C << 24 | byte3 << 16 | byte3 << 8 | byte4 } } }),
+ ]
+
+ yield Encoding::EMACS_MULE, codepoints_emacs_mule
+ yield Encoding::STATELESS_ISO_2022_JP, codepoints_emacs_mule
+ yield Encoding::STATELESS_ISO_2022_JP_KDDI, codepoints_emacs_mule
+
+ codepoints_gb18030 = [
+ *(0...0x80),
+ *((0x81..0xFE).flat_map { |byte1| (0x40...0x100).map { |byte2| byte1 << 8 | byte2 } }),
+ *((0x81..0xFE).flat_map { |byte1| (0x30...0x40).flat_map { |byte2| (0x81..0xFE).flat_map { |byte3| (0x2F...0x41).map { |byte4| byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4 } } } }),
+ ]
+
+ yield Encoding::GB18030, codepoints_gb18030
+
+ codepoints_euc_tw = [
+ *(0..0x7F),
+ *(0xA1..0xFF).flat_map { |byte1| (0xA1..0xFF).map { |byte2| (byte1 << 8) | byte2 } },
+ *(0xA1..0xB0).flat_map { |byte2| (0xA1..0xFF).flat_map { |byte3| (0xA1..0xFF).flat_map { |byte4| 0x8E << 24 | byte2 << 16 | byte3 << 8 | byte4 } } }
+ ]
+
+ yield Encoding::EUC_TW, codepoints_euc_tw
+ end
+
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
+ ignore_warnings { RubyVM::InstructionSequence.compile(source) }
end
# Assert that the given source is valid Ruby syntax by attempting to
@@ -51,6 +235,8 @@ module Prism
end
end
+ # CRuby has this same method, so define it so that we don't accidentally
+ # break CRuby CI.
def assert_raises(*args, &block)
raise "Use assert_raise instead"
end
@@ -122,5 +308,16 @@ module Prism
assert_equal expected, actual
end
end
+
+ def ignore_warnings
+ previous = $VERBOSE
+ $VERBOSE = nil
+
+ begin
+ yield
+ ensure
+ $VERBOSE = previous
+ end
+ end
end
end
diff --git a/test/prism/unescape_test.rb b/test/prism/unescape_test.rb
index 3f78a59b11..24a9e3f6bc 100644
--- a/test/prism/unescape_test.rb
+++ b/test/prism/unescape_test.rb
@@ -41,7 +41,7 @@ module Prism
result = Prism.parse(code(escape), encoding: "binary")
if result.success?
- yield result.value.statements.body.first
+ yield result.statement
else
:error
end
diff --git a/test/reline/helper.rb b/test/reline/helper.rb
index 26fe834482..9a346a17a1 100644
--- a/test/reline/helper.rb
+++ b/test/reline/helper.rb
@@ -22,29 +22,36 @@ module Reline
class <<self
def test_mode(ansi: false)
@original_iogate = IOGate
- remove_const('IOGate')
- const_set('IOGate', ansi ? Reline::ANSI : Reline::GeneralIO)
+
if ENV['RELINE_TEST_ENCODING']
encoding = Encoding.find(ENV['RELINE_TEST_ENCODING'])
else
encoding = Encoding::UTF_8
end
- @original_get_screen_size = IOGate.method(:get_screen_size)
- IOGate.singleton_class.remove_method(:get_screen_size)
- def IOGate.get_screen_size
- [24, 80]
+
+ if ansi
+ new_io_gate = ANSI.new
+ # Setting ANSI gate's screen size through set_screen_size will also change the tester's stdin's screen size
+ # Let's avoid that side-effect by stubbing the get_screen_size method
+ new_io_gate.define_singleton_method(:get_screen_size) do
+ [24, 80]
+ end
+ new_io_gate.define_singleton_method(:encoding) do
+ encoding
+ end
+ else
+ new_io_gate = Dumb.new(encoding: encoding)
end
- Reline::GeneralIO.reset(encoding: encoding) unless ansi
+
+ remove_const('IOGate')
+ const_set('IOGate', new_io_gate)
core.config.instance_variable_set(:@test_mode, true)
core.config.reset
end
def test_reset
- IOGate.singleton_class.remove_method(:get_screen_size)
- IOGate.define_singleton_method(:get_screen_size, @original_get_screen_size)
remove_const('IOGate')
const_set('IOGate', @original_iogate)
- Reline::GeneralIO.reset
Reline.instance_variable_set(:@core, nil)
end
@@ -146,7 +153,7 @@ class Reline::TestCase < Test::Unit::TestCase
expected.bytesize, byte_pointer,
<<~EOM)
<#{expected.inspect} (#{expected.encoding.inspect})> expected but was
- <#{chunk.inspect} (#{chunk.encoding.inspect})> in <Terminal #{Reline::GeneralIO.encoding.inspect}>
+ <#{chunk.inspect} (#{chunk.encoding.inspect})> in <Terminal #{Reline::Dumb.new.encoding.inspect}>
EOM
end
@@ -161,7 +168,7 @@ class Reline::TestCase < Test::Unit::TestCase
def assert_key_binding(input, method_symbol, editing_modes = [:emacs, :vi_insert, :vi_command])
editing_modes.each do |editing_mode|
@config.editing_mode = editing_mode
- assert_equal(method_symbol, @config.editing_mode.default_key_bindings[input.bytes])
+ assert_equal(method_symbol, @config.editing_mode.get(input.bytes))
end
end
end
diff --git a/test/reline/test_ansi_with_terminfo.rb b/test/reline/test_ansi_with_terminfo.rb
index e1c56b9ee1..3adda10716 100644
--- a/test/reline/test_ansi_with_terminfo.rb
+++ b/test/reline/test_ansi_with_terminfo.rb
@@ -1,7 +1,7 @@
require_relative 'helper'
-require 'reline/ansi'
+require 'reline'
-class Reline::ANSI::TestWithTerminfo < Reline::TestCase
+class Reline::ANSI::WithTerminfoTest < Reline::TestCase
def setup
Reline.send(:test_mode, ansi: true)
@config = Reline::Config.new
diff --git a/test/reline/test_ansi_without_terminfo.rb b/test/reline/test_ansi_without_terminfo.rb
index 3d153514f3..2db14cf0a0 100644
--- a/test/reline/test_ansi_without_terminfo.rb
+++ b/test/reline/test_ansi_without_terminfo.rb
@@ -1,7 +1,7 @@
require_relative 'helper'
-require 'reline/ansi'
+require 'reline'
-class Reline::ANSI::TestWithoutTerminfo < Reline::TestCase
+class Reline::ANSI::WithoutTerminfoTest < Reline::TestCase
def setup
Reline.send(:test_mode, ansi: true)
@config = Reline::Config.new
diff --git a/test/reline/test_config.rb b/test/reline/test_config.rb
index 6d6f25dff3..16727c9bc9 100644
--- a/test/reline/test_config.rb
+++ b/test/reline/test_config.rb
@@ -22,6 +22,15 @@ class Reline::Config::Test < Reline::TestCase
@config.reset
end
+ def additional_key_bindings(keymap_label)
+ @config.instance_variable_get(:@additional_key_bindings)[keymap_label].instance_variable_get(:@key_bindings)
+ end
+
+ def registered_key_bindings(keys)
+ key_bindings = @config.key_bindings
+ keys.to_h { |key| [key, key_bindings.get(key)] }
+ end
+
def test_read_lines
@config.read_lines(<<~LINES.lines)
set bell-style on
@@ -85,28 +94,29 @@ class Reline::Config::Test < Reline::TestCase
def test_encoding_is_ascii
@config.reset
- Reline.core.io_gate.reset(encoding: Encoding::US_ASCII)
+ Reline.core.io_gate.instance_variable_set(:@encoding, Encoding::US_ASCII)
@config = Reline::Config.new
assert_equal true, @config.convert_meta
end
def test_encoding_is_not_ascii
- @config.reset
- Reline.core.io_gate.reset(encoding: Encoding::UTF_8)
@config = Reline::Config.new
assert_equal nil, @config.convert_meta
end
- def test_comment_line
- @config.read_lines([" #a: error\n"])
- assert_not_include @config.key_bindings, nil
- end
-
def test_invalid_keystroke
- @config.read_lines(["a: error\n"])
- assert_not_include @config.key_bindings, nil
+ @config.read_lines(<<~LINES.lines)
+ #"a": comment
+ a: error
+ "b": no-error
+ LINES
+ key_bindings = additional_key_bindings(:emacs)
+ assert_not_include key_bindings, 'a'.bytes
+ assert_not_include key_bindings, nil
+ assert_not_include key_bindings, []
+ assert_include key_bindings, 'b'.bytes
end
def test_bind_key
@@ -244,8 +254,8 @@ class Reline::Config::Test < Reline::TestCase
"\xC": "O"
LINES
keys = [0x1, 0x3, 0x4, 0x6, 0x7, 0xC]
- key_bindings = keys.to_h { |k| [[k.ord], ['O'.ord]] }
- assert_equal(key_bindings, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
+ key_bindings = keys.to_h { |k| [[k], ['O'.ord]] }
+ assert_equal(key_bindings, additional_key_bindings(:emacs))
end
def test_unclosed_if
@@ -284,9 +294,9 @@ class Reline::Config::Test < Reline::TestCase
$endif
LINES
- assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ assert_equal({[5] => :history_search_backward}, additional_key_bindings(:emacs))
+ assert_equal({}, additional_key_bindings(:vi_insert))
+ assert_equal({}, additional_key_bindings(:vi_command))
end
def test_else
@@ -298,9 +308,9 @@ class Reline::Config::Test < Reline::TestCase
$endif
LINES
- assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ assert_equal({[6] => :history_search_forward}, additional_key_bindings(:emacs))
+ assert_equal({}, additional_key_bindings(:vi_insert))
+ assert_equal({}, additional_key_bindings(:vi_command))
end
def test_if_with_invalid_mode
@@ -312,9 +322,9 @@ class Reline::Config::Test < Reline::TestCase
$endif
LINES
- assert_equal({[6] => :history_search_forward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ assert_equal({[6] => :history_search_forward}, additional_key_bindings(:emacs))
+ assert_equal({}, additional_key_bindings(:vi_insert))
+ assert_equal({}, additional_key_bindings(:vi_command))
end
def test_mode_label_differs_from_keymap_label
@@ -329,9 +339,9 @@ class Reline::Config::Test < Reline::TestCase
"\C-e": history-search-backward
$endif
LINES
- assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ assert_equal({[5] => :history_search_backward}, additional_key_bindings(:emacs))
+ assert_equal({}, additional_key_bindings(:vi_insert))
+ assert_equal({}, additional_key_bindings(:vi_command))
end
def test_if_without_else_condition
@@ -342,9 +352,9 @@ class Reline::Config::Test < Reline::TestCase
$endif
LINES
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:emacs])
- assert_equal({[5] => :history_search_backward}, @config.instance_variable_get(:@additional_key_bindings)[:vi_insert])
- assert_equal({}, @config.instance_variable_get(:@additional_key_bindings)[:vi_command])
+ assert_equal({}, additional_key_bindings(:emacs))
+ assert_equal({[5] => :history_search_backward}, additional_key_bindings(:vi_insert))
+ assert_equal({}, additional_key_bindings(:vi_command))
end
def test_default_key_bindings
@@ -355,7 +365,7 @@ class Reline::Config::Test < Reline::TestCase
LINES
expected = { 'abcd'.bytes => 'ABCD'.bytes, 'ijkl'.bytes => 'IJKL'.bytes }
- assert_equal expected, @config.key_bindings
+ assert_equal expected, registered_key_bindings(expected.keys)
end
def test_additional_key_bindings
@@ -365,7 +375,7 @@ class Reline::Config::Test < Reline::TestCase
LINES
expected = { 'ef'.bytes => 'EF'.bytes, 'gh'.bytes => 'GH'.bytes }
- assert_equal expected, @config.key_bindings
+ assert_equal expected, registered_key_bindings(expected.keys)
end
def test_additional_key_bindings_with_nesting_and_comment_out
@@ -377,7 +387,7 @@ class Reline::Config::Test < Reline::TestCase
LINES
expected = { 'ef'.bytes => 'EF'.bytes, 'gh'.bytes => 'GH'.bytes }
- assert_equal expected, @config.key_bindings
+ assert_equal expected, registered_key_bindings(expected.keys)
end
def test_additional_key_bindings_for_other_keymap
@@ -392,7 +402,7 @@ class Reline::Config::Test < Reline::TestCase
LINES
expected = { 'cd'.bytes => 'CD'.bytes }
- assert_equal expected, @config.key_bindings
+ assert_equal expected, registered_key_bindings(expected.keys)
end
def test_additional_key_bindings_for_auxiliary_emacs_keymaps
@@ -414,7 +424,19 @@ class Reline::Config::Test < Reline::TestCase
"\C-xef".bytes => 'EF'.bytes,
"\egh".bytes => 'GH'.bytes,
}
- assert_equal expected, @config.key_bindings
+ assert_equal expected, registered_key_bindings(expected.keys)
+ end
+
+ def test_key_bindings_with_reset
+ # @config.reset is called after each readline.
+ # inputrc file is read once, so key binding shouldn't be cleared by @config.reset
+ @config.add_default_key_binding('default'.bytes, 'DEFAULT'.bytes)
+ @config.read_lines(<<~'LINES'.lines)
+ "additional": "ADDITIONAL"
+ LINES
+ @config.reset
+ expected = { 'default'.bytes => 'DEFAULT'.bytes, 'additional'.bytes => 'ADDITIONAL'.bytes }
+ assert_equal expected, registered_key_bindings(expected.keys)
end
def test_history_size
@@ -447,6 +469,17 @@ class Reline::Config::Test < Reline::TestCase
ENV['INPUTRC'] = inputrc_backup
end
+ def test_inputrc_raw_value
+ @config.read_lines(<<~'LINES'.lines)
+ set editing-mode vi ignored-string
+ set vi-ins-mode-string aaa aaa
+ set vi-cmd-mode-string bbb ccc # comment
+ LINES
+ assert_equal :vi_insert, @config.instance_variable_get(:@editing_mode_label)
+ assert_equal 'aaa aaa', @config.vi_ins_mode_string
+ assert_equal 'bbb ccc # comment', @config.vi_cmd_mode_string
+ end
+
def test_inputrc_with_utf8
# This file is encoded by UTF-8 so this heredoc string is also UTF-8.
@config.read_lines(<<~'LINES'.lines)
@@ -530,4 +563,3 @@ class Reline::Config::Test < Reline::TestCase
ENV['HOME'] = home_backup
end
end
-
diff --git a/test/reline/test_key_actor_emacs.rb b/test/reline/test_key_actor_emacs.rb
index a9baf9ad37..4dddf9c890 100644
--- a/test/reline/test_key_actor_emacs.rb
+++ b/test/reline/test_key_actor_emacs.rb
@@ -1,6 +1,6 @@
require_relative 'helper'
-class Reline::KeyActor::Emacs::Test < Reline::TestCase
+class Reline::KeyActor::EmacsTest < Reline::TestCase
def setup
Reline.send(:test_mode)
@prompt = '> '
@@ -787,9 +787,22 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
input_keys('b')
input_keys("\C-i", false)
assert_line_around_cursor('foo_ba', '')
+ input_keys("\C-h")
+ input_key_by_symbol(:complete)
+ assert_line_around_cursor('foo_ba', '')
+ input_keys("\C-h", false)
+ input_key_by_symbol(:menu_complete)
+ assert_line_around_cursor('foo_bar', '')
+ input_key_by_symbol(:menu_complete)
+ assert_line_around_cursor('foo_baz', '')
+ input_keys("\C-h", false)
+ input_key_by_symbol(:menu_complete_backward)
+ assert_line_around_cursor('foo_baz', '')
+ input_key_by_symbol(:menu_complete_backward)
+ assert_line_around_cursor('foo_bar', '')
end
- def test_autocompletion_with_upward_navigation
+ def test_autocompletion
@config.autocompletion = true
@line_editor.completion_proc = proc { |word|
%w{
@@ -806,31 +819,14 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
assert_line_around_cursor('Readline', '')
input_keys("\C-i", false)
assert_line_around_cursor('Regexp', '')
- @line_editor.input_key(Reline::Key.new(:completion_journey_up, :completion_journey_up, false))
+ input_key_by_symbol(:completion_journey_up)
assert_line_around_cursor('Readline', '')
- ensure
- @config.autocompletion = false
- end
-
- def test_autocompletion_with_upward_navigation_and_menu_complete_backward
- @config.autocompletion = true
- @line_editor.completion_proc = proc { |word|
- %w{
- Readline
- Regexp
- RegexpError
- }.map { |i|
- i.encode(@encoding)
- }
- }
- input_keys('Re')
- assert_line_around_cursor('Re', '')
- input_keys("\C-i", false)
- assert_line_around_cursor('Readline', '')
- input_keys("\C-i", false)
+ input_key_by_symbol(:complete)
assert_line_around_cursor('Regexp', '')
- @line_editor.input_key(Reline::Key.new(:menu_complete_backward, :menu_complete_backward, false))
+ input_key_by_symbol(:menu_complete_backward)
assert_line_around_cursor('Readline', '')
+ input_key_by_symbol(:menu_complete)
+ assert_line_around_cursor('Regexp', '')
ensure
@config.autocompletion = false
end
@@ -1246,14 +1242,22 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12345' # new
])
# The ed_search_prev_history doesn't have default binding
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12345')
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12aaa')
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12356')
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12356')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12345', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12356', '')
+ input_key_by_symbol(:ed_search_next_history)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_prev_char)
+ input_key_by_symbol(:ed_next_char)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12aaa', '')
+ 3.times { input_key_by_symbol(:ed_prev_char) }
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12', '356')
end
def test_ed_search_prev_history_without_match
@@ -1295,18 +1299,25 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
'12345' # new
])
# The ed_search_prev_history and ed_search_next_history doesn't have default binding
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12345')
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12aaa')
- @line_editor.__send__(:ed_search_prev_history, "\C-p".ord)
- assert_line_around_cursor('', '12356')
- @line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_line_around_cursor('', '12aaa')
- @line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_line_around_cursor('', '12345')
- @line_editor.__send__(:ed_search_next_history, "\C-n".ord)
- assert_line_around_cursor('', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12345', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12356', '')
+ input_key_by_symbol(:ed_search_next_history)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_search_next_history)
+ assert_line_around_cursor('12345', '')
+ input_key_by_symbol(:ed_search_prev_history)
+ assert_line_around_cursor('12aaa', '')
+ input_key_by_symbol(:ed_prev_char)
+ input_key_by_symbol(:ed_next_char)
+ input_key_by_symbol(:ed_search_next_history)
+ assert_line_around_cursor('12aaa', '')
+ 3.times { input_key_by_symbol(:ed_prev_char) }
+ input_key_by_symbol(:ed_search_next_history)
+ assert_line_around_cursor('12', '345')
end
def test_incremental_search_history_cancel_by_symbol_key
@@ -1441,4 +1452,176 @@ class Reline::KeyActor::Emacs::Test < Reline::TestCase
@line_editor.__send__(:vi_editing_mode, nil)
assert(@config.editing_mode_is?(:vi_insert))
end
+
+ def test_undo
+ input_keys("\C-_", false)
+ assert_line_around_cursor('', '')
+ input_keys("aあb\C-h\C-h\C-h", false)
+ assert_line_around_cursor('', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('a', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('aあ', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('aあb', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('aあ', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('a', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('', '')
+ end
+
+ def test_undo_with_cursor_position
+ input_keys("abc\C-b\C-h", false)
+ assert_line_around_cursor('a', 'c')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('ab', 'c')
+ input_keys("あいう\C-b\C-h", false)
+ assert_line_around_cursor('abあ', 'うc')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('abあい', 'うc')
+ end
+
+ def test_undo_with_multiline
+ @line_editor.multiline_on
+ @line_editor.confirm_multiline_termination_proc = proc {}
+ input_keys("1\n2\n3", false)
+ assert_whole_lines(["1", "2", "3"])
+ assert_line_index(2)
+ assert_line_around_cursor('3', '')
+ input_keys("\C-p\C-h\C-h", false)
+ assert_whole_lines(["1", "3"])
+ assert_line_index(0)
+ assert_line_around_cursor('1', '')
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('', '')
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('2', '')
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2", ""])
+ assert_line_index(2)
+ assert_line_around_cursor('', '')
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2"])
+ assert_line_index(1)
+ assert_line_around_cursor('2', '')
+ end
+
+ def test_undo_with_many_times
+ str = "a" + "b" * 99
+ input_keys(str, false)
+ 100.times { input_keys("\C-_", false) }
+ assert_line_around_cursor('a', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('a', '')
+ end
+
+ def test_redo
+ input_keys("aあb", false)
+ assert_line_around_cursor('aあb', '')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('aあb', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('aあ', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('a', '')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('aあ', '')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('aあb', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('aあ', '')
+ input_keys("c", false)
+ assert_line_around_cursor('aあc', '')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('aあc', '')
+ end
+
+ def test_redo_with_cursor_position
+ input_keys("abc\C-b\C-h", false)
+ assert_line_around_cursor('a', 'c')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('a', 'c')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('ab', 'c')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor('a', 'c')
+ end
+
+ def test_redo_with_multiline
+ @line_editor.multiline_on
+ @line_editor.confirm_multiline_termination_proc = proc {}
+ input_keys("1\n2\n3", false)
+ assert_whole_lines(["1", "2", "3"])
+ assert_line_index(2)
+ assert_line_around_cursor('3', '')
+
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2", ""])
+ assert_line_index(2)
+ assert_line_around_cursor('', '')
+
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2"])
+ assert_line_index(1)
+ assert_line_around_cursor('2', '')
+
+ input_keys("\M-\C-_", false)
+ assert_whole_lines(["1", "2", ""])
+ assert_line_index(2)
+ assert_line_around_cursor('', '')
+
+ input_keys("\M-\C-_", false)
+ assert_whole_lines(["1", "2", "3"])
+ assert_line_index(2)
+ assert_line_around_cursor('3', '')
+
+ input_keys("\C-p\C-h\C-h", false)
+ assert_whole_lines(["1", "3"])
+ assert_line_index(0)
+ assert_line_around_cursor('1', '')
+
+ input_keys("\C-n", false)
+ assert_whole_lines(["1", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('3', '')
+
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('', '')
+
+ input_keys("\C-_", false)
+ assert_whole_lines(["1", "2", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('2', '')
+
+ input_keys("\M-\C-_", false)
+ assert_whole_lines(["1", "", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('', '')
+
+ input_keys("\M-\C-_", false)
+ assert_whole_lines(["1", "3"])
+ assert_line_index(1)
+ assert_line_around_cursor('3', '')
+ end
+
+ def test_redo_with_many_times
+ str = "a" + "b" * 98 + "c"
+ input_keys(str, false)
+ 100.times { input_keys("\C-_", false) }
+ assert_line_around_cursor('a', '')
+ input_keys("\C-_", false)
+ assert_line_around_cursor('a', '')
+ 100.times { input_keys("\M-\C-_", false) }
+ assert_line_around_cursor(str, '')
+ input_keys("\M-\C-_", false)
+ assert_line_around_cursor(str, '')
+ end
end
diff --git a/test/reline/test_key_actor_vi.rb b/test/reline/test_key_actor_vi.rb
index 4deae2dd83..1288b9aaa5 100644
--- a/test/reline/test_key_actor_vi.rb
+++ b/test/reline/test_key_actor_vi.rb
@@ -1,6 +1,6 @@
require_relative 'helper'
-class Reline::KeyActor::ViInsert::Test < Reline::TestCase
+class Reline::ViInsertTest < Reline::TestCase
def setup
Reline.send(:test_mode)
@prompt = '> '
@@ -13,69 +13,73 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
@line_editor.reset(@prompt, encoding: @encoding)
end
+ def editing_mode_label
+ @config.instance_variable_get(:@editing_mode_label)
+ end
+
def teardown
Reline.test_reset
end
def test_vi_command_mode
input_keys("\C-[")
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
end
def test_vi_command_mode_with_input
input_keys("abc\C-[")
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
assert_line_around_cursor('ab', 'c')
end
def test_vi_insert
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys('i')
assert_line_around_cursor('i', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys("\C-[")
assert_line_around_cursor('', 'i')
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
input_keys('i')
assert_line_around_cursor('', 'i')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
end
def test_vi_add
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys('a')
assert_line_around_cursor('a', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys("\C-[")
assert_line_around_cursor('', 'a')
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
input_keys('a')
assert_line_around_cursor('a', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
end
def test_vi_insert_at_bol
input_keys('I')
assert_line_around_cursor('I', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys("12345\C-[hh")
assert_line_around_cursor('I12', '345')
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
input_keys('I')
assert_line_around_cursor('', 'I12345')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
end
def test_vi_add_at_eol
input_keys('A')
assert_line_around_cursor('A', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
input_keys("12345\C-[hh")
assert_line_around_cursor('A12', '345')
- assert_instance_of(Reline::KeyActor::ViCommand, @config.editing_mode)
+ assert_equal(:vi_command, editing_mode_label)
input_keys('A')
assert_line_around_cursor('A12345', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
end
def test_ed_insert_one
@@ -901,11 +905,11 @@ class Reline::KeyActor::ViInsert::Test < Reline::TestCase
assert_line_around_cursor('abc', '')
input_keys("\C-[0C")
assert_line_around_cursor('', '')
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
end
def test_vi_motion_operators
- assert_instance_of(Reline::KeyActor::ViInsert, @config.editing_mode)
+ assert_equal(:vi_insert, editing_mode_label)
assert_nothing_raised do
input_keys("test = { foo: bar }\C-[BBBldt}b")
diff --git a/test/reline/test_key_stroke.rb b/test/reline/test_key_stroke.rb
index cd205c7d9e..ec70a05957 100644
--- a/test/reline/test_key_stroke.rb
+++ b/test/reline/test_key_stroke.rb
@@ -24,16 +24,14 @@ class Reline::KeyStroke::Test < Reline::TestCase
config.add_default_key_binding(key.bytes, func.bytes)
end
stroke = Reline::KeyStroke.new(config)
- assert_equal(:matching, stroke.match_status("a".bytes))
- assert_equal(:matching, stroke.match_status("ab".bytes))
- assert_equal(:matched, stroke.match_status("abc".bytes))
- assert_equal(:matched, stroke.match_status("abz".bytes))
- assert_equal(:matched, stroke.match_status("abx".bytes))
- assert_equal(:matched, stroke.match_status("ac".bytes))
- assert_equal(:matched, stroke.match_status("aa".bytes))
- assert_equal(:matched, stroke.match_status("x".bytes))
- assert_equal(:unmatched, stroke.match_status("m".bytes))
- assert_equal(:matched, stroke.match_status("abzwabk".bytes))
+ assert_equal(Reline::KeyStroke::MATCHING_MATCHED, stroke.match_status("a".bytes))
+ assert_equal(Reline::KeyStroke::MATCHING_MATCHED, stroke.match_status("ab".bytes))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status("abc".bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status("abz".bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status("abcx".bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status("aa".bytes))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status("x".bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status("xa".bytes))
end
def test_match_unknown
@@ -47,12 +45,15 @@ class Reline::KeyStroke::Test < Reline::TestCase
"\e[1;1R", # Cursor position report
"\e[15~", # F5
"\eOP", # F1
- "\e\e[A" # Option+Up
+ "\e\e[A", # Option+Up
+ "\eX",
+ "\e\eX"
]
sequences.each do |seq|
- assert_equal(:matched, stroke.match_status(seq.bytes))
- (1...seq.size).each do |i|
- assert_equal(:matching, stroke.match_status(seq.bytes.take(i)))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status(seq.bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status(seq.bytes + [32]))
+ (2...seq.size).each do |i|
+ assert_equal(Reline::KeyStroke::MATCHING, stroke.match_status(seq.bytes.take(i)))
end
end
end
@@ -61,16 +62,18 @@ class Reline::KeyStroke::Test < Reline::TestCase
config = Reline::Config.new
{
'abc' => '123',
+ 'ab' => '456'
}.each_pair do |key, func|
config.add_default_key_binding(key.bytes, func.bytes)
end
stroke = Reline::KeyStroke.new(config)
- assert_equal('123'.bytes, stroke.expand('abc'.bytes))
+ assert_equal(['123'.bytes.map { |c| Reline::Key.new(c, c, false) }, 'de'.bytes], stroke.expand('abcde'.bytes))
+ assert_equal(['456'.bytes.map { |c| Reline::Key.new(c, c, false) }, 'de'.bytes], stroke.expand('abde'.bytes))
# CSI sequence
- assert_equal([:ed_unassigned] + 'bc'.bytes, stroke.expand("\e[1;2;3;4;5abc".bytes))
- assert_equal([:ed_unassigned] + 'BC'.bytes, stroke.expand("\e\e[ABC".bytes))
+ assert_equal([[], 'bc'.bytes], stroke.expand("\e[1;2;3;4;5abc".bytes))
+ assert_equal([[], 'BC'.bytes], stroke.expand("\e\e[ABC".bytes))
# SS3 sequence
- assert_equal([:ed_unassigned] + 'QR'.bytes, stroke.expand("\eOPQR".bytes))
+ assert_equal([[], 'QR'.bytes], stroke.expand("\eOPQR".bytes))
end
def test_oneshot_key_bindings
@@ -81,25 +84,22 @@ class Reline::KeyStroke::Test < Reline::TestCase
config.add_default_key_binding(key.bytes, func.bytes)
end
stroke = Reline::KeyStroke.new(config)
- assert_equal(:unmatched, stroke.match_status('zzz'.bytes))
- assert_equal(:matched, stroke.match_status('abc'.bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status('zzz'.bytes))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status('abc'.bytes))
end
def test_with_reline_key
config = Reline::Config.new
{
- [
- Reline::Key.new(100, 228, true), # Alt+d
- Reline::Key.new(97, 97, false) # a
- ] => 'abc',
+ "\eda".bytes => 'abc', # Alt+d a
[195, 164] => 'def'
}.each_pair do |key, func|
config.add_oneshot_key_binding(key, func.bytes)
end
stroke = Reline::KeyStroke.new(config)
- assert_equal(:unmatched, stroke.match_status('da'.bytes))
- assert_equal(:matched, stroke.match_status("\M-da".bytes))
- assert_equal(:unmatched, stroke.match_status([32, 195, 164]))
- assert_equal(:matched, stroke.match_status([195, 164]))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status('da'.bytes))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status("\eda".bytes))
+ assert_equal(Reline::KeyStroke::UNMATCHED, stroke.match_status([32, 195, 164]))
+ assert_equal(Reline::KeyStroke::MATCHED, stroke.match_status([195, 164]))
end
end
diff --git a/test/reline/test_line_editor.rb b/test/reline/test_line_editor.rb
index 7a38ecd596..1859da8199 100644
--- a/test/reline/test_line_editor.rb
+++ b/test/reline/test_line_editor.rb
@@ -4,14 +4,12 @@ require 'stringio'
class Reline::LineEditor
class RenderLineDifferentialTest < Reline::TestCase
- module TestIO
- RESET_COLOR = "\e[0m"
-
- def self.move_cursor_column(col)
+ class TestIO < Reline::IO
+ def move_cursor_column(col)
@output << "[COL_#{col}]"
end
- def self.erase_after_cursor
+ def erase_after_cursor
@output << '[ERASE]'
end
end
@@ -24,7 +22,7 @@ class Reline::LineEditor
@line_editor.instance_variable_set(:@screen_size, [24, 80])
@line_editor.instance_variable_set(:@output, @output)
Reline.send(:remove_const, :IOGate)
- Reline.const_set(:IOGate, TestIO)
+ Reline.const_set(:IOGate, TestIO.new)
Reline::IOGate.instance_variable_set(:@output, @output)
ensure
$VERBOSE = verbose
diff --git a/test/reline/test_reline.rb b/test/reline/test_reline.rb
index 40c880c11f..f2feab684d 100644
--- a/test/reline/test_reline.rb
+++ b/test/reline/test_reline.rb
@@ -303,12 +303,12 @@ class Reline::Test < Reline::TestCase
def test_vi_editing_mode
Reline.vi_editing_mode
- assert_equal(Reline::KeyActor::ViInsert, Reline.core.config.editing_mode.class)
+ assert_equal(:vi_insert, Reline.core.config.instance_variable_get(:@editing_mode_label))
end
def test_emacs_editing_mode
Reline.emacs_editing_mode
- assert_equal(Reline::KeyActor::Emacs, Reline.core.config.editing_mode.class)
+ assert_equal(:emacs, Reline.core.config.instance_variable_get(:@editing_mode_label))
end
def test_add_dialog_proc
@@ -375,13 +375,67 @@ class Reline::Test < Reline::TestCase
def test_dumb_terminal
lib = File.expand_path("../../lib", __dir__)
out = IO.popen([{"TERM"=>"dumb"}, Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", "p Reline.core.io_gate"], &:read)
- assert_equal("Reline::GeneralIO", out.chomp)
+ assert_match(/#<Reline::Dumb/, out.chomp)
+ end
+
+ def test_print_prompt_before_everything_else
+ pend if win?
+ lib = File.expand_path("../../lib", __dir__)
+ code = "p Reline::IOGate.class; p Reline.readline 'prompt> '"
+ out = IO.popen([Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", code], "r+") do |io|
+ io.write "abc\n"
+ io.close_write
+ io.read
+ end
+ assert_match(/\AReline::ANSI\nprompt> /, out)
+ end
+
+ def test_read_eof_returns_input
+ pend if win?
+ lib = File.expand_path("../../lib", __dir__)
+ code = "p result: Reline.readline"
+ out = IO.popen([Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", code], "r+") do |io|
+ io.write "a\C-a"
+ io.close_write
+ io.read
+ end
+ assert_include(out, '{:result=>"a"}')
+ end
+
+ def test_read_eof_returns_nil_if_empty
+ pend if win?
+ lib = File.expand_path("../../lib", __dir__)
+ code = "p result: Reline.readline"
+ out = IO.popen([Reline.test_rubybin, "-I#{lib}", "-rreline", "-e", code], "r+") do |io|
+ io.write "a\C-h"
+ io.close_write
+ io.read
+ end
+ assert_include(out, '{:result=>nil}')
+ end
+
+ def test_require_reline_should_not_trigger_winsize
+ pend if win?
+ lib = File.expand_path("../../lib", __dir__)
+ code = <<~RUBY
+ require "io/console"
+ def STDIN.tty?; true; end
+ def STDOUT.tty?; true; end
+ def STDIN.winsize; raise; end
+ require("reline") && p(Reline.core.io_gate)
+ RUBY
+ out = IO.popen([{}, Reline.test_rubybin, "-I#{lib}", "-e", code], &:read)
+ assert_include(out.chomp, "Reline::ANSI")
+ end
+
+ def win?
+ /mswin|mingw/.match?(RUBY_PLATFORM)
end
def get_reline_encoding
if encoding = Reline.core.encoding
encoding
- elsif RUBY_PLATFORM =~ /mswin|mingw/
+ elsif win?
Encoding::UTF_8
else
Encoding::default_external
diff --git a/test/reline/test_reline_key.rb b/test/reline/test_reline_key.rb
index 7f9a11394a..1e6b9fcb6c 100644
--- a/test/reline/test_reline_key.rb
+++ b/test/reline/test_reline_key.rb
@@ -2,53 +2,10 @@ require_relative 'helper'
require "reline"
class Reline::TestKey < Reline::TestCase
- def setup
- Reline.test_mode
- end
-
- def teardown
- Reline.test_reset
- end
-
- def test_match_key
- assert(Reline::Key.new(1, 2, false).match?(Reline::Key.new(1, 2, false)))
- assert(Reline::Key.new(1, 2, false).match?(Reline::Key.new(nil, 2, false)))
- assert(Reline::Key.new(1, 2, false).match?(Reline::Key.new(1, 2, nil)))
-
- assert(Reline::Key.new(nil, 2, false).match?(Reline::Key.new(nil, 2, false)))
- assert(Reline::Key.new(1, nil, false).match?(Reline::Key.new(1, nil, false)))
- assert(Reline::Key.new(1, 2, nil).match?(Reline::Key.new(1, 2, nil)))
-
- assert(Reline::Key.new(nil, 2, false).match?(Reline::Key.new(nil, 2, false)))
- assert(Reline::Key.new(1, nil, false).match?(Reline::Key.new(1, nil, false)))
- assert(Reline::Key.new(1, 2, nil).match?(Reline::Key.new(1, 2, nil)))
-
- assert(!Reline::Key.new(1, 2, false).match?(Reline::Key.new(3, 1, false)))
- assert(!Reline::Key.new(1, 2, false).match?(Reline::Key.new(1, 3, false)))
- assert(!Reline::Key.new(1, 2, false).match?(Reline::Key.new(1, 3, true)))
- end
-
- def test_match_integer
- assert(Reline::Key.new(1, 2, false).match?(2))
- assert(Reline::Key.new(nil, 2, false).match?(2))
- assert(Reline::Key.new(1, nil, false).match?(1))
-
- assert(!Reline::Key.new(1, 2, false).match?(1))
- assert(!Reline::Key.new(1, nil, false).match?(2))
- assert(!Reline::Key.new(nil, nil, false).match?(1))
- end
-
def test_match_symbol
- assert(Reline::Key.new(:key1, :key2, false).match?(:key2))
- assert(Reline::Key.new(:key1, nil, false).match?(:key1))
-
- assert(!Reline::Key.new(:key1, :key2, false).match?(:key1))
- assert(!Reline::Key.new(:key1, nil, false).match?(:key2))
- assert(!Reline::Key.new(nil, nil, false).match?(:key1))
- end
-
- def test_match_other
- assert(!Reline::Key.new(:key1, 2, false).match?("key1"))
- assert(!Reline::Key.new(nil, nil, false).match?(nil))
+ assert(Reline::Key.new(:key1, :key1, false).match?(:key1))
+ refute(Reline::Key.new(:key1, :key1, false).match?(:key2))
+ refute(Reline::Key.new(:key1, :key1, false).match?(nil))
+ refute(Reline::Key.new(1, 1, false).match?(:key1))
end
end
diff --git a/test/reline/test_within_pipe.rb b/test/reline/test_within_pipe.rb
index a42ca755fc..4f05255301 100644
--- a/test/reline/test_within_pipe.rb
+++ b/test/reline/test_within_pipe.rb
@@ -23,7 +23,6 @@ class Reline::WithinPipeTest < Reline::TestCase
@reader.close
@output_writer.close
@config.reset
- @config.reset_default_key_bindings
Reline.test_reset
end
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 74798c338f..f119d686cd 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -543,15 +543,10 @@ begin
EOC
end
- def test_enable_bracketed_paste
+ def test_bracketed_paste
omit if Reline.core.io_gate.win?
- write_inputrc <<~LINES
- set enable-bracketed-paste on
- LINES
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
- write("\e[200~,")
- write("def hoge\n 3\nend")
- write("\e[200~.")
+ write("\e[200~def hoge\r\t3\rend\e[201~")
close
assert_screen(<<~EOC)
Multiline REPL.
@@ -561,6 +556,35 @@ begin
EOC
end
+ def test_bracketed_paste_with_undo
+ omit if Reline.core.io_gate.win?
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+ write("abc")
+ write("\e[200~def hoge\r\t3\rend\e[201~")
+ write("\C-_")
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> abc
+ EOC
+ end
+
+ def test_bracketed_paste_with_redo
+ omit if Reline.core.io_gate.win?
+ start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+ write("abc")
+ write("\e[200~def hoge\r\t3\rend\e[201~")
+ write("\C-_")
+ write("\M-\C-_")
+ close
+ assert_screen(<<~EOC)
+ Multiline REPL.
+ prompt> abcdef hoge
+ prompt> 3
+ prompt> end
+ EOC
+ end
+
def test_backspace_until_returns_to_initial
start_terminal(5, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
write("ABC")
@@ -945,6 +969,18 @@ begin
EOC
end
+ def test_nontty
+ omit if Reline.core.io_gate.win?
+ cmd = %Q{ruby -e 'puts(%Q{ello\C-ah\C-e})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })' | ruby -e 'print STDIN.read'}
+ start_terminal(40, 50, ['bash', '-c', cmd])
+ sleep 1
+ close rescue nil
+ assert_screen(<<~'EOC')
+ > hello
+ "hello"
+ EOC
+ end
+
def test_eof_with_newline
omit if Reline.core.io_gate.win?
cmd = %Q{ruby -e 'print(%Q{abc def \\e\\r})' | ruby -I#{@pwd}/lib -rreline -e 'p Reline.readline(%{> })'}
diff --git a/test/ripper/test_lexer.rb b/test/ripper/test_lexer.rb
index 7d62a7ee28..64b4336375 100644
--- a/test/ripper/test_lexer.rb
+++ b/test/ripper/test_lexer.rb
@@ -253,18 +253,31 @@ world"
assert_equal(code, Ripper.tokenize(code).join(""), bug)
end
+ InvalidHeredocInsideBlockParam = <<~CODE
+ a do |b
+ <<-C
+ C
+ |
+ end
+ CODE
+
def test_heredoc_inside_block_param
bug = '[Bug #19399]'
- code = <<~CODE
- a do |b
- <<-C
- C
- |
- end
- CODE
+ code = InvalidHeredocInsideBlockParam
assert_equal(code, Ripper.tokenize(code).join(""), bug)
end
+ def test_heredoc_no_memory_leak
+ assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}", rss: true)
+ require "ripper"
+ source = "" #{InvalidHeredocInsideBlockParam.dump}
+ begin;
+ 400_000.times do
+ Ripper.new(source).parse
+ end
+ end;
+ end
+
def test_heredoc_unterminated_interpolation
code = <<~'HEREDOC'
<<A+1
@@ -302,9 +315,8 @@ world"
[[6, 2], :on_tstring_content, "3\n", state(:EXPR_BEG)],
[[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)],
]
- assert_equal(code, Ripper.tokenize(code).join(""))
- assert_equal(expected, result = Ripper.lex(code),
- proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}})
+
+ assert_lexer(expected, code)
code = <<~'HEREDOC'
<<-H1
@@ -330,6 +342,55 @@ world"
[[6, 0], :on_tstring_content, " 3\n", state(:EXPR_BEG)],
[[7, 0], :on_heredoc_end, "H1\n", state(:EXPR_BEG)],
]
+
+ assert_lexer(expected, code)
+ end
+
+ def test_invalid_escape_ctrl_mbchar
+ code = %["\\C-\u{3042}"]
+ expected = [
+ [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)],
+ [[1, 1], :on_tstring_content, "\\C-\u{3042}", state(:EXPR_BEG)],
+ [[1, 7], :on_tstring_end, '"', state(:EXPR_END)],
+ ]
+
+ assert_lexer(expected, code)
+ end
+
+ def test_invalid_escape_meta_mbchar
+ code = %["\\M-\u{3042}"]
+ expected = [
+ [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)],
+ [[1, 1], :on_tstring_content, "\\M-\u{3042}", state(:EXPR_BEG)],
+ [[1, 7], :on_tstring_end, '"', state(:EXPR_END)],
+ ]
+
+ assert_lexer(expected, code)
+ end
+
+ def test_invalid_escape_meta_ctrl_mbchar
+ code = %["\\M-\\C-\u{3042}"]
+ expected = [
+ [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)],
+ [[1, 1], :on_tstring_content, "\\M-\\C-\u{3042}", state(:EXPR_BEG)],
+ [[1, 10], :on_tstring_end, '"', state(:EXPR_END)],
+ ]
+
+ assert_lexer(expected, code)
+ end
+
+ def test_invalid_escape_ctrl_meta_mbchar
+ code = %["\\C-\\M-\u{3042}"]
+ expected = [
+ [[1, 0], :on_tstring_beg, '"', state(:EXPR_BEG)],
+ [[1, 1], :on_tstring_content, "\\C-\\M-\u{3042}", state(:EXPR_BEG)],
+ [[1, 10], :on_tstring_end, '"', state(:EXPR_END)],
+ ]
+
+ assert_lexer(expected, code)
+ end
+
+ def assert_lexer(expected, code)
assert_equal(code, Ripper.tokenize(code).join(""))
assert_equal(expected, result = Ripper.lex(code),
proc {expected.zip(result) {|e, r| break diff(e, r) unless e == r}})
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index cbae6e7ed5..5434acb523 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -267,17 +267,29 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
end
def test_assign_error_backref
- thru_assign_error = false
+ errors = []
result =
- parse('$` = 1', :on_assign_error) {thru_assign_error = true}
- assert_equal true, thru_assign_error
- assert_equal '[assign(assign_error(var_field($`)),1)]', result
+ parse('$& = 1', %i[on_assign_error compile_error]) {|e, *| errors << e}
+ assert_equal %i[on_assign_error], errors
+ assert_equal '[assign(assign_error(var_field($&)),1)]', result
- thru_assign_error = false
+ errors = []
result =
- parse('$`, _ = 1', :on_assign_error) {thru_assign_error = true}
- assert_equal true, thru_assign_error
- assert_equal '[massign([assign_error(var_field($`)),var_field(_)],1)]', result
+ parse('$&, _ = 1', %i[on_assign_error compile_error]) {|e, *| errors << e}
+ assert_equal %i[on_assign_error], errors
+ assert_equal '[massign([assign_error(var_field($&)),var_field(_)],1)]', result
+
+ errors = []
+ result =
+ parse('$& += 1', %i[on_assign_error compile_error]) {|e, *| errors << e}
+ assert_equal %i[on_assign_error], errors
+ assert_equal '[assign_error(opassign(var_field($&),+=,1))]', result
+
+ errors = []
+ result =
+ parse('$& += cmd 1, 2', %i[on_assign_error compile_error]) {|e, *| errors << e}
+ assert_equal %i[on_assign_error], errors
+ assert_equal '[assign_error(opassign(var_field($&),+=,command(cmd,[1,2])))]', result
end
def test_assign_error_const_qualified
@@ -1682,8 +1694,8 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
else
end
STR
- assert_match(/duplicated 'when' clause/, fmt)
- assert_equal([3], args)
+ assert_match(/duplicates 'when' clause/, fmt)
+ assert_equal([4, 3], args)
end
def test_warn_duplicated_hash_keys
diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb
index 48348c0fbd..fbe0548899 100644
--- a/test/ruby/test_allocation.rb
+++ b/test/ruby/test_allocation.rb
@@ -25,6 +25,10 @@ class TestAllocation < Test::Unit::TestCase
empty_array = empty_array = []
empty_hash = empty_hash = {}
array1 = array1 = [1]
+ r2k_array = r2k_array = [Hash.ruby2_keywords_hash(a: 3)]
+ r2k_array1 = r2k_array1 = [1, Hash.ruby2_keywords_hash(a: 3)]
+ r2k_empty_array = r2k_empty_array = [Hash.ruby2_keywords_hash({})]
+ r2k_empty_array1 = r2k_empty_array1 = [1, Hash.ruby2_keywords_hash({})]
hash1 = hash1 = {a: 2}
nill = nill = nil
block = block = lambda{}
@@ -95,6 +99,8 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 0, "none(*empty_array, *empty_array#{block})")
check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})")
check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})")
+
+ check_allocations(0, 0, "none(*r2k_empty_array#{block})")
RUBY
end
@@ -114,6 +120,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "required(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(0, 0, "required(*r2k_empty_array1#{block})")
+ check_allocations(0, 1, "required(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
RUBY
@@ -135,6 +144,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(0, 0, "optional(*r2k_empty_array#{block})")
+ check_allocations(0, 0, "optional(*r2k_empty_array1#{block})")
+ check_allocations(0, 1, "optional(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
RUBY
@@ -166,6 +179,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat(*r2k_empty_array#{block})")
+ check_allocations(1, 0, "splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat(*r2k_array#{block})")
+ check_allocations(1, 1, "splat(*r2k_array1#{block})")
RUBY
end
@@ -195,6 +213,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "req_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 0, "req_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "req_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "req_splat(*r2k_array1#{block})")
RUBY
end
@@ -224,6 +246,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_post(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 0, "splat_post(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_post(*r2k_array#{block})")
+ check_allocations(1, 1, "splat_post(*r2k_array1#{block})")
RUBY
end
@@ -251,6 +277,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 0, "keyword(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})")
@@ -281,6 +310,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword_splat(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
@@ -311,6 +343,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
@@ -350,6 +385,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "required_and_keyword(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
@@ -397,6 +435,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_and_keyword(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})")
+
+ check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*r2k_array#{block})")
+ check_allocations(1, 0, "splat_and_keyword(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*r2k_array1#{block})")
RUBY
end
@@ -433,6 +476,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*r2k_array1#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
@@ -480,6 +526,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -521,6 +572,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -562,6 +618,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -603,6 +664,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})")
RUBY
end
@@ -644,6 +710,61 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})")
+ RUBY
+ end
+
+ def test_ruby2_keywords
+ check_allocations(<<~RUBY)
+ def self.r2k(*a#{block}); end
+ singleton_class.send(:ruby2_keywords, :r2k)
+
+ check_allocations(1, 1, "r2k(1, a: 2#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "r2k(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "r2k(1, **nil#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "r2k(1, *empty_array#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(*array1, a: 2#{block})")
+
+ check_allocations(1, 0, "r2k(*array1, **nill#{block})")
+ check_allocations(1, 1, "r2k(*array1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, **hash1#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "r2k(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "r2k(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "r2k(*array1, **nil#{block})")
+
+ check_allocations(1, 0, "r2k(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "r2k(*r2k_array#{block})")
+ unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+ # YJIT may or may not allocate depending on arch?
+ check_allocations(1, 0, "r2k(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "r2k(*r2k_array1#{block})")
+ end
RUBY
end
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 49c1278340..45c6b63963 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -337,6 +337,8 @@ class TestAst < Test::Unit::TestCase
end
def test_node_id_for_location
+ omit if compiling_with_prism?
+
exception = begin
raise
rescue => e
@@ -356,6 +358,8 @@ class TestAst < Test::Unit::TestCase
end
def test_of_proc_and_method
+ omit if compiling_with_prism?
+
proc = Proc.new { 1 + 2 }
method = self.method(__method__)
@@ -385,6 +389,8 @@ class TestAst < Test::Unit::TestCase
end
def test_of_backtrace_location
+ omit if compiling_with_prism?
+
backtrace_location, lineno = sample_backtrace_location
node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
@@ -396,6 +402,8 @@ class TestAst < Test::Unit::TestCase
end
def test_of_proc_and_method_under_eval
+ omit if compiling_with_prism?
+
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = false
@@ -425,6 +433,7 @@ class TestAst < Test::Unit::TestCase
end
def test_of_proc_and_method_under_eval_with_keep_script_lines
+ omit if compiling_with_prism?
pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO
keep_script_lines_back = RubyVM.keep_script_lines
@@ -456,6 +465,8 @@ class TestAst < Test::Unit::TestCase
end
def test_of_backtrace_location_under_eval
+ omit if compiling_with_prism?
+
keep_script_lines_back = RubyVM.keep_script_lines
RubyVM.keep_script_lines = false
@@ -474,6 +485,7 @@ class TestAst < Test::Unit::TestCase
end
def test_of_backtrace_location_under_eval_with_keep_script_lines
+ omit if compiling_with_prism?
pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO
keep_script_lines_back = RubyVM.keep_script_lines
@@ -736,6 +748,8 @@ dummy
end
def test_keep_script_lines_for_of
+ omit if compiling_with_prism?
+
proc = Proc.new { 1 + 2 }
method = self.method(__method__)
@@ -1247,6 +1261,15 @@ dummy
end;
end
+ private
+
+ # We can't revisit instruction sequences to find node ids if the prism
+ # compiler was used instead of the parse.y compiler. In that case, we'll omit
+ # some tests.
+ def compiling_with_prism?
+ RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
+ end
+
def assert_error_tolerant(src, expected, keep_tokens: false)
begin
verbose_bak, $VERBOSE = $VERBOSE, false
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index 1f882c6cb9..1858793952 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -821,5 +821,11 @@ class TestBignum < Test::Unit::TestCase
assert_nil(T1024P.infinite?)
assert_nil((-T1024P).infinite?)
end
+
+ def test_gmp_version
+ if RbConfig::CONFIG.fetch('configure_args').include?("'--with-gmp'")
+ assert_kind_of(String, Integer::GMP_VERSION)
+ end
+ end if ENV['GITHUB_WORKFLOW'] == 'Compilations'
end
end
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index a52b75c267..ced1eaf5e9 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -136,7 +136,7 @@ class TestCall < Test::Unit::TestCase
# Prevent "assigned but unused variable" warnings
_ = [h, a, kw, b]
- message = /keyword arg given in index/
+ message = /keyword arg given in index assignment/
# +=, without block, non-popped
assert_syntax_error(%q{h[**kw] += 1}, message)
@@ -270,7 +270,7 @@ class TestCall < Test::Unit::TestCase
def o.[](...) 2 end
def o.[]=(...) end
- message = /keyword arg given in index/
+ message = /keyword arg given in index assignment/
assert_syntax_error(%q{o[kw: 1] += 1}, message)
assert_syntax_error(%q{o[**o] += 1}, message)
@@ -292,7 +292,7 @@ class TestCall < Test::Unit::TestCase
def []=(*a, **b) @set = [a, b] end
end.new
- message = /keyword arg given in index/
+ message = /keyword arg given in index assignment/
a = []
kw = {}
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index aa10566bfa..9fa96f8a7c 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -460,6 +460,39 @@ class TestFile < Test::Unit::TestCase
end
end
+ def test_initialize
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ path = File.join(tmpdir, "foo")
+
+ assert_raise(Errno::ENOENT) {File.new(path)}
+ f = File.new(path, "w")
+ f.write("FOO\n")
+ f.close
+ f = File.new(path)
+ data = f.read
+ f.close
+ assert_equal("FOO\n", data)
+
+ f = File.new(path, File::WRONLY)
+ f.write("BAR\n")
+ f.close
+ f = File.new(path, File::RDONLY)
+ data = f.read
+ f.close
+ assert_equal("BAR\n", data)
+
+ data = File.open(path) {|file|
+ File.new(file.fileno, mode: File::RDONLY, autoclose: false).read
+ }
+ assert_equal("BAR\n", data)
+
+ data = File.open(path) {|file|
+ File.new(file.fileno, File::RDONLY, autoclose: false).read
+ }
+ assert_equal("BAR\n", data)
+ end
+ end
+
def test_file_open_newline_option
Dir.mktmpdir(__method__.to_s) do |tmpdir|
path = File.join(tmpdir, "foo")
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 39b001c3d0..491746fe83 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -648,7 +648,7 @@ class TestGc < Test::Unit::TestCase
def test_thrashing_for_young_objects
# This test prevents bugs like [Bug #18929]
- assert_separately([], __FILE__, __LINE__, <<-'RUBY')
+ assert_separately([], __FILE__, __LINE__, <<-'RUBY', timeout: 60)
# Grow the heap
@ary = 100_000.times.map { Object.new }
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
index f47c0b046b..c331968b3d 100644
--- a/test/ruby/test_gc_compact.rb
+++ b/test/ruby/test_gc_compact.rb
@@ -146,7 +146,7 @@ class TestGCCompact < Test::Unit::TestCase
end
def test_ast_compacts
- assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
def walk_ast ast
children = ast.children.grep(RubyVM::AbstractSyntaxTree::Node)
@@ -185,7 +185,7 @@ class TestGCCompact < Test::Unit::TestCase
end
def test_updating_references_for_heap_allocated_shared_arrays
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ary = []
50.times { |i| ary << i }
@@ -209,7 +209,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_updating_references_for_embed_shared_arrays
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ary = Array.new(50)
50.times { |i| ary[i] = i }
@@ -233,7 +233,7 @@ class TestGCCompact < Test::Unit::TestCase
end
def test_updating_references_for_heap_allocated_frozen_shared_arrays
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ary = []
50.times { |i| ary << i }
@@ -258,7 +258,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_updating_references_for_embed_frozen_shared_arrays
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ary = Array.new(50)
50.times { |i| ary[i] = i }
@@ -286,7 +286,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_arrays_down_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ARY_COUNT = 50000
@@ -308,7 +308,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_arrays_up_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
ARY_COUNT = 50000
@@ -332,7 +332,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_objects_between_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 60)
begin;
class Foo
def add_ivars
@@ -364,7 +364,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_strings_up_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30)
begin;
STR_COUNT = 50000
@@ -385,7 +385,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_strings_down_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30)
begin;
STR_COUNT = 50000
@@ -407,7 +407,7 @@ class TestGCCompact < Test::Unit::TestCase
# AR and ST hashes are in the same size pool on 32 bit
omit unless RbConfig::SIZEOF["uint64_t"] <= RbConfig::SIZEOF["void*"]
- assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 30)
begin;
HASH_COUNT = 50000
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 476d9f882f..ce81286c4d 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -2929,6 +2929,15 @@ class TestIO < Test::Unit::TestCase
f.close
assert_equal("FOO\n", File.read(t.path))
+
+ fd = IO.sysopen(t.path)
+ %w[w r+ w+ a+].each do |mode|
+ assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
+ end
+ f = IO.new(fd, "r")
+ data = f.read
+ f.close
+ assert_equal("FOO\n", data)
}
end
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index d2a39e673f..1c7de5bb2b 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -167,6 +167,14 @@ class TestISeq < Test::Unit::TestCase
end
end
+ def test_ractor_shareable_value_frozen_core
+ iseq = RubyVM::InstructionSequence.compile(<<~'RUBY')
+ # shareable_constant_value: literal
+ REGEX = /#{}/ # [Bug #20569]
+ RUBY
+ assert_includes iseq.to_binary, "REGEX".b
+ end
+
def test_disasm_encoding
src = +"\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm
@@ -347,11 +355,17 @@ class TestISeq < Test::Unit::TestCase
end
end
assert_equal([m1, e1.message], [m2, e2.message], feature11951)
- message = e1.message.each_line
- message.with_index(1) do |line, i|
- next if /^ / =~ line
- assert_send([line, :start_with?, __FILE__],
- proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")})
+
+ if e1.message.lines[0] == "#{__FILE__}:#{line}: syntax errors found\n"
+ # Prism lays out the error messages in line with the source, so the
+ # following assertions do not make sense in that context.
+ else
+ message = e1.message.each_line
+ message.with_index(1) do |line, i|
+ next if /^ / =~ line
+ assert_send([line, :start_with?, __FILE__],
+ proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")})
+ end
end
end
@@ -463,7 +477,7 @@ class TestISeq < Test::Unit::TestCase
["<class:C>@1",
["bar@10", ["block in bar@11",
["block (2 levels) in bar@12"]]],
- ["foo@2", ["ensure in foo@2"],
+ ["foo@2", ["ensure in foo@7"],
["rescue in foo@4"]]],
["<class:D>@17"]]
@@ -496,7 +510,7 @@ class TestISeq < Test::Unit::TestCase
[4, :line],
[7, :line],
[9, :return]]],
- [["ensure in foo@2", [[7, :line]]]],
+ [["ensure in foo@7", [[7, :line]]]],
[["rescue in foo@4", [[5, :line],
[5, :rescue]]]]]],
[["<class:D>@17", [[17, :class],
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index c6154af1f6..8732d8f0d0 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -640,11 +640,20 @@ class TestRubyLiteral < Test::Unit::TestCase
end
begin
r2 = eval(s)
- rescue NameError, SyntaxError
+ rescue ArgumentError
+ # Debug log for a random failure: ArgumentError: SyntaxError#path changed
+ $stderr.puts "TestRubyLiteral#test_float failed: %p" % s
+ raise
+ rescue SyntaxError => e
+ r2 = :err
+ rescue NameError
r2 = :err
end
r2 = :err if Range === r2
- assert_equal(r1, r2, "Float(#{s.inspect}) != eval(#{s.inspect})")
+ s = s.inspect
+ mesg = "Float(#{s}) != eval(#{s})"
+ mesg << ":" << e.message if e
+ assert_equal(r1, r2, mesg)
}
}
}
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index 79d9577737..bcd8892f23 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -570,13 +570,19 @@ class TestMarshal < Test::Unit::TestCase
def test_class_ivar
assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
- assert_not_operator(TestClass, :instance_variable_defined?, :@bug)
+ assert_not_operator(TestClass, :instance_variable_defined?, :@ivar_bug)
+
+ assert_raise(TypeError) {Marshal.load("\x04\x08[\x07c\x1bTestMarshal::TestClassI@\x06\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestClass, :instance_variable_defined?, :@ivar_bug)
end
def test_module_ivar
assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
- assert_not_operator(TestModule, :instance_variable_defined?, :@bug)
+ assert_not_operator(TestModule, :instance_variable_defined?, :@ivar_bug)
+
+ assert_raise(TypeError) {Marshal.load("\x04\x08[\x07m\x1cTestMarshal::TestModuleI@\x06\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestModule, :instance_variable_defined?, :@ivar_bug)
end
class TestForRespondToFalse
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 75d8d909d7..370b7351c2 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -3163,6 +3163,19 @@ class TestModule < Test::Unit::TestCase
end;
end
+ def test_define_method_changes_visibility_with_existing_method_bug_19749
+ c = Class.new do
+ def a; end
+ private def b; end
+
+ define_method(:b, instance_method(:b))
+ private
+ define_method(:a, instance_method(:a))
+ end
+ assert_equal([:b], c.public_instance_methods(false))
+ assert_equal([:a], c.private_instance_methods(false))
+ end
+
def test_define_method_with_unbound_method
# Passing an UnboundMethod to define_method succeeds if it is from an ancestor
assert_nothing_raised do
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index 1ce46e8916..a0c66c188b 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -895,4 +895,22 @@ EXPECTED
}
assert_equal [nil], "a".unpack("C", offset: 1)
end
+
+ def test_monkey_pack
+ assert_separately([], <<-'end;')
+ $-w = false
+ class Array
+ alias :old_pack :pack
+ def pack _; "oh no"; end
+ end
+
+ v = [2 ** 15].pack('n')
+
+ class Array
+ alias :pack :old_pack
+ end
+
+ assert_equal "oh no", v
+ end;
+ end
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index b8de2ba952..20364c5a0d 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -512,12 +512,12 @@ class TestParse < Test::Unit::TestCase
def t.dummy(_)
end
- assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index/)
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index assignment/)
begin;
t[42, &blk] ||= 42
end;
- assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index/)
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /block arg given in index assignment/)
begin;
t[42, &blk] ||= t.dummy 42 # command_asgn test
end;
@@ -555,34 +555,42 @@ class TestParse < Test::Unit::TestCase
mesg = 'from the backslash through the invalid char'
e = assert_syntax_error('"\xg1"', /hex escape/)
- assert_equal(' ^~'"\n", e.message.lines.last, mesg)
+ assert_match(/(^|\| ) \^~(?!~)/, e.message.lines.last, mesg)
e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape')
- assert_equal(' ^'"\n", e.message.lines.last, mesg)
+ assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg)
e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape')
- assert_equal(' ^'"\n", e.message.lines.last, mesg)
+ assert_match(/(^|\| ) \^(?!~)/, e.message.lines.last, mesg)
e = assert_syntax_error('"\u{xxxx', 'Unicode escape')
- assert_pattern_list([
- /.*: invalid Unicode escape\n.*\n/,
- / \^/,
- /\n/,
- /.*: unterminated Unicode escape\n.*\n/,
- / \^/,
- /\n/,
- /.*: unterminated string.*\n.*\n/,
- / \^\n/,
- ], e.message)
+ if e.message.lines.first == "#{__FILE__}:#{__LINE__ - 1}: syntax errors found\n"
+ assert_pattern_list([
+ /\s+\| \^ unterminated string;.+\n/,
+ /\s+\| \^ unterminated Unicode escape\n/,
+ /\s+\| \^ invalid Unicode escape sequence\n/,
+ ], e.message.lines[2..-1].join)
+ else
+ assert_pattern_list([
+ /.*: invalid Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated string.*\n.*\n/,
+ / \^\n/,
+ ], e.message)
+ end
e = assert_syntax_error('"\M1"', /escape character syntax/)
- assert_equal(' ^~~'"\n", e.message.lines.last, mesg)
+ assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg)
e = assert_syntax_error('"\C1"', /escape character syntax/)
- assert_equal(' ^~~'"\n", e.message.lines.last, mesg)
+ assert_match(/(^|\| ) \^~~(?!~)/, e.message.lines.last, mesg)
src = '"\xD0\u{90'"\n""000000000000000000000000"
- assert_syntax_error(src, /:#{__LINE__}: unterminated/o)
+ assert_syntax_error(src, /(:#{__LINE__}:|> #{__LINE__} \|.+) unterminated/om)
assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/)
assert_equal("", eval('"\u{}"'))
@@ -605,22 +613,22 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error("\"\\C-\\M-\x01\"", 'Invalid escape character syntax')
e = assert_syntax_error('"\c\u0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error('"\c\U0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error('"\C-\u0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error('"\C-\U0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error('"\M-\u0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax')
- assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ assert_match(/(^|\| ) \^~~~~(?!~)/, e.message.lines.last)
e = assert_syntax_error(%["\\C-\u3042"], 'Invalid escape character syntax')
- assert_match(/^\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )$/x, e.message.lines.last)
+ assert_match(/(^|\|\s)\s \^(?# \\ ) ~(?# C ) ~(?# - ) ~+(?# U+3042 )($|\s)/x, e.message.lines.last)
assert_not_include(e.message, "invalid multibyte char")
end
@@ -875,7 +883,8 @@ x = __ENCODING__
$test_parse_foobarbazqux = nil
assert_equal(nil, $&)
assert_equal(nil, eval('alias $& $preserve_last_match'))
- assert_syntax_error('a = $#', /as a global variable name\na = \$\#\n \^~$/)
+ assert_syntax_error('a = $#', /as a global variable name/)
+ assert_syntax_error('a = $#', /a = \$\#\n(^|.+?\| ) \^~(?!~)/)
end
def test_invalid_instance_variable
@@ -1259,8 +1268,8 @@ x = __ENCODING__
assert_syntax_error("def f r:def d; def f 0end", /unexpected/)
end;
- assert_syntax_error("def\nf(000)end", /^ \^~~/)
- assert_syntax_error("def\nf(&0)end", /^ \^/)
+ assert_syntax_error("def\nf(000)end", /(^|\| ) \^~~/)
+ assert_syntax_error("def\nf(&0)end", /(^|\| ) \^/)
end
def test_method_location_in_rescue
@@ -1336,17 +1345,21 @@ x = __ENCODING__
end
def test_unexpected_token_after_numeric
- assert_syntax_error('0000xyz', /^ \^~~\Z/)
- assert_syntax_error('1.2i1.1', /^ \^~~\Z/)
- assert_syntax_error('1.2.3', /^ \^~\Z/)
+ assert_syntax_error('0000xyz', /(^|\| ) \^~~(?!~)/)
+ assert_syntax_error('1.2i1.1', /(^|\| ) \^~~(?!~)/)
+ assert_syntax_error('1.2.3', /(^|\| ) \^~(?!~)/)
assert_syntax_error('1.', /unexpected end-of-input/)
assert_syntax_error('1e', /expecting end-of-input/)
end
def test_truncated_source_line
- e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789",
+ lineno = __LINE__ + 1
+ e = assert_syntax_error("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 123456789012345678901234567890123456789",
/unexpected local variable or method/)
+
line = e.message.lines[1]
+ line.delete_prefix!("> #{lineno} | ") if line.start_with?(">")
+
assert_operator(line, :start_with?, "...")
assert_operator(line, :end_with?, "...\n")
end
@@ -1390,11 +1403,11 @@ x = __ENCODING__
end
def test_unexpected_eof
- assert_syntax_error('unless', /^ \^\Z/)
+ assert_syntax_error('unless', /(^|\| ) \^(?!~)/)
end
def test_location_of_invalid_token
- assert_syntax_error('class xxx end', /^ \^~~\Z/)
+ assert_syntax_error('class xxx end', /(^|\| ) \^~~(?!~)/)
end
def test_whitespace_warning
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index db6ad06b82..cfe3bd1e19 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -1331,7 +1331,7 @@ END
end
assert_block do
- case {}
+ case C.new({})
in {}
C.keys == nil
end
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index c72029ca80..828117f516 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -1783,7 +1783,7 @@ class TestRegexp < Test::Unit::TestCase
end
t = Time.now - t
- assert_in_delta(timeout, t, timeout / 2)
+ assert_operator(timeout, :<=, [timeout * 1.5, 1].max)
end;
end
@@ -1862,7 +1862,7 @@ class TestRegexp < Test::Unit::TestCase
def test_timeout_shorter_than_global
omit "timeout test is too unstable on s390x" if RUBY_PLATFORM =~ /s390x/
- per_instance_redos_test(10, 0.2, 0.2)
+ per_instance_redos_test(10, 0.5, 0.5)
end
def test_timeout_longer_than_global
@@ -1929,7 +1929,7 @@ class TestRegexp < Test::Unit::TestCase
end
def test_match_cache_positive_look_ahead
- assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", timeout: 30)
timeout = #{ EnvUtil.apply_timeout_scale(10).inspect }
begin;
Regexp.timeout = timeout
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index ef33928376..5aadf779fb 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -847,7 +847,7 @@ class TestRequire < Test::Unit::TestCase
f.close
File.unlink(f.path)
File.mkfifo(f.path)
- assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
th = Thread.current
Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
@@ -866,7 +866,7 @@ class TestRequire < Test::Unit::TestCase
File.unlink(f.path)
File.mkfifo(f.path)
- assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10)
begin;
path = ARGV[0]
th = Thread.current
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index 76be9152a7..22663ed007 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -302,11 +302,8 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_parser_flag
- warning = /compiler based on the Prism parser is currently experimental/
-
- assert_in_out_err(%w(--parser=prism -e) + ["puts :hi"], "", %w(hi), warning)
- assert_in_out_err(%w(--parser=prism -W:no-experimental -e) + ["puts :hi"], "", %w(hi), [])
- assert_in_out_err(%w(--parser=prism -W:no-experimental --dump=parsetree -e _=:hi), "", /"hi"/, [])
+ assert_in_out_err(%w(--parser=prism -e) + ["puts :hi"], "", %w(hi), [])
+ assert_in_out_err(%w(--parser=prism --dump=parsetree -e _=:hi), "", /"hi"/, [])
assert_in_out_err(%w(--parser=parse.y -e) + ["puts :hi"], "", %w(hi), [])
assert_norun_with_rflag('--parser=parse.y', '--version', "")
@@ -1208,15 +1205,12 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_frozen_string_literal_debug
- default_frozen = eval("'test'").frozen?
-
with_debug_pat = /created at/
wo_debug_pat = /can\'t modify frozen String: "\w+" \(FrozenError\)\n\z/
frozen = [
["--enable-frozen-string-literal", true],
["--disable-frozen-string-literal", false],
]
- frozen << [nil, false] unless default_frozen
debugs = [
["--debug-frozen-string-literal", true],
diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb
index d729aa5af8..053a914a8a 100644
--- a/test/ruby/test_rubyvm.rb
+++ b/test/ruby/test_rubyvm.rb
@@ -32,6 +32,7 @@ class TestRubyVM < Test::Unit::TestCase
end
def test_keep_script_lines
+ omit if compiling_with_prism?
pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO
prev_conf = RubyVM.keep_script_lines
@@ -68,4 +69,12 @@ class TestRubyVM < Test::Unit::TestCase
ensure
RubyVM.keep_script_lines = prev_conf
end
+
+ private
+
+ # RubyVM.keep_script_lines does not mean anything in the context of prism, so
+ # we should omit tests that are looking for that functionality.
+ def compiling_with_prism?
+ RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism
+ end
end
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index c453ecd350..8cf2c63a20 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -266,8 +266,8 @@ class TestSprintf < Test::Unit::TestCase
# Specifying the precision multiple times with negative star arguments:
assert_raise(ArgumentError, "[ruby-core:11570]") {sprintf("%.*.*.*.*f", -1, -1, -1, 5, 1)}
- # Null bytes after percent signs are removed:
- assert_equal("%\0x hello", sprintf("%\0x hello"), "[ruby-core:11571]")
+ assert_raise(ArgumentError) {sprintf("%\0x hello")}
+ assert_raise(ArgumentError) {sprintf("%\nx hello")}
assert_raise(ArgumentError, "[ruby-core:11573]") {sprintf("%.25555555555555555555555555555555555555s", "hello")}
@@ -279,10 +279,9 @@ class TestSprintf < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$*d", 3) }
assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$.*d", 3) }
- verbose, $VERBOSE = $VERBOSE, nil
- assert_nothing_raised { sprintf("", 1) }
- ensure
- $VERBOSE = verbose
+ assert_warning(/too many arguments/) do
+ sprintf("", 1)
+ end
end
def test_float
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index ebe85dac82..9bd86dfb78 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -3617,17 +3617,16 @@ CODE
def test_chilled_string
chilled_string = eval('"chilled"')
- # Chilled strings pretend to be frozen
- assert_predicate chilled_string, :frozen?
+ assert_not_predicate chilled_string, :frozen?
assert_not_predicate chilled_string.dup, :frozen?
- assert_predicate chilled_string.clone, :frozen?
+ assert_not_predicate chilled_string.clone, :frozen?
# @+ treat the original string as frozen
assert_not_predicate(+chilled_string, :frozen?)
assert_not_same chilled_string, +chilled_string
- # @- the original string as mutable
+ # @- treat the original string as mutable
assert_predicate(-chilled_string, :frozen?)
assert_not_same chilled_string, -chilled_string
end
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index 41b71097d1..2275de06a8 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -120,8 +120,7 @@ class TestSymbol < Test::Unit::TestCase
def test_inspect_under_gc_compact_stress
omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
- omit "very flaky on many platforms, more so with YJIT enabled" if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
- omit "very flaky on many platforms, more so with RJIT enabled" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
+
EnvUtil.under_gc_compact_stress do
assert_inspect_evaled(':testing')
end
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 44162f06cb..8aa3a54084 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -392,12 +392,11 @@ class TestSyntax < Test::Unit::TestCase
end
def test_keyword_self_reference
- message = /circular argument reference - var/
- assert_syntax_error("def foo(var: defined?(var)) var end", message)
- assert_syntax_error("def foo(var: var) var end", message)
- assert_syntax_error("def foo(var: bar(var)) var end", message)
- assert_syntax_error("def foo(var: bar {var}) var end", message)
- assert_syntax_error("def foo(var: (1 in ^var)); end", message)
+ assert_valid_syntax("def foo(var: defined?(var)) var end")
+ assert_valid_syntax("def foo(var: var) var end")
+ assert_valid_syntax("def foo(var: bar(var)) var end")
+ assert_valid_syntax("def foo(var: bar {var}) var end")
+ assert_valid_syntax("def foo(var: (1 in ^var)); end")
o = Object.new
assert_warn("") do
@@ -423,6 +422,9 @@ class TestSyntax < Test::Unit::TestCase
assert_warn("") do
o.instance_eval("proc {|var: 1| var}")
end
+
+ o = Object.new
+ assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
end
def test_keyword_invalid_name
@@ -456,14 +458,13 @@ class TestSyntax < Test::Unit::TestCase
end
def test_optional_self_reference
- message = /circular argument reference - var/
- assert_syntax_error("def foo(var = defined?(var)) var end", message)
- assert_syntax_error("def foo(var = var) var end", message)
- assert_syntax_error("def foo(var = bar(var)) var end", message)
- assert_syntax_error("def foo(var = bar {var}) var end", message)
- assert_syntax_error("def foo(var = (def bar;end; var)) var end", message)
- assert_syntax_error("def foo(var = (def self.bar;end; var)) var end", message)
- assert_syntax_error("def foo(var = (1 in ^var)); end", message)
+ assert_valid_syntax("def foo(var = defined?(var)) var end")
+ assert_valid_syntax("def foo(var = var) var end")
+ assert_valid_syntax("def foo(var = bar(var)) var end")
+ assert_valid_syntax("def foo(var = bar {var}) var end")
+ assert_valid_syntax("def foo(var = (def bar;end; var)) var end")
+ assert_valid_syntax("def foo(var = (def self.bar;end; var)) var end")
+ assert_valid_syntax("def foo(var = (1 in ^var)); end")
o = Object.new
assert_warn("") do
@@ -489,6 +490,9 @@ class TestSyntax < Test::Unit::TestCase
assert_warn("") do
o.instance_eval("proc {|var = 1| var}")
end
+
+ o = Object.new
+ assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
end
def test_warn_grouped_expression
@@ -506,10 +510,6 @@ class TestSyntax < Test::Unit::TestCase
end
def test_warn_balanced
- warning = <<WARN
-test:1: warning: '%s' after local variable or literal is interpreted as binary operator
-test:1: warning: even though it seems like %s
-WARN
[
[:**, "argument prefix"],
[:*, "argument prefix"],
@@ -523,7 +523,9 @@ WARN
all_assertions do |a|
["puts 1 #{op}0", "puts :a #{op}0", "m = 1; puts m #{op}0"].each do |src|
a.for(src) do
- assert_warning(warning % [op, syn], src) do
+ warning = /'#{Regexp.escape(op)}' after local variable or literal is interpreted as binary operator.+?even though it seems like #{syn}/m
+
+ assert_warning(warning, src) do
assert_valid_syntax(src, "test", verbose: true)
end
end
@@ -715,8 +717,8 @@ WARN
end
def test_duplicated_when
- w = 'warning: duplicated \'when\' clause with line 3 is ignored'
- assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
+ w = ->(line) { "warning: 'when' clause on line #{line} duplicates 'when' clause on line 3 and is ignored" }
+ assert_warning(/#{w[3]}.+#{w[4]}.+#{w[4]}.+#{w[5]}.+#{w[5]}/m) {
eval %q{
case 1
when 1, 1
@@ -725,7 +727,7 @@ WARN
end
}
}
- assert_warning(/#{w}/) {#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ assert_warning(/#{w[3]}.+#{w[4]}.+#{w[5]}.+#{w[5]}/m) {
a = a = 1
eval %q{
case 1
@@ -735,7 +737,7 @@ WARN
end
}
}
- assert_warning(/3: #{w}/m) {
+ assert_warning(/#{w[3]}/) {
eval %q{
case 1
when __LINE__, __LINE__
@@ -744,7 +746,7 @@ WARN
end
}
}
- assert_warning(/3: #{w}/m) {
+ assert_warning(/#{w[3]}/) {
eval %q{
case 1
when __FILE__, __FILE__
@@ -756,7 +758,7 @@ WARN
end
def test_duplicated_when_check_option
- w = /duplicated \'when\' clause with line 3 is ignored/
+ w = /'when' clause on line 4 duplicates 'when' clause on line 3 and is ignored/
assert_in_out_err(%[-wc], "#{<<~"begin;"}\n#{<<~'end;'}", ["Syntax OK"], w)
begin;
case 1
@@ -889,6 +891,16 @@ e"
assert_dedented_heredoc(expect, result)
end
+ def test_dedented_heredoc_with_leading_blank_line
+ # the blank line has six leading spaces
+ result = " \n" \
+ " b\n"
+ expect = " \n" \
+ "b\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+
def test_dedented_heredoc_with_blank_more_indented_line_escaped
result = " a\n" \
"\\ \\ \\ \\ \\ \\ \n" \
@@ -996,7 +1008,7 @@ eom
end
def test_dedented_heredoc_concatenation
- assert_equal("\n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
+ assert_equal(" \n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
end
def test_heredoc_mixed_encoding
@@ -1238,6 +1250,20 @@ eom
assert_syntax_error("a&.x,=0", /multiple assignment destination/)
end
+ def test_safe_call_in_for_variable
+ assert_valid_syntax("for x&.bar in []; end")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ foo = nil
+ for foo&.bar in [1]; end
+ assert_nil(foo)
+
+ foo = Struct.new(:bar).new
+ for foo&.bar in [1]; end
+ assert_equal(1, foo.bar)
+ end;
+ end
+
def test_no_warning_logop_literal
assert_warning("") do
eval("true||raise;nil")
@@ -1577,7 +1603,7 @@ eom
end
def test_syntax_error_at_newline
- expected = "\n ^"
+ expected = /(\n|\| ) \^/
assert_syntax_error("%[abcdef", expected)
assert_syntax_error("%[abcdef\n", expected)
end
@@ -1755,8 +1781,8 @@ eom
assert_equal("instance ok", k.new.rescued("ok"))
# Current technical limitation: cannot prepend "private" or something for command endless def
- error = /syntax error, unexpected string literal/
- error2 = /syntax error, unexpected local variable or method/
+ error = /(syntax error,|\^~*) unexpected string literal/
+ error2 = /(syntax error,|\^~*) unexpected local variable or method/
assert_syntax_error('private def foo = puts "Hello"', error)
assert_syntax_error('private def foo() = puts "Hello"', error)
assert_syntax_error('private def foo(x) = puts x', error2)
@@ -1900,7 +1926,7 @@ eom
]
end
assert_valid_syntax('proc {def foo(_);end;it}')
- assert_syntax_error('p { [it **2] }', /unexpected \*\*arg/)
+ assert_syntax_error('p { [it **2] }', /unexpected \*\*/)
end
def test_value_expr_in_condition
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index da14c429e6..6620ccbf33 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -3,7 +3,6 @@
require 'test/unit'
require "rbconfig/sizeof"
require "timeout"
-require "fiddle"
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
@@ -1446,13 +1445,16 @@ q.pop
end
def test_thread_native_thread_id_across_fork_on_linux
- rtld_default = Fiddle.dlopen(nil)
- omit "this test is only for Linux" unless rtld_default.sym_defined?('gettid')
-
- gettid = Fiddle::Function.new(rtld_default['gettid'], [], Fiddle::TYPE_INT)
+ begin
+ require '-test-/thread/id'
+ rescue LoadError
+ omit "this test is only for Linux"
+ else
+ extend Bug::ThreadID
+ end
parent_thread_id = Thread.main.native_thread_id
- real_parent_thread_id = gettid.call
+ real_parent_thread_id = gettid
assert_equal real_parent_thread_id, parent_thread_id
@@ -1464,7 +1466,7 @@ q.pop
else
# child
puts Thread.main.native_thread_id
- puts gettid.call
+ puts gettid
end
end
child_thread_id = child_lines[0].chomp.to_i
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
index 796787e355..44b935758d 100644
--- a/test/ruby/test_yjit.rb
+++ b/test/ruby/test_yjit.rb
@@ -56,14 +56,26 @@ class TestYJIT < Test::Unit::TestCase
def test_yjit_enable
args = []
args << "--disable=yjit" if RubyVM::YJIT.enabled?
- assert_separately(args, <<~RUBY)
- assert_false RubyVM::YJIT.enabled?
- assert_false RUBY_DESCRIPTION.include?("+YJIT")
+ assert_separately(args, <<~'RUBY')
+ refute_predicate RubyVM::YJIT, :enabled?
+ refute_includes RUBY_DESCRIPTION, "+YJIT"
RubyVM::YJIT.enable
- assert_true RubyVM::YJIT.enabled?
- assert_true RUBY_DESCRIPTION.include?("+YJIT")
+ assert_predicate RubyVM::YJIT, :enabled?
+ assert_includes RUBY_DESCRIPTION, "+YJIT"
+ RUBY
+ end
+
+ def test_yjit_disable
+ assert_separately(["--yjit", "--yjit-disable"], <<~'RUBY')
+ refute_predicate RubyVM::YJIT, :enabled?
+ refute_includes RUBY_DESCRIPTION, "+YJIT"
+
+ RubyVM::YJIT.enable
+
+ assert_predicate RubyVM::YJIT, :enabled?
+ assert_includes RUBY_DESCRIPTION, "+YJIT"
RUBY
end
diff --git a/test/rubygems/helper.rb b/test/rubygems/helper.rb
index f97306717d..7014843bba 100644
--- a/test/rubygems/helper.rb
+++ b/test/rubygems/helper.rb
@@ -76,8 +76,6 @@ class Gem::TestCase < Test::Unit::TestCase
attr_accessor :uri # :nodoc:
- @@tempdirs = []
-
def assert_activate(expected, *specs)
specs.each do |spec|
case spec
@@ -451,9 +449,7 @@ class Gem::TestCase < Test::Unit::TestCase
Dir.chdir @current_dir
- FileUtils.rm_rf @tempdir
-
- restore_env
+ ENV.replace(@orig_env)
Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE
Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE,
@@ -481,12 +477,9 @@ class Gem::TestCase < Test::Unit::TestCase
@back_ui.close
- refute_directory_exists @tempdir, "may be still in use"
- ghosts = @@tempdirs.filter_map do |test_name, tempdir|
- test_name if File.exist?(tempdir)
- end
- @@tempdirs << [method_name, @tempdir]
- assert_empty ghosts
+ FileUtils.rm_rf @tempdir
+
+ refute_directory_exists @tempdir, "#{@tempdir} used by test #{method_name} is still in use"
end
def credential_setup
@@ -541,6 +534,16 @@ class Gem::TestCase < Test::Unit::TestCase
ENV["BUNDLE_GEMFILE"] = File.join(@tempdir, "Gemfile")
end
+ def with_env(overrides, &block)
+ orig_env = ENV.to_h
+ ENV.replace(overrides)
+ begin
+ block.call
+ ensure
+ ENV.replace(orig_env)
+ end
+ end
+
##
# A git_gem is used with a gem dependencies file. The gem created here
# has no files, just a gem specification for the given +name+ and +version+.
@@ -1526,23 +1529,6 @@ Also, a list:
PUBLIC_KEY = nil
PUBLIC_CERT = nil
end if Gem::HAVE_OPENSSL
-
- private
-
- def restore_env
- unless Gem.win_platform?
- ENV.replace(@orig_env)
- return
- end
-
- # Fallback logic for Windows below to workaround
- # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all
- # supported rubies include the fix for that.
-
- ENV.clear
-
- @orig_env.each {|k, v| ENV[k] = v }
- end
end
# https://github.com/seattlerb/minitest/blob/13c48a03d84a2a87855a4de0c959f96800100357/lib/minitest/mock.rb#L192
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 50e621f22b..a737185681 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -33,7 +33,7 @@ class TestGemBundledCA < Gem::TestCase
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.cert_store = bundled_certificate_store
http.get("/")
- rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError, Net::OpenTimeout
+ rescue Errno::ENOENT, Errno::ETIMEDOUT, SocketError, Gem::Net::OpenTimeout
pend "#{host} seems offline, I can't tell whether ssl would work."
rescue OpenSSL::SSL::SSLError => e
# Only fail for certificate verification errors
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index 244b7749a5..e8a294d65c 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -516,7 +516,10 @@ class TestGem < Gem::TestCase
Gem.clear_paths
- assert_nil Gem::Specification.send(:class_variable_get, :@@all)
+ with_env("GEM_HOME" => "foo", "GEM_PATH" => "bar") do
+ assert_equal("foo", Gem.dir)
+ assert_equal("bar", Gem.path.first)
+ end
end
def test_self_configuration
@@ -1281,7 +1284,6 @@ class TestGem < Gem::TestCase
def test_self_try_activate_missing_extensions
spec = util_spec "ext", "1" do |s|
s.extensions = %w[ext/extconf.rb]
- s.mark_version
s.installed_by_version = v("2.2")
end
@@ -1560,21 +1562,20 @@ class TestGem < Gem::TestCase
assert_equal m1.gem_dir, File.join(Gem.user_dir, "gems", "m-1")
tests = [
- [:dir0, [Gem.dir, Gem.user_dir], m0],
- [:dir1, [Gem.user_dir, Gem.dir], m1],
+ [:dir0, [Gem.dir, Gem.user_dir]],
+ [:dir1, [Gem.user_dir, Gem.dir]],
]
- tests.each do |name, paths, expected|
+ tests.each do |name, paths|
Gem.use_paths paths.first, paths
- Gem::Specification.reset
Gem.searcher = nil
assert_equal Gem::Dependency.new("m","1").to_specs,
Gem::Dependency.new("m","1").to_specs.sort
assert_equal \
- [expected.gem_dir],
- Gem::Dependency.new("m","1").to_specs.map(&:gem_dir).sort,
+ [m0.gem_dir, m1.gem_dir],
+ Gem::Dependency.new("m","1").to_specs.map(&:gem_dir).uniq.sort,
"Wrong specs for #{name}"
spec = Gem::Dependency.new("m","1").to_spec
@@ -1614,9 +1615,11 @@ class TestGem < Gem::TestCase
Gem.use_paths Gem.dir, [Gem.dir, Gem.user_dir]
+ spec = Gem::Dependency.new("m", "1").to_spec
+
assert_equal \
File.join(Gem.dir, "gems", "m-1"),
- Gem::Dependency.new("m","1").to_spec.gem_dir,
+ spec.gem_dir,
"Wrong spec selected"
end
diff --git a/test/rubygems/test_gem_ci_detector.rb b/test/rubygems/test_gem_ci_detector.rb
index 3caefce97d..a28ee49f4b 100644
--- a/test/rubygems/test_gem_ci_detector.rb
+++ b/test/rubygems/test_gem_ci_detector.rb
@@ -3,7 +3,7 @@
require_relative "helper"
require "rubygems"
-class TestCiDetector < Test::Unit::TestCase
+class TestCiDetector < Gem::TestCase
def test_ci?
with_env("FOO" => "bar") { assert_equal(false, Gem::CIDetector.ci?) }
with_env("CI" => "true") { assert_equal(true, Gem::CIDetector.ci?) }
@@ -29,16 +29,4 @@ class TestCiDetector < Test::Unit::TestCase
assert_equal(["dsari", "taskcluster"], Gem::CIDetector.ci_strings)
end
end
-
- private
-
- def with_env(overrides, &block)
- @orig_env = ENV.to_h
- ENV.replace(overrides)
- begin
- block.call
- ensure
- ENV.replace(@orig_env)
- end
- end
end
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index a17d7837c9..b8b39133ff 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -96,7 +96,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
out = @ui.output.split("\n")
assert_equal "Restoring gems to pristine condition...", out.shift
- assert_equal "Restored #{a.full_name}", out.shift
+ assert_equal "Restored #{a.full_name} in #{Gem.user_dir}", out.shift
assert_empty out, out.inspect
ensure
FileUtils.chmod(0o755, @gemhome)
@@ -404,7 +404,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
out = @ui.output.split "\n"
assert_equal "Restoring gems to pristine condition...", out.shift
- assert_equal "Restored #{a.full_name}", out.shift
+ assert_equal "Restored #{a.full_name} in #{@gemhome}", out.shift
assert_equal "Restored #{b.full_name}", out.shift
assert_empty out, out.inspect
@@ -476,8 +476,9 @@ class TestGemCommandsPristineCommand < Gem::TestCase
[
"Restoring gems to pristine condition...",
- "Cached gem for a-1 not found, attempting to fetch...",
- "Restored a-1",
+ "Cached gem for a-1 in #{@gemhome} not found, attempting to fetch...",
+ "Restored a-1 in #{@gemhome}",
+ "Restored b-1 in #{@gemhome}",
"Cached gem for b-1 not found, attempting to fetch...",
"Restored b-1",
].each do |line|
@@ -495,7 +496,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
assert_path_exist File.join(gemhome2, "cache", "b-1.gem")
assert_path_not_exist File.join(@gemhome, "cache", "b-2.gem")
assert_path_exist File.join(gemhome2, "gems", "b-1")
- assert_path_not_exist File.join(@gemhome, "gems", "b-1")
+ assert_path_exist File.join(@gemhome, "gems", "b-1")
end
def test_execute_no_gem
diff --git a/test/rubygems/test_gem_commands_rebuild_command.rb b/test/rubygems/test_gem_commands_rebuild_command.rb
index 5e8c797e2d..3b7927c44e 100644
--- a/test/rubygems/test_gem_commands_rebuild_command.rb
+++ b/test/rubygems/test_gem_commands_rebuild_command.rb
@@ -105,7 +105,7 @@ class TestGemCommandsRebuildCommand < Gem::TestCase
assert_equal old_spec.name, new_spec.name
assert_equal old_spec.summary, new_spec.summary
- reproduced
+ [reproduced, original]
end
def test_build_is_reproducible
@@ -134,12 +134,21 @@ class TestGemCommandsRebuildCommand < Gem::TestCase
# also testing that `gem rebuild` overrides the value.
ENV["SOURCE_DATE_EPOCH"] = Time.new(2007, 8, 9, 10, 11, 12).to_s
- rebuild_gem_file = util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp)
+ rebuild_gem_file, saved_gem_file =
+ util_test_rebuild_gem(@gem, [@gem_name, @gem_version], original_gem_file, gemspec_file, timestamp)
rebuild_contents = File.read(rebuild_gem_file)
assert_equal build_contents, rebuild_contents
ensure
ENV["SOURCE_DATE_EPOCH"] = epoch
+ if rebuild_gem_file
+ File.unlink(rebuild_gem_file)
+ dir = File.dirname(rebuild_gem_file)
+ Dir.rmdir(dir)
+ File.unlink(saved_gem_file)
+ Dir.rmdir(File.dirname(saved_gem_file))
+ Dir.rmdir(File.dirname(dir))
+ end
end
end
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index 43f695f147..8eedb6c03a 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -159,6 +159,23 @@ class TestGemCommandsSetupCommand < Gem::TestCase
end
end
+ def test_destdir_flag_regenerates_binstubs
+ # install to destdir
+ destdir = File.join(@tempdir, "foo")
+ gem_bin_path = gem_install "destdir-only-gem", install_dir: destdir
+
+ # change binstub manually
+ write_file gem_bin_path do |io|
+ io.puts "I changed it!"
+ end
+
+ @cmd.options[:destdir] = destdir
+ @cmd.options[:prefix] = "/"
+ @cmd.execute
+
+ assert_match(/\A#!/, File.read(gem_bin_path))
+ end
+
def test_files_in
assert_equal %w[rubygems.rb rubygems/requirement.rb rubygems/ssl_certs/rubygems.org/foo.pem],
@cmd.files_in("lib").sort
@@ -412,7 +429,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase
end
end
- def gem_install(name)
+ def gem_install(name, **options)
gem = util_spec name do |s|
s.executables = [name]
s.files = %W[bin/#{name}]
@@ -420,8 +437,8 @@ class TestGemCommandsSetupCommand < Gem::TestCase
write_file File.join @tempdir, "bin", name do |f|
f.puts "#!/usr/bin/ruby"
end
- install_gem gem
- File.join @gemhome, "bin", name
+ install_gem gem, **options
+ File.join options[:install_dir] || @gemhome, "bin", name
end
def gem_install_with_plugin(name)
diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb
index 4daa61cb0c..66885395d8 100644
--- a/test/rubygems/test_gem_commands_uninstall_command.rb
+++ b/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -32,7 +32,7 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
@cmd.execute
end
- assert_equal %w[a_evil-9 b-2 c-1.2 default-1 dep_x-1 pl-1-x86-linux x-1],
+ assert_equal %w[a-4 a_evil-9 b-2 c-1.2 default-1 dep_x-1 pl-1-x86-linux x-1],
Gem::Specification.all_names.sort
end
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 2683840f2e..194ebb030a 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -217,7 +217,15 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
end
def test_execute_system_update_installed_in_non_default_gem_path
- rubygems_update_spec = quick_gem "rubygems-update", 9 do |s|
+ rubygems_update_spec = Gem::Specification.new do |s|
+ s.name = "rubygems-update"
+ s.version = "9"
+ s.author = "A User"
+ s.email = "example@example.com"
+ s.homepage = "http://example.com"
+ s.summary = "this is a summary"
+ s.description = "This is a test description"
+
write_file File.join(@tempdir, "setup.rb")
s.files += %w[setup.rb]
diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb
index 4469750f9a..a3f95bb770 100644
--- a/test/rubygems/test_gem_package_tar_header.rb
+++ b/test/rubygems/test_gem_package_tar_header.rb
@@ -99,6 +99,31 @@ class TestGemPackageTarHeader < Gem::Package::TarTestCase
assert_empty @tar_header
end
+ def test_empty
+ @tar_header = Gem::Package::TarHeader.from(StringIO.new(Gem::Package::TarHeader::EMPTY_HEADER))
+
+ assert_empty @tar_header
+ assert_equal Gem::Package::TarHeader.new(
+ checksum: 0,
+ devmajor: 0,
+ devminor: 0,
+ empty: true,
+ gid: 0,
+ gname: "",
+ linkname: "",
+ magic: "",
+ mode: 0,
+ mtime: 0,
+ name: "",
+ prefix: "",
+ size: 0,
+ typeflag: "0",
+ uid: 0,
+ uname: "",
+ version: 0,
+ ), @tar_header
+ end
+
def test_equals2
assert_equal @tar_header, @tar_header
assert_equal @tar_header, @tar_header.dup
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index e4bf882317..00e48498c6 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -145,6 +145,9 @@ class TestGemPlatform < Gem::TestCase
"x86_64-openbsd3.9" => ["x86_64", "openbsd", "3.9"],
"x86_64-openbsd4.0" => ["x86_64", "openbsd", "4.0"],
"x86_64-openbsd" => ["x86_64", "openbsd", nil],
+ "wasm32-wasi" => ["wasm32", "wasi", nil],
+ "wasm32-wasip1" => ["wasm32", "wasi", nil],
+ "wasm32-wasip2" => ["wasm32", "wasi", nil],
}
test_cases.each do |arch, expected|
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 9e05649f7c..9395e34f75 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -56,7 +56,6 @@ end
s.add_dependency "jabber4r", "> 0.0.0"
s.add_dependency "pqa", ["> 0.4", "<= 0.6"]
- s.mark_version
s.files = %w[lib/code.rb]
end
end
@@ -69,7 +68,6 @@ end
s.license = "MIT"
s.platform = platform
- s.mark_version
s.files = %w[lib/code.rb]
s.installed_by_version = v("2.2")
end
@@ -96,7 +94,6 @@ end
s.requirements << "A working computer"
s.license = "MIT"
- s.mark_version
s.files = %w[lib/code.rb]
end
@@ -970,7 +967,10 @@ dependencies: []
def test_self_stubs_for_lazy_loading
Gem.loaded_specs.clear
- Gem::Specification.class_variable_set(:@@stubs, nil)
+
+ specification_record = Gem::Specification.specification_record
+
+ specification_record.instance_variable_set(:@stubs, nil)
dir_standard_specs = File.join Gem.dir, "specifications"
@@ -978,9 +978,9 @@ dependencies: []
save_gemspec("b-1", "1", dir_standard_specs) {|s| s.name = "b" }
assert_equal ["a-1"], Gem::Specification.stubs_for("a").map(&:full_name)
- assert_equal 1, Gem::Specification.class_variable_get(:@@stubs_by_name).length
+ assert_equal 1, specification_record.instance_variable_get(:@stubs_by_name).length
assert_equal ["b-1"], Gem::Specification.stubs_for("b").map(&:full_name)
- assert_equal 2, Gem::Specification.class_variable_get(:@@stubs_by_name).length
+ assert_equal 2, specification_record.instance_variable_get(:@stubs_by_name).length
assert_equal(
Gem::Specification.stubs_for("a").map(&:object_id),
@@ -989,7 +989,7 @@ dependencies: []
Gem.loaded_specs.delete "a"
Gem.loaded_specs.delete "b"
- Gem::Specification.class_variable_set(:@@stubs, nil)
+ specification_record.instance_variable_set(:@stubs, nil)
end
def test_self_stubs_for_no_lazy_loading_after_all_specs_setup
@@ -3187,7 +3187,7 @@ or set it to nil if you don't want to specify a license.
end
def test_removed_methods
- assert_equal Gem::Specification::REMOVED_METHODS, [:rubyforge_project=]
+ assert_equal Gem::Specification::REMOVED_METHODS, [:rubyforge_project=, :mark_version]
end
def test_validate_removed_rubyforge_project
@@ -3480,12 +3480,17 @@ Did you mean 'Ruby'?
util_setup_validate
@a1.rubygems_version = "3"
- e = assert_raise Gem::InvalidSpecificationException do
+
+ use_ui @ui do
@a1.validate
end
- assert_equal "expected RubyGems version #{Gem::VERSION}, was 3",
- e.message
+ expected = <<~EXPECTED
+ #{w}: expected RubyGems version #{Gem::VERSION}, was 3
+ #{w}: See https://guides.rubygems.org/specification-reference/ for help
+ EXPECTED
+
+ assert_equal expected, @ui.error
end
def test_validate_specification_version
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index 9e0c1aa3d8..794aaf1aec 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -19,8 +19,6 @@ class TestGemUninstaller < Gem::InstallerTestCase
@user_spec = @user_installer.spec
end
end
-
- Gem::Specification.reset
end
def test_initialize_expand_path
@@ -188,22 +186,81 @@ class TestGemUninstaller < Gem::InstallerTestCase
refute File.exist?(plugin_path), "plugin not removed"
end
- def test_remove_plugins_with_install_dir
+ def test_uninstall_with_install_dir_removes_plugins
write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io|
io.write "# do nothing"
end
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec), force: true).install
+ package = Gem::Package.build(@spec)
+
+ Gem::Installer.at(package, force: true).install
plugin_path = File.join Gem.plugindir, "a_plugin.rb"
assert File.exist?(plugin_path), "plugin not written"
- Dir.mkdir "#{@gemhome}2"
- Gem::Uninstaller.new(nil, install_dir: "#{@gemhome}2").remove_plugins @spec
+ install_dir = "#{@gemhome}2"
+
+ Gem::Installer.at(package, force: true, install_dir: install_dir).install
+
+ install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb"
+ assert File.exist?(install_dir_plugin_path), "plugin not written"
+
+ Gem::Specification.dirs = [install_dir]
+ Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall
assert File.exist?(plugin_path), "plugin unintentionally removed"
+ refute File.exist?(install_dir_plugin_path), "plugin not removed"
+ end
+
+ def test_uninstall_with_install_dir_regenerates_plugins
+ write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io|
+ io.write "# do nothing"
+ end
+
+ @spec.files += %w[lib/rubygems_plugin.rb]
+
+ install_dir = "#{@gemhome}2"
+
+ package = Gem::Package.build(@spec)
+
+ spec_v9 = @spec.dup
+ spec_v9.version = "9"
+ package_v9 = Gem::Package.build(spec_v9)
+
+ Gem::Installer.at(package, force: true, install_dir: install_dir).install
+ Gem::Installer.at(package_v9, force: true, install_dir: install_dir).install
+
+ install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb"
+ assert File.exist?(install_dir_plugin_path), "plugin not written"
+
+ Gem::Specification.dirs = [install_dir]
+ Gem::Uninstaller.new(@spec.name, version: "9", executables: true, install_dir: install_dir).uninstall
+ assert File.exist?(install_dir_plugin_path), "plugin unintentionally removed"
+
+ Gem::Specification.dirs = [install_dir]
+ Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall
+ refute File.exist?(install_dir_plugin_path), "plugin not removed"
+ end
+
+ def test_remove_plugins_user_installed
+ write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io|
+ io.write "# do nothing"
+ end
+
+ @spec.files += %w[lib/rubygems_plugin.rb]
+
+ Gem::Installer.at(Gem::Package.build(@spec), force: true, user_install: true).install
+
+ plugin_path = File.join Gem.user_dir, "plugins/a_plugin.rb"
+ assert File.exist?(plugin_path), "plugin not written"
+
+ Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
+
+ Gem::Uninstaller.new(@spec.name, executables: true, force: true, user_install: true).uninstall
+
+ refute File.exist?(plugin_path), "plugin not removed"
end
def test_regenerate_plugins_for
@@ -370,7 +427,7 @@ create_makefile '#{@spec.name}'
end
def test_uninstall_user_install
- @user_spec = Gem::Specification.find_by_name "b"
+ Gem::Specification.dirs = [Gem.user_dir]
uninstaller = Gem::Uninstaller.new(@user_spec.name,
executables: true,
@@ -394,6 +451,32 @@ create_makefile '#{@spec.name}'
assert_same uninstaller, @post_uninstall_hook_arg
end
+ def test_uninstall_user_install_with_symlinked_home
+ pend "Symlinks not supported or not enabled" unless symlink_supported?
+
+ Gem::Specification.dirs = [Gem.user_dir]
+
+ symlinked_home = File.join(@tempdir, "new-home")
+ FileUtils.ln_s(Gem.user_home, symlinked_home)
+
+ ENV["HOME"] = symlinked_home
+ Gem.instance_variable_set(:@user_home, nil)
+ Gem.instance_variable_set(:@data_home, nil)
+
+ uninstaller = Gem::Uninstaller.new(@user_spec.name,
+ executables: true,
+ user_install: true,
+ force: true)
+
+ gem_dir = File.join @user_spec.gem_dir
+
+ assert_path_exist gem_dir
+
+ uninstaller.uninstall
+
+ assert_path_not_exist gem_dir
+ end
+
def test_uninstall_wrong_repo
Dir.mkdir "#{@gemhome}2"
Gem.use_paths "#{@gemhome}2", [@gemhome]
diff --git a/test/rubygems/test_webauthn_poller.rb b/test/rubygems/test_webauthn_poller.rb
index 23290d8ea1..fd24081758 100644
--- a/test/rubygems/test_webauthn_poller.rb
+++ b/test/rubygems/test_webauthn_poller.rb
@@ -13,7 +13,7 @@ class WebauthnPollerTest < Gem::TestCase
@fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
@credentials = {
- email: "email@example.com",
+ identifier: "email@example.com",
password: "password",
}
end
@@ -121,4 +121,14 @@ class WebauthnPollerTest < Gem::TestCase
assert_equal error.message,
"Security device verification failed: The token in the link you used has either expired or been used already."
end
+
+ def test_poll_for_otp_missing_credentials
+ @credentials = { password: "password" }
+
+ error = assert_raise Gem::WebauthnVerificationError do
+ Gem::GemcutterUtilities::WebauthnPoller.new({}, @host).poll_for_otp(@webauthn_url, @credentials)
+ end
+
+ assert_equal error.message, "Security device verification failed: Provided missing credentials"
+ end
end
diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb
index 9031650581..aeccac2577 100644
--- a/test/stringio/test_stringio.rb
+++ b/test/stringio/test_stringio.rb
@@ -990,7 +990,8 @@ class TestStringIO < Test::Unit::TestCase
assert_predicate(s.string, :ascii_only?)
end
- if eval(%{ "test".frozen? && !"test".equal?("test") }) # Ruby 3.4+ chilled strings
+ require "objspace"
+ if ObjectSpace.respond_to?(:dump) && ObjectSpace.dump(eval(%{"test"})).include?('"chilled":true') # Ruby 3.4+ chilled strings
def test_chilled_string
chilled_string = eval(%{""})
io = StringIO.new(chilled_string)
diff --git a/test/test_tempfile.rb b/test/test_tempfile.rb
index eddbac5d75..d4ae7d4b3f 100644
--- a/test/test_tempfile.rb
+++ b/test/test_tempfile.rb
@@ -425,4 +425,54 @@ puts Tempfile.new('foo').path
assert_not_send([File.absolute_path(actual), :start_with?, target])
end
end
+
+ def test_create_anonymous_without_block
+ t = Tempfile.create(anonymous: true)
+ assert_equal(File, t.class)
+ assert_equal(0600, t.stat.mode & 0777) unless /mswin|mingw/ =~ RUBY_PLATFORM
+ t.puts "foo"
+ t.rewind
+ assert_equal("foo\n", t.read)
+ t.close
+ ensure
+ t.close if t
+ end
+
+ def test_create_anonymous_with_block
+ result = Tempfile.create(anonymous: true) {|t|
+ assert_equal(File, t.class)
+ assert_equal(0600, t.stat.mode & 0777) unless /mswin|mingw/ =~ RUBY_PLATFORM
+ t.puts "foo"
+ t.rewind
+ assert_equal("foo\n", t.read)
+ :result
+ }
+ assert_equal(:result, result)
+ end
+
+ def test_create_anonymous_removes_file
+ Dir.mktmpdir {|d|
+ t = Tempfile.create("", d, anonymous: true)
+ t.close
+ assert_equal([], Dir.children(d))
+ }
+ end
+
+ def test_create_anonymous_path
+ Dir.mktmpdir {|d|
+ begin
+ t = Tempfile.create("", d, anonymous: true)
+ assert_equal(File.join(d, ""), t.path)
+ ensure
+ t.close if t
+ end
+ }
+ end
+
+ def test_create_anonymous_autoclose
+ Tempfile.create(anonymous: true) {|t|
+ assert_equal(true, t.autoclose?)
+ }
+ end
+
end
diff --git a/test/test_timeout.rb b/test/test_timeout.rb
index e900b10cd7..34966f92a4 100644
--- a/test/test_timeout.rb
+++ b/test/test_timeout.rb
@@ -66,7 +66,7 @@ class TestTimeout < Test::Unit::TestCase
a = nil
assert_raise(Timeout::Error) do
Timeout.timeout(0.1) {
- Timeout.timeout(1) {
+ Timeout.timeout(30) {
nil while true
}
a = 1
@@ -84,7 +84,7 @@ class TestTimeout < Test::Unit::TestCase
def test_nested_timeout_error_identity
begin
Timeout.timeout(0.1, MyNewErrorOuter) {
- Timeout.timeout(1, MyNewErrorInner) {
+ Timeout.timeout(30, MyNewErrorInner) {
nil while true
}
}
diff --git a/test/win32/test_registry.rb b/test/win32/test_registry.rb
new file mode 100644
index 0000000000..02cafc09b0
--- /dev/null
+++ b/test/win32/test_registry.rb
@@ -0,0 +1,97 @@
+if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
+ begin
+ require 'win32/registry'
+ rescue LoadError
+ else
+ require 'test/unit'
+ end
+end
+
+if defined?(Win32::Registry)
+ class TestWin32Registry < Test::Unit::TestCase
+ COMPUTERNAME = 'SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName'
+ VOLATILE_ENVIRONMENT = 'Volatile Environment'
+
+ def test_predefined
+ assert_predefined_key Win32::Registry::HKEY_CLASSES_ROOT
+ assert_predefined_key Win32::Registry::HKEY_CURRENT_USER
+ assert_predefined_key Win32::Registry::HKEY_LOCAL_MACHINE
+ assert_predefined_key Win32::Registry::HKEY_USERS
+ assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_DATA
+ assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_TEXT
+ assert_predefined_key Win32::Registry::HKEY_PERFORMANCE_NLSTEXT
+ assert_predefined_key Win32::Registry::HKEY_CURRENT_CONFIG
+ assert_predefined_key Win32::Registry::HKEY_DYN_DATA
+ end
+
+ def test_class_open
+ name1, keys1 = Win32::Registry.open(Win32::Registry::HKEY_LOCAL_MACHINE, "SYSTEM") do |reg|
+ assert_predicate reg, :open?
+ [reg.name, reg.keys]
+ end
+ name2, keys2 = Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM") do |reg|
+ assert_predicate reg, :open?
+ [reg.name, reg.keys]
+ end
+ assert_equal name1, name2
+ assert_equal keys1, keys2
+ end
+
+ def test_read
+ computername = ENV['COMPUTERNAME']
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(COMPUTERNAME) do |reg|
+ assert_equal computername, reg['ComputerName']
+ assert_equal [Win32::Registry::REG_SZ, computername], reg.read('ComputerName')
+ assert_raise(TypeError) {reg.read('ComputerName', Win32::Registry::REG_DWORD)}
+ end
+ end
+
+ def test_create
+ desired = Win32::Registry::KEY_ALL_ACCESS
+ option = Win32::Registry::REG_OPTION_VOLATILE
+ Win32::Registry::HKEY_CURRENT_USER.open(VOLATILE_ENVIRONMENT, desired) do |reg|
+ v = self.class.unused_value(reg)
+ begin
+ reg.create(v, desired, option) {}
+ ensure
+ reg.delete_key(v, true)
+ end
+ end
+ end
+
+ def test_write
+ desired = Win32::Registry::KEY_ALL_ACCESS
+ Win32::Registry::HKEY_CURRENT_USER.open(VOLATILE_ENVIRONMENT, desired) do |reg|
+ v = self.class.unused_value(reg)
+ begin
+ reg.write_s(v, "data")
+ assert_equal [Win32::Registry::REG_SZ, "data"], reg.read(v)
+ reg.write_i(v, 0x5fe79027)
+ assert_equal [Win32::Registry::REG_DWORD, 0x5fe79027], reg.read(v)
+ ensure
+ reg.delete(v)
+ end
+ end
+ end
+
+ private
+
+ def assert_predefined_key(key)
+ assert_kind_of Win32::Registry, key
+ assert_predicate key, :open?
+ assert_not_predicate key, :created?
+ end
+
+ class << self
+ def unused_value(reg, prefix = "Test_", limit = 100, fail: true)
+ limit.times do
+ v = + rand(0x100000).to_s(36)
+ reg.read(v)
+ rescue
+ return v
+ end
+ omit "Unused value not found in #{reg}" if fail
+ end
+ end
+ end
+end
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index ae4adc21fe..15e5bd852f 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -991,6 +991,25 @@ if defined? Zlib
assert_raise(ArgumentError) { f.read(-1) }
assert_equal(str, f.read)
end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ s = "".b
+
+ assert_raise(ArgumentError) { f.read(-1, s) }
+
+ assert_same s, f.read(1, s)
+ assert_equal "\xE3".b, s
+
+ assert_same s, f.read(2, s)
+ assert_equal "\x81\x82".b, s
+
+ assert_same s, f.read(6, s)
+ assert_equal "\u3044\u3046".b, s
+
+ assert_nil f.read(1, s)
+ assert_equal "".b, s
+ assert_predicate f, :eof?
+ end
}
end
@@ -1005,10 +1024,14 @@ if defined? Zlib
Zlib::GzipReader.open(t.path) do |f|
s = "".dup
- f.readpartial(3, s)
+ assert_same s, f.readpartial(3, s)
assert("foo".start_with?(s))
assert_raise(ArgumentError) { f.readpartial(-1) }
+
+ assert_same s, f.readpartial(3, s)
+
+ assert_predicate f, :eof?
end
}
end