ruby-next-core 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -1
  3. data/README.md +12 -14
  4. data/bin/reparse +19 -0
  5. data/bin/transform +22 -8
  6. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +5 -2
  7. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +4 -4
  8. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +25 -10
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/endless_range.rb +1 -1
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/pattern_matching.rb +133 -103
  11. data/lib/.rbnext/2.3/ruby-next/utils.rb +1 -1
  12. data/lib/.rbnext/2.7/ruby-next/core.rb +203 -0
  13. data/lib/ruby-next.rb +5 -38
  14. data/lib/ruby-next/commands/nextify.rb +4 -1
  15. data/lib/ruby-next/config.rb +45 -0
  16. data/lib/ruby-next/core.rb +5 -4
  17. data/lib/ruby-next/core/array/deconstruct.rb +1 -1
  18. data/lib/ruby-next/core/constants/no_matching_pattern_error.rb +1 -1
  19. data/lib/ruby-next/core/hash/deconstruct_keys.rb +1 -1
  20. data/lib/ruby-next/core/struct/deconstruct_keys.rb +3 -3
  21. data/lib/ruby-next/core_ext.rb +2 -0
  22. data/lib/ruby-next/language.rb +16 -5
  23. data/lib/ruby-next/language/bootsnap.rb +1 -1
  24. data/lib/ruby-next/language/edge.rb +0 -6
  25. data/lib/ruby-next/language/eval.rb +3 -3
  26. data/lib/ruby-next/language/parser.rb +19 -0
  27. data/lib/ruby-next/language/rewriters/args_forward.rb +8 -4
  28. data/lib/ruby-next/language/rewriters/args_forward_leading.rb +75 -0
  29. data/lib/ruby-next/language/rewriters/base.rb +21 -6
  30. data/lib/ruby-next/language/rewriters/in_pattern.rb +56 -0
  31. data/lib/ruby-next/language/rewriters/numbered_params.rb +6 -2
  32. data/lib/ruby-next/language/rewriters/pattern_matching.rb +131 -101
  33. data/lib/ruby-next/language/rewriters/safe_navigation.rb +30 -26
  34. data/lib/ruby-next/language/setup.rb +6 -3
  35. data/lib/ruby-next/language/unparser.rb +10 -0
  36. data/lib/ruby-next/rubocop.rb +79 -12
  37. data/lib/ruby-next/setup_self.rb +2 -2
  38. data/lib/ruby-next/version.rb +1 -1
  39. metadata +15 -6
  40. data/lib/.rbnext/2.3/ruby-next/language/rewriters/right_hand_assignment.rb +0 -107
  41. data/lib/ruby-next/language/rewriters/right_hand_assignment.rb +0 -107
@@ -13,7 +13,7 @@ load_iseq = RubyVM::InstructionSequence.method(:load_iseq)
13
13
  if load_iseq.source_location[0].include?("/bootsnap/")
