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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b97da767efae69a9faa9ed2b4c4a3daf9c38a5b70768502b013692b7a3c04a50
4
- data.tar.gz: 05ba2f4c4677c40d46a451a68a94e434b54d4e9daa9a2af51cbd508ef0a198ab
3
+ metadata.gz: 37dbd285f7d0d450d8458a2dc5c01e7b8c5f7744e54ea05d133ea7444dc9704c
4
+ data.tar.gz: 7a881b5d76d032ce4ee5a94515542bff75a3a5b899b8412d828d9c4a27728eda
5
5
  SHA512:
6
- metadata.gz: ef678d9b8ed431a8a8f005b00c445da56096c39a3c9ffc1ce88708c4b1280e630cb5c58a63a154381c384b54968ec19b3c88a4a49665b3503bd5df62f454a168
7
- data.tar.gz: a25ebf4e0c3456213a1f682d27df031a6add5bc117800026d8b0f9b48cacb49e72a4b97115f9b1b1acf058ca3f6974dfa94402498a58a19ac532916eb9a81199
6
+ metadata.gz: '0971fe638bfbd8c1ac3e5aa5485457fd9e9e70cfbb6f6430222d18ae13ce1820e97f3e28f93393d8c4ecc5578df0113d229b0179f5f07a2e1e19ae94087112ff'
7
+ data.tar.gz: aefa941c90244ae1e7c68eb7bae6f79c2fb936495e28158f8eb18a8b90dc8eb7dcbf35a50d133b8b1653a0bbb118469c989ed97cb6e7039d383c6264aca2f6bd
@@ -2,10 +2,40 @@
2
2
 
3
3
  ## master
4
4
 
5
- ## 0.10.1 (2020-09-09)
5
+ ## 0.11.0 (2020-12-24) 🎄
6
+
7
+ - Extended proposed shorthand Hash syntax to support kwargs as well. ([@palkan][])
8
+
9
+ You can try it: `x = 1; y = 2; foo(x:, y:) # => foo(x: x, y: y)`.
10
+
11
+ - **Rewrite mode is used by default in transpiler**. ([@palkan][])
12
+
13
+ - Move 3.0 features to stable features. ([@palkan][])
14
+
15
+ - Refactor `a => x` and `a in x` to comply with Ruby 3.0. ([@palkan][])
16
+
17
+ ## 0.10.5 (2020-10-13)
18
+
19
+ - Fix Unparser 0.5.0 compatibility. ([@palkan][])
20
+
21
+ ## 0.10.4 (2020-10-09)
22
+
23
+ - Restrict Unparser dependency. ([@palkan][])
24
+
25
+ Unparser 0.5.0 is currently not supported.
26
+
27
+ ## 0.10.3 (2020-09-28)
28
+
29
+ - Update RuboCop integration to handle the latest Parser changes. ([@palkan][])
30
+
31
+ Parser 2.7.1.5 unified endless and normal methods and rightward and leftward assignments, thus, making some cops report false negatives.
32
+
33
+ ## 0.10.2 (2020-09-09)
6
34
 
7
35
  - Fix regression when `nextify` produces incorrect files for 2.7. ([@palkan][])
8
36
 
37
+ ## ~~0.10.1~~
38
+
9
39
  ## 0.10.0 (2020-09-02)
10
40
 
11
41
  - Add proposed shorthand Hash syntax. ([@palkan][])
