summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2024-03-05 08:51:10 -0500
committergit <svn-admin@ruby-lang.org>2024-03-06 16:42:44 +0000
commit7bcca7177d6c42891004899d63e13ef47d78a337 (patch)
tree35aa71897799886529aaa020bca2e1d312738907
parent2cb75b8f1760a9aefb2221c4a6fab9304bdb3da2 (diff)
[ruby/prism] Implement hash patterns for ripper translation
https://github.com/ruby/prism/commit/6bd7ae2ed2
-rw-r--r--lib/prism/translation/ripper.rb20
-rw-r--r--test/prism/ripper_test.rb278
2 files changed, 20 insertions, 278 deletions
diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb
index adc82406fa..89f793cb70 100644
--- a/lib/prism/translation/ripper.rb
+++ b/lib/prism/translation/ripper.rb
@@ -1117,7 +1117,25 @@ module Prism
# foo => {}
# ^^
def visit_hash_pattern_node(node)
- raise NoMethodError, __method__
+ constant = visit(node.constant)
+ elements =
+ if node.elements.any? || !node.rest.nil?
+ node.elements.map do |element|
+ bounds(element.key.location)
+ key = on_label(element.key.slice)
+ value = visit(element.value)
+
+ [key, value]
+ end
+ end
+
+ rest =
+ if !node.rest.nil?
+ visit(node.rest.value)
+ end
+
+ bounds(node.location)
+ on_hshptn(constant, elements, rest)
end
# if foo then bar end
diff --git a/test/prism/ripper_test.rb b/test/prism/ripper_test.rb
index 228240e005..5b91ea905d 100644
--- a/test/prism/ripper_test.rb
+++ b/test/prism/ripper_test.rb
@@ -6,273 +6,7 @@ File.delete("passing.txt") if File.exist?("passing.txt")
File.delete("failing.txt") if File.exist?("failing.txt")
module Prism
- class RipperTestCase < TestCase
- private
-
- def truffleruby?
- RUBY_ENGINE == "truffleruby"
- end
-
- # Ripper produces certain ambiguous structures. For instance, it often
- # adds an :args_add_block with "false" as the block meaning there is
- # no block call. It can be hard to tell which of multiple equivalent
- # structures it will produce. This method attempts to return a normalized
- # comparable structure.
- def normalized_sexp(parsed)
- if parsed.is_a?(Array)
- # For args_add_block, if the third entry is nil or false, remove it.
- # Note that CRuby Ripper uses false for no block, while older JRuby
- # uses nil. We need to do this for both.
- return normalized_sexp(parsed[1]) if parsed[0] == :args_add_block && !parsed[2]
-
- parsed.each.with_index do |item, idx|
- if item.is_a?(Array)
- parsed[idx] = normalized_sexp(parsed[idx])
- end
- end
- end
-
- parsed
- end
-
- def assert_ripper_equivalent(source, path: "inline source code")
- expected = Ripper.sexp_raw(source)
-
- refute_nil expected, "Could not parse #{path} with Ripper!"
- expected = normalized_sexp(expected)
- actual = Prism::Translation::Ripper.sexp_raw(source)
- refute_nil actual, "Could not parse #{path} with Prism!"
- actual = normalized_sexp(actual)
- assert_equal expected, actual, "Expected Ripper and Prism to give equivalent output for #{path}!"
- end
- end
-
- class RipperShortSourceTest < RipperTestCase
- def test_binary
- assert_equivalent("1 + 2")
- assert_equivalent("3 - 4 * 5")
- assert_equivalent("6 / 7; 8 % 9")
- end
-
- def test_unary
- assert_equivalent("-7")
- end
-
- def test_unary_parens
- assert_equivalent("-(7)")
- assert_equivalent("(-7)")
- assert_equivalent("(-\n7)")
- end
-
- def test_binary_parens
- assert_equivalent("(3 + 7) * 4")
- end
-
- def test_method_calls_with_variable_names
- assert_equivalent("foo")
- assert_equivalent("foo()")
- assert_equivalent("foo -7")
- assert_equivalent("foo(-7)")
- assert_equivalent("foo(1, 2, 3)")
- assert_equivalent("foo 1")
- assert_equivalent("foo bar")
- assert_equivalent("foo 1, 2")
- assert_equivalent("foo.bar")
-
- # TruffleRuby prints emoji symbols differently in a way that breaks here.
- unless truffleruby?
- assert_equivalent("🗻")
- assert_equivalent("🗻.location")
- assert_equivalent("foo.🗻")
- assert_equivalent("🗻.😮!")
- assert_equivalent("🗻 🗻,🗻,🗻")
- end
-
- assert_equivalent("foo&.bar")
- assert_equivalent("foo { bar }")
- assert_equivalent("foo.bar { 7 }")
- assert_equivalent("foo(1) { bar }")
- assert_equivalent("foo(bar)")
- assert_equivalent("foo(bar(1))")
- assert_equivalent("foo(bar(1)) { 7 }")
- assert_equivalent("foo bar(1)")
- end
-
- def test_method_call_blocks
- assert_equivalent("foo { |a| a }")
-
- assert_equivalent("foo(bar 1)")
- assert_equivalent("foo bar 1")
- assert_equivalent("foo(bar 1) { 7 }")
- assert_equivalent("foo(bar 1) {; 7 }")
- assert_equivalent("foo(bar 1) {;}")
-
- assert_equivalent("foo do\n bar\nend")
- assert_equivalent("foo do\nend")
- assert_equivalent("foo do; end")
- assert_equivalent("foo do bar; end")
- assert_equivalent("foo do bar end")
- assert_equivalent("foo do; bar; end")
- end
-
- def test_method_calls_on_immediate_values
- assert_equivalent("7.even?")
- assert_equivalent("!1")
- assert_equivalent("7 && 7")
- assert_equivalent("7 and 7")
- assert_equivalent("7 || 7")
- assert_equivalent("7 or 7")
- assert_equivalent("'racecar'.reverse")
- end
-
- def test_range
- assert_equivalent("(...2)")
- assert_equivalent("(..2)")
- assert_equivalent("(1...2)")
- assert_equivalent("(1..2)")
- assert_equivalent("(foo..-7)")
- end
-
- def test_parentheses
- assert_equivalent("()")
- assert_equivalent("(1)")
- assert_equivalent("(1; 2)")
- end
-
- def test_numbers
- assert_equivalent("[1, -1, +1, 1.0, -1.0, +1.0]")
- assert_equivalent("[1r, -1r, +1r, 1.5r, -1.5r, +1.5r]")
- assert_equivalent("[1i, -1i, +1i, 1.5i, -1.5i, +1.5i]")
- assert_equivalent("[1ri, -1ri, +1ri, 1.5ri, -1.5ri, +1.5ri]")
- end
-
- def test_begin_end
- # Empty begin
- assert_equivalent("begin; end")
- assert_equivalent("begin end")
- assert_equivalent("begin; rescue; end")
-
- assert_equivalent("begin:s.l end")
- end
-
- def test_begin_rescue
- # Rescue with exception(s)
- assert_equivalent("begin a; rescue Exception => ex; c; end")
- assert_equivalent("begin a; rescue RuntimeError => ex; c; rescue Exception => ex; d; end")
- assert_equivalent("begin a; rescue RuntimeError => ex; c; rescue Exception => ex; end")
- assert_equivalent("begin a; rescue RuntimeError,FakeError,Exception => ex; c; end")
- assert_equivalent("begin a; rescue RuntimeError,FakeError,Exception; c; end")
-
- # Empty rescue
- assert_equivalent("begin a; rescue; ensure b; end")
- assert_equivalent("begin a; rescue; end")
-
- assert_equivalent("begin; a; ensure; b; end")
- end
-
- def test_begin_ensure
- # Empty ensure
- assert_equivalent("begin a; rescue; c; ensure; end")
- assert_equivalent("begin a; ensure; end")
- assert_equivalent("begin; ensure; end")
-
- # Ripper treats statements differently, depending whether there's
- # a semicolon after the keyword.
- assert_equivalent("begin a; rescue; c; ensure b; end")
- assert_equivalent("begin a; rescue c; ensure b; end")
- assert_equivalent("begin a; rescue; c; ensure; b; end")
-
- # Need to make sure we're handling multibyte characters correctly for source offsets
- assert_equivalent("begin 🗻; rescue; c; ensure;🗻🗻🗻🗻🗻; end")
- assert_equivalent("begin 🗻; rescue; c; ensure 🗻🗻🗻🗻🗻; end")
- end
-
- def test_break
- assert_equivalent("foo { break }")
- assert_equivalent("foo { break 7 }")
- assert_equivalent("foo { break [1, 2, 3] }")
- end
-
- def test_constants
- assert_equivalent("Foo")
- assert_equivalent("Foo + F🗻")
- assert_equivalent("Foo = 'soda'")
- end
-
- def test_op_assign
- assert_equivalent("a += b")
- assert_equivalent("a -= b")
- assert_equivalent("a *= b")
- assert_equivalent("a /= b")
- end
-
- def test_arrays
- assert_equivalent("[1, 2, 7]")
- assert_equivalent("[1, [2, 7]]")
- end
-
- def test_array_refs
- assert_equivalent("a[1]")
- assert_equivalent("a[1] = 7")
- end
-
- def test_strings
- assert_equivalent("'a'")
- assert_equivalent("'a\01'")
- assert_equivalent("`a`")
- assert_equivalent("`a\07`")
- assert_equivalent('"a#{1}c"')
- assert_equivalent('"a#{1}b#{2}c"')
- assert_equivalent("`f\oo`")
- end
-
- def test_symbols
- assert_equivalent(":a")
- assert_equivalent(":'a'")
- assert_equivalent(':"a"')
- assert_equivalent("%s(foo)")
- end
-
- def test_assign
- assert_equivalent("a = b")
- assert_equivalent("a = 1")
- end
-
- def test_alias
- assert_equivalent("alias :foo :bar")
- assert_equivalent("alias $a $b")
- assert_equivalent("alias $a $'")
- assert_equivalent("alias foo bar")
- assert_equivalent("alias foo if")
- assert_equivalent("alias :'def' :\"abc\#{1}\"")
- assert_equivalent("alias :\"abc\#{1}\" :'def'")
-
- unless truffleruby?
- assert_equivalent("alias :foo :Ę") # Uppercase Unicode character is a constant
- assert_equivalent("alias :Ę :foo")
- end
-
- assert_equivalent("alias foo +")
- assert_equivalent("alias foo :+")
- assert_equivalent("alias :foo :''")
- assert_equivalent("alias :'' :foo")
- end
-
- def test_keyword_aliases
- assert_equivalent("alias :foo :if")
- assert_equivalent("alias :foo :self")
- assert_equivalent("alias :foo :__FILE__")
- assert_equivalent("alias foo __ENCODING__")
- end
-
- private
-
- def assert_equivalent(source)
- assert_ripper_equivalent(source)
- end
- end
-
- class RipperFixturesTest < RipperTestCase
+ class RipperTest < RipperTestCase
base = File.join(__dir__, "fixtures")
relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
@@ -362,14 +96,7 @@ module Prism
seattlerb/call_stabby_with_braces_block.txt
seattlerb/call_trailing_comma.txt
seattlerb/case_in.txt
- seattlerb/case_in_37.txt
seattlerb/case_in_else.txt
- seattlerb/case_in_hash_pat.txt
- seattlerb/case_in_hash_pat_assign.txt
- seattlerb/case_in_hash_pat_paren_assign.txt
- seattlerb/case_in_hash_pat_paren_true.txt
- seattlerb/case_in_hash_pat_rest.txt
- seattlerb/case_in_hash_pat_rest_solo.txt
seattlerb/class_comments.txt
seattlerb/defn_arg_forward_args.txt
seattlerb/defn_args_forward_args.txt
@@ -478,7 +205,6 @@ module Prism
seattlerb/parse_pattern_051.txt
seattlerb/parse_pattern_058.txt
seattlerb/parse_pattern_058_2.txt
- seattlerb/parse_pattern_069.txt
seattlerb/parse_pattern_076.txt
seattlerb/parse_until_not_canonical.txt
seattlerb/parse_until_not_noncanonical.txt
@@ -655,7 +381,6 @@ module Prism
whitequark/masgn_nested.txt
whitequark/masgn_splat.txt
whitequark/method_definition_in_while_cond.txt
- whitequark/multiple_pattern_matches.txt
whitequark/newline_in_hash_argument.txt
whitequark/next_block.txt
whitequark/not.txt
@@ -676,7 +401,6 @@ module Prism
whitequark/parser_slash_slash_n_escaping_in_literals.txt
whitequark/pattern_matching_blank_else.txt
whitequark/pattern_matching_else.txt
- whitequark/pattern_matching_single_line_allowed_omission_of_parentheses.txt
whitequark/resbody_var.txt
whitequark/rescue_else.txt
whitequark/rescue_else_ensure.txt