14
14
  Bootsnap::CompileCache::ISeq.singleton_class.prepend(
15
15
  Module.new do
16
- def input_to_storage(source, path)
16
+ def input_to_storage(source, path, *)
17
17
  return super unless RubyNext::Language.transformable?(path)
18
18
  source = RubyNext::Language.transform(source, rewriters: RubyNext::Language.current_rewriters)
19
19
 
@@ -1,9 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Load edge Ruby features
4
-
5
- require "ruby-next/language/rewriters/endless_method"
6
- RubyNext::Language.rewriters << RubyNext::Language::Rewriters::EndlessMethod
7
-
8
- require "ruby-next/language/rewriters/right_hand_assignment"
9
- RubyNext::Language.rewriters << RubyNext::Language::Rewriters::RightHandAssignment
@@ -20,7 +20,7 @@ module RubyNext
20
20
  module InstanceEval # :nodoc:
21
21
  refine Object do
22
22
  def instance_eval(*args, &block)
23
- return super(*args, &block) if block_given?
23
+ return super(*args, &block) if block
24
24
 
25
25
  source = args.shift
26
26
  new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
@@ -33,7 +33,7 @@ module RubyNext
33
33
  module ClassEval
34
34
  refine Module do
35
35
  def module_eval(*args, &block)
36
- return super(*args, &block) if block_given?
36
+ return super(*args, &block) if block
37
37
 
38
38
  source = args.shift
39
39
  new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
@@ -42,7 +42,7 @@ module RubyNext
42
42
  end
43
43
 
44
44
  def class_eval(*args, &block)
45
- return super(*args, &block) if block_given?
45
+ return super(*args, &block) if block
46
46
 
47
47
  source = args.shift
48
48
  new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
@@ -4,8 +4,27 @@ require "parser/rubynext"
4
4
 
5
5
  module RubyNext
6
6
  module Language
7
+ module BuilderExt
8
+ def match_pattern(lhs, match_t, rhs)
9
+ n(:match_pattern, [lhs, rhs],
10
+ binary_op_map(lhs, match_t, rhs))
11
+ end
12
+
13
+ def match_pattern_p(lhs, match_t, rhs)
14
+ n(:match_pattern_p, [lhs, rhs],
15
+ binary_op_map(lhs, match_t, rhs))
16
+ end
17
+ end
18
+
7
19
  class Builder < ::Parser::Builders::Default
8
20
  modernize
21
+
22
+ # Unparser doesn't support kwargs node yet
23
+ self.emit_kwargs = false if respond_to?(:emit_kwargs=)
24
+
25
+ unless method_defined?(:match_pattern_p)
26
+ include BuilderExt
27
+ end
9
28
  end
10
29
 
11
30
  class << self
@@ -5,8 +5,8 @@ module RubyNext
5
5
  module Rewriters
6
6
  class ArgsForward < Base
7
7
  NAME = "args-forward"
8
- SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(1, ...); end"
9
- MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.2")
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(...); end"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("2.7.0")
10
10
 
11
11
  REST = :__rest__
12
12
  BLOCK = :__block__
@@ -28,14 +28,14 @@ module RubyNext
28
28
  end
29
29
 
30
30
  def on_send(node)
31
- fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
31
+ fargs = extract_fargs(node)
32
32
  return super(node) unless fargs
33
33
 
34
34
  process_fargs(node, fargs)
35
35
  end
36
36
 
37
37
  def on_super(node)
38
- fargs = node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
38
+ fargs = extract_fargs(node)
39
39
  return super(node) unless fargs
40
40
 
41
41
  process_fargs(node, fargs)
@@ -43,6 +43,10 @@ module RubyNext
43
43
 
44
44
  private
45
45
 
46
+ def extract_fargs(node)
47
+ node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :forwarded_args }
48
+ end
49
+
46
50
  def process_fargs(node, fargs)
47
51
  replace(fargs.loc.expression, "*#{REST}, &#{BLOCK}")
48
52
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class ArgsForwardLeading < ArgsForward
7
+ NAME = "args-forward-leading"
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo(...) super(1, ...); end"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
+
11
+ attr_reader :leading_farg
12
+ alias leading_farg? leading_farg
13
+
14
+ def on_def(node)
15
+ @leading_farg = method_with_leading_arg(node)
16
+
17
+ super
18
+ end
19
+
20
+ def on_defs(node)
21
+ @leading_farg = method_with_leading_arg(node)
22
+
23
+ super
24
+ end
25
+
26
+ def on_forward_arg(node)
27
+ return super if leading_farg?
28
+
29
+ node
30
+ end
31
+
32
+ def on_send(node)
33
+ return super if leading_farg?
34
+
35
+ node
36
+ end
37
+
38
+ def on_super(node)
39
+ return super if leading_farg?
40
+
41
+ node
42
+ end
43
+
44
+ private
45
+
46
+ def send_with_leading_farg(node)
47
+ return false unless node.type == :send || node.type == :super
48
+
49
+ fargs = extract_fargs(node)
50
+
51
+ return false unless fargs
52
+
53
+ node.children.index(fargs) > (node.type == :send ? 2 : 0)
54
+ end
55
+
56
+ def method_with_leading_arg(node)
57
+ find_child(node) { |child| child.type == :forward_arg } &&
58
+ (
59
+ def_with_leading_farg(node) ||
60
+ find_child(node) { |child| send_with_leading_farg(child) }
61
+ )
62
+ end
63
+
64
+ def def_with_leading_farg(node)
65
+ args = node.type == :defs ? node.children[2] : node.children[1]
66
+ args = args.children
67
+
68
+ farg = args.detect { |child| child.type == :forward_arg }
69
+
70
+ args.index(farg) > 0
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -61,7 +61,7 @@ module RubyNext
61
61
  eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