data/README.md CHANGED
@@ -35,6 +35,7 @@ Read more about the motivation behind the Ruby Next in this post: [Ruby Next: Ma
35
35
  ## Examples
36
36
 
37
37
  - Ruby gems
38
+ - [action_policy](https://github.com/palkan/action_policy)
38
39
  - [anyway_config](https://github.com/palkan/anyway_config)
39
40
  - [graphql-fragment_cache](https://github.com/DmitryTsepelev/graphql-ruby-fragment_cache)
40
41
  - Rails applications
@@ -92,8 +93,7 @@ def greet(val) =
92
93
  '👽'
93
94
  end
94
95
 
95
- greet(hello: 'martian') => greeting
96
- puts greeting
96
+ puts greet(hello: 'martian')
97
97
  "
98
98
 
99
99
  => 👽
@@ -178,17 +178,17 @@ In the AST mode, we parse the source code into AST, modifies this AST and **gene
178
178
 
179
179
  In the rewrite mode, we apply changes to the source code itself, thus, keeping the original formatting of the unaffected code (in a similar way to RuboCop's autocorrect feature).
180
180
 
181
- By default, we use the AST mode. That could likely change in the future when we collect enough feedback on the rewrite mode and fix potential bugs.
182
-
183
181
  The main benefit of the rewrite mode is that it preserves the original code line numbers and layout, which is especially useful in debugging.
184
182
 
183
+ By default, we use the rewrite mode. If you found a bug with rewrite mode which is not reproducible in the AST mode, please, let us know.
184
+
185
185
  You can change the transpiler mode:
186
186
 
187
187
  - From code by setting `RubyNext::Language.mode = :ast` or `RubyNext::Language.mode = :rewrite`.
188
- - Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=rewrite`.
188
+ - Via environmental variable `RUBY_NEXT_TRANSPILE_MODE=ast`.
189
189
  - Via CLI option ([see below](#cli)).
190
190
 
191
- **NOTE:** For the time being, Unparser [doesn't support](https://github.com/mbj/unparser/pull/142) new Ruby 2.7 AST nodes, so we always use rewrite mode in Ruby 2.7+.
191
+ **NOTE:** For the time being, Unparser doesn't support Ruby 3.0 AST nodes, so we always use rewrite mode in Ruby 3.0+.
192
192
 
193
193
  ## CLI
194
194
 
@@ -219,7 +219,7 @@ Usage: ruby-next nextify DIRECTORY_OR_FILE [options]
219
219
 
220
220
  The behaviour depends on whether you transpile a single file or a directory:
221
221
 
222
- - When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.6`, `.rbnext/2.7`). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
222
+ - When transpiling a directory, the `.rbnext` subfolder is created within the target folder with subfolders for each supported Ruby versions (e.g., `.rbnext/2.6`, `.rbnext/2.7`, `.rbnext/3.0`). If you want to create only a single version (the smallest), you can also pass `--single-version` flag. In that case, no version directory is created (i.e., transpiled files go into `.rbnext`).
223
223
 
224
224
  - When transpiling a file and providing the output path as a _file_ path, only a single version is created. For example:
225
225
 
@@ -322,7 +322,7 @@ due to the way feature resolving works in Ruby (scanning the `$LOAD_PATH` and ha
322
322
 
323
323
  If you're using [runtime mode](#runtime-usage) a long with `setup_gem_load_path` (e.g., in tests), the transpiled files are ignored (i.e., we do not modify `$LOAD_PATH`).
324
324
 
325
- \* Ruby Next avoids storing duplicates; instead, only the code for the earlier version is created and is assumed to be used with other versions. For example, if the transpiled code is the same for Ruby 2.5 and Ruby 2.6, only the `.rbnext/2.7/path/to/file.rb` is kept. That's why multiple entries are added to the `$LOAD_PATH` (`.rbnext/2.6` and `.rbnext/2.7` in the specified order for Ruby 2.5 and only `.rbnext/2.7` for Ruby 2.6).
325
+ \* Ruby Next avoids storing duplicates; instead, only the code for the earlier version is created and is assumed to be used with other versions. For example, if the transpiled code is the same for Ruby 2.5 and Ruby 2.6, only the `.rbnext/2.7/path/to/file.rb` is kept. That's why multiple entries are added to the `$LOAD_PATH` (`.rbnext/2.6`, `.rbnext/2.7`, and `.rbnext/3.0` in the specified order for Ruby 2.5, and `.rbnext/2.7` and `.rbnext/3.0` for Ruby 2.6).
326
326
 
327
327
  ### Transpiled files vs. VCS vs. installing from source
328
328
 
@@ -475,6 +475,8 @@ RUBY_NEXT_CORE_STRATEGY=backports ruby-next nextify lib/
475
475
 
476
476
  **NOTE:** For Ruby 2.2, safe navigation operator (`&.`) and squiggly heredocs (`<<~TXT`) support is provided.
477
477
 
478
+ **IMPORTANT:** Unparser `~> 0.4.8` is required to run the transpiler on Ruby <2.4.
479
+
478
480
  ## Proposed and edge features
479
481
 
480
482
  Ruby Next aims to bring edge and proposed features to Ruby community before they (hopefully) reach an official Ruby release.
@@ -506,17 +508,13 @@ require "ruby-next/language/runtime"
506
508
 
507
509
  ### Supported edge features
508
510
 
509
- - "Endless" method definition (`def foo() = 42`) ([#16746](https://bugs.ruby-lang.org/issues/16746)).
510
-
511
- - Right-hand assignment (`13.divmod(5) => a,b`) ([#15921](https://bugs.ruby-lang.org/issues/15921)).
512
-
513
- - Find pattern (`[0, 1, 2] in [*, 1 => a, *c]`) ([#16828](https://bugs.ruby-lang.org/issues/16828)).
511
+ No new features since 3.0 release.
514
512
 
515
513
  ### Supported proposed features
516
514
 
517
515
  - _Method reference_ operator (`.:`) ([#13581](https://bugs.ruby-lang.org/issues/13581)).
518
516
 
519
- - Shorthand Hash notation (`data = {x, y}`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
517
+ - Shorthand Hash/kwarg notation (`data = {x, y}` or `foo(x:, y:)`) ([#15236](https://bugs.ruby-lang.org/issues/15236)).
520
518
 
521
519
  ## Contributing
522
520
 
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path("../../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ $VERBOSE = nil
6
+
7
+ require "bundler/setup"
8
+
9
+ require "ruby-next/language"
10
+
11
+ contents =
12
+ if File.exist?(ARGV[0])
13
+ File.read(ARGV[0])
14
+ else
15
+ ARGV[0]
16
+ end
17
+
18
+ ast = RubyNext::Language.parse(contents)
19
+ puts Unparser.unparse(ast)
@@ -5,6 +5,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require "bundler/setup"
7
7
 
8
+ require "optparse"
9
+
10
+ begin
11
+ require "pry-byebug"
12
+ rescue LoadError
13
+ end
14
+
8
15
  ENV["RUBY_NEXT_EDGE"] = "1"
9
16
  ENV["RUBY_NEXT_PROPOSED"] = "1"
10
17
 
@@ -13,16 +20,23 @@ require "ruby-next/language/rewriters/runtime"
13
20
 
14
21
  contents =
15
22
  if File.exist?(ARGV[0])
16
- File.read(ARGV[0])
23
+ File.read(ARGV.shift)
17
24
  else
18
- ARGV[0]
25
+ ARGV.shift
19
26
  end
20
27
 
21
- opts =
22
- if ARGV[1] && ARGV[1] == "--current"
23
- {rewriters: RubyNext::Language.current_rewriters}
24
- else
25
- {}
28
+ transform_opts = {}
29
+
30
+ OptionParser.new do |opts|
31
+ opts.banner = "Usage: transform filepath_or_code [options]"
32
+
33
+ opts.on("--current", "Use rewriters for the current Ruby version only") do
34
+ transform_opts[:rewriters] = RubyNext::Language.current_rewriters
35
+ end
36
+
37
+ opts.on("--rewrite", "User rewrite transpiling mode") do
38
+ RubyNext::Language.mode = :rewrite
26
39
  end
40
+ end.parse!
27
41
 
28
- puts RubyNext::Language.transform(contents, **opts)
42
+ puts RubyNext::Language.transform(contents, **transform_opts)
@@ -57,7 +57,7 @@ module RubyNext
57
57
 
58
58
  opts.on(
59
59
  "--transpile-mode=MODE",
60
- "Transpiler mode (ast or rewrite). Default: ast"
60
+ "Transpiler mode (ast or rewrite). Default: rewrite"
61
61
  ) do |val|
62
62
  Language.mode = val.to_sym
63
63
  end
@@ -95,7 +95,7 @@ module RubyNext
95
95
  exit 0
96
96
  end
97
97
 
98
- unless ((!lib_path.nil?) || nil) && lib_path.then(&File.method(:exist?))
98
+ unless ((!lib_path.nil? || nil) && lib_path.then(&File.method(:exist?)))
99
99
  $stdout.puts "Path not found: #{lib_path}"
100
100
  $stdout.puts optparser.help
101
101
  exit 2
@@ -148,6 +148,9 @@ module RubyNext
148
148
 
149
149
  # Then, generate the source code for the next version
150
150
  transpile path, contents, version: version
151
+ rescue SyntaxError, StandardError => e
152
+ warn "Failed to transpile #{path}: #{e.class} — #{e.message}"
153
+ exit 1
151
154
  end
152
155
 
153
156
  def save(contents, path, version)
@@ -8,7 +8,7 @@ module RubyNext
8
8
  def eval(source, bind = nil, *args)
9
9
  new_source = ::RubyNext::Language::Runtime.transform(
10
10
  source,
11
- using: ((!bind.nil?) || nil) && bind.receiver == TOPLEVEL_BINDING.receiver || ((!(((!bind.nil?) || nil) && bind.receiver).nil?) || nil) && (((!bind.nil?) || nil) && bind.receiver).is_a?(Module)
11
+ using: ((!bind.nil? || nil) && bind.receiver) == TOPLEVEL_BINDING.receiver || ((!((!bind.nil? || nil) && bind.receiver).nil? || nil) && ((!bind.nil? || nil) && bind.receiver).is_a?(Module))
12
12
  )
13
13
  RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
14
14
  super new_source, bind, *args
@@ -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)
@@ -60,7 +60,7 @@ module RubyNext
60
60
  eval_mid = Kernel.respond_to?(:eval_without_ruby_next) ? :eval_without_ruby_next : :eval
61
61
  Kernel.send eval_mid, self::SYNTAX_PROBE, nil, __FILE__, __LINE__
62
62
  false
63
- rescue SyntaxError, NameError
63
+ rescue SyntaxError, StandardError
64
64
  true
65
65
  ensure
66
66
  $VERBOSE = save_verbose
@@ -72,10 +72,6 @@ module RubyNext
72
72
  self::MIN_SUPPORTED_VERSION > version
73
73
  end
74
74
 
75
- def min_supported_minor_version
76
- Gem::Version.new(self::MIN_SUPPORTED_VERSION.segments[0..1].join(".") + ".0")
77
- end
78
-
79
75
  private
80
76
 
81
77
  def transform(source)
@@ -97,25 +93,44 @@ module RubyNext
97
93
 
98
94
  private
99
95
 
96
+ # BFS with predicate block
97
+ def find_child(node)
98
+ queue = [node]
99
+
100
+ loop do
101
+ break if queue.empty?
102
+
103
+ child = queue.shift
104
+ next unless child.is_a?(::Parser::AST::Node)
105
+
106
+ return child if yield child
107
+
108
+ queue.push(*child.children)
109
+ end
110
+
111
+ nil
112
+ end
113
+
100
114
  def replace(range, ast)
101
- ((!@source_rewriter.nil?) || nil) && @source_rewriter.replace(range, unparse(ast))
115
+ ((!@source_rewriter.nil? || nil) && @source_rewriter.replace(range, unparse(ast)))
102
116
  end
103
117
 
104
118
  def remove(range)
105
- ((!@source_rewriter.nil?) || nil) && @source_rewriter.remove(range)
119
+ ((!@source_rewriter.nil? || nil) && @source_rewriter.remove(range))
106
120
  end
107
121
 
108
122
  def insert_after(range, ast)
109
- ((!@source_rewriter.nil?) || nil) && @source_rewriter.insert_after(range, unparse(ast))
123
+ ((!@source_rewriter.nil? || nil) && @source_rewriter.insert_after(range, unparse(ast)))
110
124
  end
111
125
 
112
126
  def insert_before(range, ast)
113
- ((!@source_rewriter.nil?) || nil) && @source_rewriter.insert_before(range, unparse(ast))
127
+ ((!@source_rewriter.nil? || nil) && @source_rewriter.insert_before(range, unparse(ast)))
114
128
  end
115
129
 
116
130
  def unparse(ast)
117
131
  return ast if ast.is_a?(String)
118
- Unparser.unparse(ast)
132
+
133
+ Unparser.unparse(ast).chomp
119
134
  end
120
135
 
121
136
  attr_reader :context
@@ -55,7 +55,7 @@ module RubyNext
55
55
  attr_reader :current_index
56
56
 
57
57
  def index_arg?(node)
58
- ((!(((!current_index.nil?) || nil) && current_index.children).nil?) || nil) && (((!current_index.nil?) || nil) && current_index.children).include?(node)
58
+ ((!((!current_index.nil? || nil) && current_index.children).nil? || nil) && ((!current_index.nil? || nil) && current_index.children).include?(node))
59
59
  end
60
60
  end
61
61
  end
@@ -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)
@@ -309,7 +313,7 @@ module RubyNext
309
313
  remove(node.children[0].loc.expression)
310
314
 
311
315
  node.children[1..-1].each.with_index do |clause, i|
312
- if ((!clause.nil?) || nil) && clause.type == :in_pattern
316
+ if ((!clause.nil? || nil) && clause.type) == :in_pattern
313
317
  # handle multiline clauses differently
314
318
  if clause.loc.last_line > clause.children[0].loc.last_line + 1
315
319
  height = clause.loc.last_line - clause.children[0].loc.last_line
@@ -340,7 +344,7 @@ module RubyNext
340
344
  clauses = []
341
345
 
342
346
  nodes.each do |clause|
343
- if ((!clause.nil?) || nil) && clause.type == :in_pattern
347
+ if ((!clause.nil? || nil) && clause.type) == :in_pattern
344
348
  clauses << build_when_clause(clause)
345
349
  else
346
350
  else_clause = process(clause)
@@ -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