62
62
  Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
63
63
  false
64
- rescue SyntaxError, NameError
64
+ rescue SyntaxError, StandardError
65
65
  true
66
66
  ensure
67
67
  $VERBOSE = save_verbose
@@ -73,10 +73,6 @@ module RubyNext
73
73
  self::MIN_SUPPORTED_VERSION > version
74
74
  end
75
75
 
76
- def min_supported_minor_version
77
- Gem::Version.new(self::MIN_SUPPORTED_VERSION.segments[0..1].join(".") + ".0")
78
- end
79
-
80
76
  private
81
77
 
82
78
  def transform(source)
@@ -98,6 +94,24 @@ module RubyNext
98
94
 
99
95
  private
100
96
 
97
+ # BFS with predicate block
98
+ def find_child(node)
99
+ queue = [node]
100
+
101
+ loop do
102
+ break if queue.empty?
103
+
104
+ child = queue.shift
105
+ next unless child.is_a?(::Parser::AST::Node)
106
+
107
+ return child if yield child
108
+
109
+ queue.push(*child.children)
110
+ end
111
+
112
+ nil
113
+ end
114
+
101
115
  def replace(range, ast)
102
116
  @source_rewriter&.replace(range, unparse(ast))
103
117
  end
@@ -116,7 +130,8 @@ module RubyNext
116
130
 
117
131
  def unparse(ast)
118
132
  return ast if ast.is_a?(String)
119
- Unparser.unparse(ast)
133
+
134
+ Unparser.unparse(ast).chomp
120
135
  end
121
136
 
122
137
  attr_reader :context
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-next/language/rewriters/pattern_matching"
4
+
5
+ module RubyNext
6
+ module Language
7
+ module Rewriters
8
+ using RubyNext
9
+
10
+ # Separate pattern matching rewriter for Ruby 2.7 to
11
+ # transpile only `in` patterns
12
+ class InPattern < PatternMatching
13
+ NAME = "pattern-matching-in"
14
+ SYNTAX_PROBE = "1 in 2"
15
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
16
+
17
+ # Make case-match no-op
18
+ def on_case_match(node)
19
+ node
20
+ end
21
+
22
+ def on_match_pattern_p(node)
23
+ context.track! self
24
+
25
+ @deconstructed_keys = {}
26
+ @predicates = Predicates::Noop.new
27
+
28
+ matchee =
29
+ s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
30
+
31
+ pattern =
32
+ locals.with(
33
+ matchee: MATCHEE,
34
+ arr: MATCHEE_ARR,
35
+ hash: MATCHEE_HASH
36
+ ) do
37
+ send(
38
+ :"#{node.children[1].type}_clause",
39
+ node.children[1]
40
+ )
41
+ end
42
+
43
+ node.updated(
44
+ :and,
45
+ [
46
+ matchee,
47
+ pattern
48
+ ]
49
+ ).tap do |new_node|
50
+ replace(node.loc.expression, inline_blocks(unparse(new_node)))
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -16,9 +16,9 @@ module RubyNext
16
16
  proc_or_lambda, num, body = *node.children
17
17
 
18
18
  if proc_or_lambda.type == :lambda
19
- insert_before(node.loc.begin, "(#{unparse(proc_args(num))})")
19
+ insert_before(node.loc.begin, "(#{proc_args_str(num)})")
20
20
  else
21
- insert_after(node.loc.begin, " |#{unparse(proc_args(num))}|")
21
+ insert_after(node.loc.begin, " |#{proc_args_str(num)}|")
22
22
  end
23
23
 
24
24
  node.updated(
@@ -33,6 +33,10 @@ module RubyNext
33
33
 
34
34
  private
35
35
 
36
+ def proc_args_str(n)
37
+ (1..n).map { |numero| "_#{numero}" }.join(", ")
38
+ end
39
+
36
40
  def proc_args(n)
37
41
  return s(:args, s(:procarg0, s(:arg, :_1))) if n == 1
38
42
 
@@ -13,12 +13,12 @@ module RubyNext
13
13
 
14
14
  # Useful to generate simple operation nodes
15
15
  # (e.g., 'a + b')
16
- def -(val)
17
- ::Parser::AST::Node.new(:send, [self, :-, val.to_ast_node])
16
+ def -(other)
17
+ ::Parser::AST::Node.new(:send, [self, :-, other.to_ast_node])
18
18
  end
19
19
 
20
- def +(val)
21
- ::Parser::AST::Node.new(:send, [self, :+, val.to_ast_node])
20
+ def +(other)
21
+ ::Parser::AST::Node.new(:send, [self, :+, other.to_ast_node])
22
22
  end
23
23
  end
24
24
 
@@ -218,9 +218,10 @@ module RubyNext
218
218
  predicate_clause(:respond_to_deconstruct_keys, node)
219
219
  end
220
220
 
221
- def hash_key(node, key)
222
- key = key.children.first if key.is_a?(::Parser::AST::Node)
223
- predicate_clause(:"hash_key_#{key}", node)
221
+ def hash_keys(node, keys)
222
+ keys = keys.map { |key| key.is_a?(::Parser::AST::Node) ? key.children.first : key }
223
+
224
+ predicate_clause(:"hash_keys_#{keys.join("_p_")}", node)
224
225
  end
225
226
  end
226
227
  end
@@ -244,7 +245,7 @@ module RubyNext
244
245
  @predicates = Predicates::CaseIn.new
245
246
 
246
247
  matchee_ast =
247
- s(:lvasgn, MATCHEE, node.children[0])
248
+ s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
248
249
 
249
250
  patterns = locals.with(
250
251
  matchee: MATCHEE,
@@ -266,14 +267,14 @@ module RubyNext
266
267
  )
267
268
  end
268
269
 
269
- def on_in_match(node)
270
+ def on_match_pattern(node)
270
271
  context.track! self
271
272
 
272
273
  @deconstructed_keys = {}
273
274
  @predicates = Predicates::Noop.new
274
275
 
275
276
  matchee =
276
- s(:lvasgn, MATCHEE, node.children[0])
277
+ s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
277
278
 
278
279
  pattern =
279
280
  locals.with(
@@ -285,9 +286,10 @@ module RubyNext
285
286
  :"#{node.children[1].type}_clause",
286
287
  node.children[1]
287
288
  ).then do |node|
288
- s(:or,
289
- node,
290
- no_matching_pattern)
289
+ s(:begin,
290
+ s(:or,
291
+ node,
292
+ no_matching_pattern))
291
293
  end
292
294
  end
293
295
 
@@ -302,6 +304,8 @@ module RubyNext
302
304
  end
303
305
  end
304
306
 
307
+ alias on_in_match on_match_pattern
308
+
305
309
  private
306
310
 
307
311
  def rewrite_case_in!(node, matchee, new_node)
@@ -378,9 +382,10 @@ module RubyNext
378
382
  predicates.const(case_eq_clause(const, right), const).then do |node|
379
383
  next node if pattern.nil?
380
384
 
381
- s(:and,
382
- node,
383
- send(:"#{pattern.type}_clause", pattern))
385
+ s(:begin,
386
+ s(:and,
387
+ node,
388
+ send(:"#{pattern.type}_clause", pattern)))
384
389
  end
385
390
  end
386
391
 
@@ -391,13 +396,14 @@ module RubyNext
391
396
  send :"#{child.type}_clause", child
392
397
  end
393
398
  end
394
- s(:or, *children)
399
+ s(:begin, s(:or, *children))
395
400
  end
396
401
 
397
402
  def match_as_clause(node, right = s(:lvar, locals[:matchee]))
398
- s(:and,
399
- send(:"#{node.children[0].type}_clause", node.children[0], right),
400
- match_var_clause(node.children[1], right))
403
+ s(:begin,
404
+ s(:and,
405
+ send(:"#{node.children[0].type}_clause", node.children[0], right),
406
+ match_var_clause(node.children[1], right)))
401
407
  end
402
408
 
403
409
  def match_var_clause(node, left = s(:lvar, locals[:matchee]))
@@ -405,9 +411,10 @@ module RubyNext
405
411
 
406
412
  check_match_var_alternation! node.children[0]
407
413
 
408
- s(:or,
409
- s(:lvasgn, node.children[0], left),
410
- s(:true))
414
+ s(:begin,
415
+ s(:or,
416
+ s(:begin, s(:lvasgn, node.children[0], left)),
417
+ s(:true)))
411
418
  end
412
419
 
413
420
  def pin_clause(node, right = s(:lvar, locals[:matchee]))
@@ -417,8 +424,8 @@ module RubyNext
417
424
 
418
425
  def case_eq_clause(node, right = s(:lvar, locals[:matchee]))
419
426
  predicates.terminate!
420
- s(:send,
421
- process(node), :===, right)
427
+ s(:begin, s(:send,
428
+ process(node), :===, right))
422
429
  end
423
430
 
424
431
  #=========== ARRAY PATTERN (START) ===============
@@ -429,10 +436,11 @@ module RubyNext
429
436
  # if there is no rest or tail, match the size first
430
437
  unless node.type == :array_pattern_with_tail || node.children.any? { |n| n.type == :match_rest }
431
438
  size_check = predicates.array_size(
432
- s(:send,
433
- node.children.size.to_ast_node,
434
- :==,
435
- s(:send, s(:lvar, locals[:arr]), :size)),
439
+ s(:begin,
440
+ s(:send,
441
+ node.children.size.to_ast_node,
442
+ :==,
443
+ s(:send, s(:lvar, locals[:arr]), :size))),
436
444
  node.children.size
437
445
  )
438
446
  end
@@ -448,9 +456,10 @@ module RubyNext
448
456
 
449
457
  right = s(:and, size_check, right) if size_check
450
458
 
451
- s(:and,
452
- dnode,
453
- right)
459
+ s(:begin,
460
+ s(:and,
461
+ dnode,
462
+ right))
454
463
  end
455
464
  end
456
465
 
@@ -468,14 +477,17 @@ module RubyNext
468
477
  predicates.array_deconstructed(
469
478
  s(:and,
470
479
  respond_check,
471
- s(:and,
472
- s(:or,
473
- s(:lvasgn, locals[:arr], right),
474
- s(:true)),
475
- s(:or,
476
- s(:send,
477
- s(:const, nil, :Array), :===, s(:lvar, locals[:arr])),
478
- raise_error(:TypeError, "#deconstruct must return Array"))))
480
+ s(:begin,
481
+ s(:and,
482
+ s(:begin,
483
+ s(:or,
484
+ s(:begin, s(:lvasgn, locals[:arr], right)),
485
+ s(:true))),
486
+ s(:begin,
487
+ s(:or,
488
+ s(:send,
489
+ s(:const, nil, :Array), :===, s(:lvar, locals[:arr])),
490
+ raise_error(:TypeError, "#deconstruct must return Array"))))))
479
491
  )
480
492
  end
481
493
 
@@ -485,9 +497,10 @@ module RubyNext
485
497
  send("#{head.type}_array_element", head, index).then do |node|
486
498
  next node if tail.empty?
487
499
 
488
- s(:and,
489
- node,
490
- array_element(index + 1, *tail))
500
+ s(:begin,
501
+ s(:and,
502
+ node,
503
+ array_element(index + 1, *tail)))
491
504
  end
492
505
  end
493
506
 
@@ -526,15 +539,17 @@ module RubyNext
526
539
 
527
540
  pattern = array_rest_element(*nodes, index).then do |needle|
528
541
  next needle unless head_match
529
- s(:and,
530
- needle,
531
- head_match)
542
+ s(:begin,
543
+ s(:and,
544
+ needle,
545
+ head_match))
532
546
  end.then do |headed_needle|
533
547
  next headed_needle unless tail_match
534
548
 
535
- s(:and,
536
- headed_needle,
537
- tail_match)
549
+ s(:begin,
550
+ s(:and,
551
+ headed_needle,
552
+ tail_match))
538
553
  end
539
554
 
540
555
  s(:block,
@@ -550,13 +565,14 @@ module RubyNext
550
565
  next block if match_vars.empty?
551
566
 
552
567
  # We need to declare match vars outside of `find` block
553
- locals_declare = s(:masgn,
568
+ locals_declare = s(:begin, s(:masgn,
554
569
  s(:mlhs, *match_vars),
555
- s(:nil))
570
+ s(:nil)))
556
571
 
557
- s(:or,
558
- locals_declare,
559
- block)
572
+ s(:begin,
573
+ s(:or,
574
+ locals_declare,
575
+ block))
560
576
  end
561
577
  end
562
578
 
@@ -575,18 +591,20 @@ module RubyNext
575
591
 
576
592
  return rest if tail.empty?
577
593
 
578
- s(:and,
579
- rest,
580
- array_rest_element(*tail, -(size - 1)))
594
+ s(:begin,
595
+ s(:and,
596
+ rest,
597
+ array_rest_element(*tail, -(size - 1))))
581
598
  end
582
599
 
583
600
  def array_rest_element(head, *tail, index)
584
601
  send("#{head.type}_array_element", head, index).then do |node|
585
602
  next node if tail.empty?
586
603
 
587
- s(:and,
588
- node,
589
- array_rest_element(*tail, index + 1))
604
+ s(:begin,
605
+ s(:and,
606
+ node,
607
+ array_rest_element(*tail, index + 1)))
590
608
  end
591
609
  end
592
610
 
@@ -610,7 +628,7 @@ module RubyNext
610
628
  children = node.children.map do |child, i|
611
629
  send :"#{child.type}_array_element", child, index
612
630
  end
613
- s(:or, *children)
631
+ s(:begin, s(:or, *children))
614
632
  end
615
633
 
616
634
  def match_var_array_element(node, index)
@@ -661,18 +679,20 @@ module RubyNext
661
679
  elsif specified_key_names.empty?
662
680
  hash_element(*node.children)
663
681
  else
664
- s(:and,
665
- having_hash_keys(specified_key_names),
666
- hash_element(*node.children))
682
+ s(:begin,
683
+ s(:and,
684
+ having_hash_keys(specified_key_names),
685
+ hash_element(*node.children)))
667
686
  end
668
687
 
669
688
  predicates.pop
670
689
 
671
690
  next dnode if right.nil?
672
691
 
673
- s(:and,
674
- dnode,
675
- right)
692
+ s(:begin,
693
+ s(:and,
694
+ dnode,
695
+ right))
676
696
  end
677
697
  end
678
698
 
@@ -715,7 +735,7 @@ module RubyNext
715
735
  # Duplicate the source hash when matching **rest, 'cause we mutate it
716
736
  hash_dup =
717
737
  if @hash_match_rest
718
- s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup))
738
+ s(:begin, s(:lvasgn, locals[:hash], s(:send, s(:lvar, locals[:hash, :src]), :dup)))
719
739
  else
720
740
  s(:true)
721
741
  end
@@ -728,29 +748,33 @@ module RubyNext
728
748
  key_names = keys.children.map { |node| node.children.last }
729
749
  predicates.push locals[:hash]
730
750
 
731
- s(:lvasgn, deconstruct_name,
751
+ s(:begin, s(:lvasgn, deconstruct_name,
732
752
  s(:send,
733
- matchee, :deconstruct_keys, keys)).then do |dnode|
753
+ matchee, :deconstruct_keys, keys))).then do |dnode|
734
754
  next dnode if respond_to_checked
735
755
 
736
756
  s(:and,
737
757
  respond_check,
738
- s(:and,
739
- s(:or,
740
- dnode,
741
- s(:true)),
742
- s(:or,
743
- s(:send,
744
- s(:const, nil, :Hash), :===, s(:lvar, deconstruct_name)),
745
- raise_error(:TypeError, "#deconstruct_keys must return Hash"))))
758
+ s(:begin,
759
+ s(:and,
760
+ s(:begin,
761
+ s(:or,
762
+ dnode,
763
+ s(:true))),
764
+ s(:begin,
765
+ s(:or,
766
+ s(:send,
767
+ s(:const, nil, :Hash), :===, s(:lvar, deconstruct_name)),
768
+ raise_error(:TypeError, "#deconstruct_keys must return Hash"))))))
746
769
  end.then do |dnode|
747
770
  predicates.hash_deconstructed(dnode, key_names)
748
771
  end.then do |dnode|
749
772
  next dnode unless @hash_match_rest
750
773
 
751
- s(:and,
752
- dnode,
753
- hash_dup)
774
+ s(:begin,
775
+ s(:and,
776
+ dnode,
777
+ hash_dup))
754
778
  end
755
779
  end
756
780
 
@@ -780,9 +804,10 @@ module RubyNext
780
804
 
781
805
  next node if right.nil?
782
806
 
783
- s(:and,
784
- node,
785
- right)
807
+ s(:begin,
808
+ s(:and,
809
+ node,
810
+ right))
786
811
  end
787
812
  end
788
813
 
@@ -792,7 +817,7 @@ module RubyNext
792
817
  end
793
818
 
794
819
  def match_alt_hash_element(node, key)
795
- element_node = s(:lvasgn, locals[:hash, :el], hash_value_at(key))
820
+ element_node = s(:begin, s(:lvasgn, locals[:hash, :el], hash_value_at(key)))
796
821
 
797
822
  children = locals.with(hash_element: locals[:hash, :el]) do
798
823
  node.children.map do |child, i|
@@ -800,11 +825,14 @@ module RubyNext
800
825
  end
801
826
  end
802
827
 
803
- s(:and,
804
- s(:or,
805
- element_node,
806
- s(:true)),
807
- s(:or, *children))
828
+ s(:begin,
829
+ s(:and,
830
+ s(:begin,
831
+ s(:or,
832
+ element_node,
833
+ s(:true))),
834
+ s(:begin,
835
+ s(:or, *children))))
808
836
  end
809
837
 
810
838
  def match_as_hash_element(node, key)
@@ -858,13 +886,14 @@ module RubyNext
858
886
  end
859
887
 
860
888
  def having_hash_keys(keys, hash = s(:lvar, locals[:hash]))
861
- key = keys.shift
862
- node = predicates.hash_key(hash_has_key(key, hash), key)
889
+ keys.reduce(nil) do |acc, key|
890
+ pnode = hash_has_key(key, hash)
891
+ next pnode unless acc
863
892
 
864
- keys.reduce(node) do |res, key|
865
- s(:and,
866
- res,
867
- predicates.hash_key(hash_has_key(key, hash), key))
893
+ s(:begin,
894
+ s(:and, acc, pnode))
895
+ end.then do |node|
896
+ predicates.hash_keys(node, keys)
868
897
  end
869
898
  end
870
899
 
@@ -873,9 +902,10 @@ module RubyNext
873
902
  def with_guard(node, guard)
874
903
  return node unless guard
875
904
 
876
- s(:and,
877
- node,
878
- guard.children[0]).then do |expr|
905
+ s(:begin,
906
+ s(:and,
907
+ node,
908
+ guard.children[0])).then do |expr|
879
909
  next expr unless guard.type == :unless_guard
880
910
  s(:send, expr, :!)
881
911
  end
@@ -933,10 +963,10 @@ module RubyNext
933
963
  deconstructed_keys[key] = :"k#{deconstructed_keys.size}"
934
964
  end
935
965
 
936
- # Unparser generates `do .. end` blocks, we want to
966
+ # Unparser generates `do .. end` or `{ ... }` multiline blocks, we want to
937
967
  # have single-line blocks with `{ ... }`.
938
968
  def inline_blocks(source)
939
- source.gsub(/do \|_, __i__\|\n\s*([^\n]+)\n\s*end/, '{ |_, __i__| \1 }')
969
+ source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
940
970
  end
941
971
  end
942
972
  end