ruby-next-core 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +31 -5
  4. data/bin/transform +5 -1
  5. data/lib/.rbnext/2.1/ruby-next/core.rb +7 -1
  6. data/lib/.rbnext/2.1/ruby-next/language.rb +41 -23
  7. data/lib/.rbnext/2.3/ruby-next/commands/nextify.rb +2 -2
  8. data/lib/.rbnext/2.3/ruby-next/language/eval.rb +1 -0
  9. data/lib/.rbnext/2.3/ruby-next/language/rewriters/{endless_range.rb → 2.6/endless_range.rb} +0 -0
  10. data/lib/.rbnext/2.3/ruby-next/language/rewriters/{pattern_matching.rb → 2.7/pattern_matching.rb} +53 -7
  11. data/lib/.rbnext/2.3/ruby-next/language/rewriters/3.1/anonymous_block.rb +68 -0
  12. data/lib/.rbnext/2.3/ruby-next/language/rewriters/base.rb +13 -1
  13. data/lib/.rbnext/2.7/ruby-next/core.rb +7 -1
  14. data/lib/ruby-next/commands/nextify.rb +2 -2
  15. data/lib/ruby-next/config.rb +2 -2
  16. data/lib/ruby-next/core/enumerable/compact.rb +22 -0
  17. data/lib/ruby-next/core/integer/try_convert.rb +16 -0
  18. data/lib/ruby-next/core/matchdata/match.rb +9 -0
  19. data/lib/ruby-next/core/refinement/import.rb +60 -0
  20. data/lib/ruby-next/core.rb +7 -1
  21. data/lib/ruby-next/language/eval.rb +1 -0
  22. data/lib/ruby-next/language/rewriters/{numeric_literals.rb → 2.1/numeric_literals.rb} +0 -0
  23. data/lib/ruby-next/language/rewriters/{required_kwargs.rb → 2.1/required_kwargs.rb} +0 -0
  24. data/lib/ruby-next/language/rewriters/{safe_navigation.rb → 2.3/safe_navigation.rb} +0 -0
  25. data/lib/ruby-next/language/rewriters/{squiggly_heredoc.rb → 2.3/squiggly_heredoc.rb} +0 -0
  26. data/lib/ruby-next/language/rewriters/{runtime → 2.4}/dir.rb +0 -0
  27. data/lib/ruby-next/language/rewriters/{endless_range.rb → 2.6/endless_range.rb} +0 -0
  28. data/lib/ruby-next/language/rewriters/{args_forward.rb → 2.7/args_forward.rb} +0 -0
  29. data/lib/ruby-next/language/rewriters/{numbered_params.rb → 2.7/numbered_params.rb} +0 -0
  30. data/lib/ruby-next/language/rewriters/{pattern_matching.rb → 2.7/pattern_matching.rb} +53 -7
  31. data/lib/ruby-next/language/rewriters/{args_forward_leading.rb → 3.0/args_forward_leading.rb} +0 -0
  32. data/lib/ruby-next/language/rewriters/{endless_method.rb → 3.0/endless_method.rb} +15 -12
  33. data/lib/ruby-next/language/rewriters/{find_pattern.rb → 3.0/find_pattern.rb} +1 -3
  34. data/lib/ruby-next/language/rewriters/3.0/in_pattern.rb +22 -0
  35. data/lib/ruby-next/language/rewriters/3.1/anonymous_block.rb +68 -0
  36. data/lib/ruby-next/language/rewriters/3.1/endless_method_command.rb +46 -0
  37. data/lib/ruby-next/language/rewriters/3.1/oneline_pattern_parensless.rb +41 -0
  38. data/lib/ruby-next/language/rewriters/3.1/pin_vars_pattern.rb +50 -0
  39. data/lib/ruby-next/language/rewriters/3.1/refinement_import_methods.rb +60 -0
  40. data/lib/ruby-next/language/rewriters/{shorthand_hash.rb → 3.1/shorthand_hash.rb} +6 -4
  41. data/lib/ruby-next/language/rewriters/base.rb +13 -1
  42. data/lib/ruby-next/language/{edge.rb → rewriters/edge.rb} +0 -0
  43. data/lib/ruby-next/language/rewriters/proposed/bind_vars_pattern.rb +50 -0
  44. data/lib/ruby-next/language/rewriters/{method_reference.rb → proposed/method_reference.rb} +0 -0
  45. data/lib/ruby-next/language/rewriters/proposed.rb +9 -0
  46. data/lib/ruby-next/language/rewriters/runtime.rb +1 -2
  47. data/lib/ruby-next/language/setup.rb +2 -1
  48. data/lib/ruby-next/language.rb +41 -23
  49. data/lib/ruby-next/rubocop.rb +24 -4
  50. data/lib/ruby-next/version.rb +1 -1
  51. metadata +35 -24
  52. data/lib/ruby-next/language/proposed.rb +0 -9
  53. data/lib/ruby-next/language/rewriters/in_pattern.rb +0 -56
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # We cannot use refinements here, since Ruby 2.6- doesn't support them in refine modules.
4
+ # So, we use a defined method instead (and transpile source code to use it).
5
+ # NOTE: We have to transpile the source code anyway, since we need to pass a binding.
6
+ RubyNext::Core.singleton_class.module_eval do
7
+ def import_methods(other, bind)
8
+ import = []
9
+
10
+ other.instance_methods(false).each do |mid|
11
+ # check for non-Ruby methods
12
+ meth = other.instance_method(mid)
13
+ location = meth.source_location
14
+
15
+ if location.nil? || location.first.match?(/(<internal:|resource:\/truffleruby\/core)/)
16
+ raise ArgumentError, "Can't import method: #{other}##{mid} from #{location}"
17
+ end
18
+
19
+ source_file, lineno = *location
20
+
21
+ raise ArgumentError, "Can't import dynamicly added methods: #{other}##{mid}" unless File.file?(source_file)
22
+
23
+ lines = File.open(source_file).readlines
24
+
25
+ buffer = []
26
+
27
+ lines[(lineno - 1)..-1].each do |line|
28
+ buffer << line + "\n"
29
+
30
+ begin
31
+ if defined?(::RubyNext::Language) && ::RubyNext::Language.runtime?
32
+ new_source = ::RubyNext::Language.transform(buffer.join, rewriters: RubyNext::Language.current_rewriters, using: false)
33
+ # Transformed successfully => valid method => evaluate transpiled code
34
+ import << [new_source, source_file, lineno]
35
+ buffer.clear
36
+ break
37
+ end
38
+
39
+ # Borrowed from https://github.com/banister/method_source/blob/81d039c966ffd95d26e12eb2e205c0eb8377f49d/lib/method_source/code_helpers.rb#L66
40
+ catch(:valid) do
41
+ eval("BEGIN{throw :valid}\nObject.new.instance_eval { #{buffer.join} }") # rubocop:disable all
42
+ end
43
+ break
44
+ rescue SyntaxError
45
+ end
46
+ end
47
+
48
+ import << [buffer.join, source_file, lineno] unless buffer.empty?
49
+ end
50
+
51
+ import.each do |(definition, file, lino)|
52
+ Kernel.eval definition, bind, file, lino
53
+ end
54
+
55
+ # Copy constants (they could be accessed from methods)
56
+ other.constants.each do |name|
57
+ Kernel.eval "#{name} = #{other}::#{name}", bind
58
+ end
59
+ end
60
+ end
@@ -62,7 +62,7 @@ module RubyNext
62
62
  mod_name = singleton? ? singleton.name : mod.name
63
63
  camelized_method_name = method_name.to_s.split("_").map(&:capitalize).join
64
64
 
65
- "#{mod_name}#{camelized_method_name}".gsub(/\W/, "")
65
+ "#{mod_name}#{camelized_method_name}".gsub(/\W/, "") # rubocop:disable Performance/StringReplacement
66
66
  end
67
67
 
68
68
  def build_location(trace_locations)
@@ -173,6 +173,8 @@ require "ruby-next/core/unboundmethod/bind_call"
173
173
  require "ruby-next/core/time/floor"
174
174
  require "ruby-next/core/time/ceil"
175
175
 
176
+ require "ruby-next/core/refinement/import"
177
+
176
178
  # Core extensions required for pattern matching
177
179
  # Required for pattern matching with refinements
178
180
  unless defined?(NoMatchingPatternError)
@@ -191,6 +193,10 @@ require "ruby-next/core/hash/except"
191
193
 
192
194
  require "ruby-next/core/array/intersect"
193
195
 
196
+ require "ruby-next/core/matchdata/match"
197
+ require "ruby-next/core/enumerable/compact"
198
+ require "ruby-next/core/integer/try_convert"
199
+
194
200
  # Generate refinements
195
201
  RubyNext.module_eval do
196
202
  RubyNext::Core.patches.refined.each do |mod, patches|
@@ -37,6 +37,7 @@ module RubyNext
37
37
 
38
38
  source = args.shift
39
39
  new_source = ::RubyNext::Language::Runtime.transform(source, using: false)
40
+
40
41
  RubyNext.debug_source(new_source, "(#{caller_locations(1, 1).first})")
41
42
  super new_source, *args
42
43
  end
@@ -306,6 +306,38 @@ module RubyNext
306
306
 
307
307
  alias on_in_match on_match_pattern
308
308
 
309
+ def on_match_pattern_p(node)
310
+ context.track! self
311
+
312
+ @deconstructed_keys = {}
313
+ @predicates = Predicates::Noop.new
314
+
315
+ matchee =
316
+ s(:begin, s(:lvasgn, MATCHEE, node.children[0]))
317
+
318
+ pattern =
319
+ locals.with(
320
+ matchee: MATCHEE,
321
+ arr: MATCHEE_ARR,
322
+ hash: MATCHEE_HASH
323
+ ) do
324
+ send(
325
+ :"#{node.children[1].type}_clause",
326
+ node.children[1]
327
+ )
328
+ end
329
+
330
+ node.updated(
331
+ :and,
332
+ [
333
+ matchee,
334
+ pattern
335
+ ]
336
+ ).tap do |new_node|
337
+ replace(node.loc.expression, inline_blocks(unparse(new_node)))
338
+ end
339
+ end
340
+
309
341
  private
310
342
 
311
343
  def rewrite_case_in!(node, matchee, new_node)
@@ -407,13 +439,14 @@ module RubyNext
407
439
  end
408
440
 
409
441
  def match_var_clause(node, left = s(:lvar, locals[:matchee]))
410
- return s(:true) if node.children[0] == :_
442
+ var = node.children[0]
443
+ return s(:true) if var == :_
411
444
 
412
- check_match_var_alternation! node.children[0]
445
+ check_match_var_alternation!(var) unless var.is_a?(::Parser::AST::Node)
413
446
 
414
447
  s(:begin,
415
448
  s(:or,
416
- s(:begin, s(:lvasgn, node.children[0], left)),
449
+ s(:begin, build_var_assignment(var, left)),
417
450
  s(:true)))
418
451
  end
419
452
 
@@ -512,7 +545,7 @@ module RubyNext
512
545
 
513
546
  head_match =
514
547
  unless head.children.empty?
515
- match_vars << s(:lvasgn, head.children[0].children[0])
548
+ match_vars << build_var_assignment(head.children[0].children[0])
516
549
 
517
550
  arr_take = s(:send,
518
551
  s(:lvar, locals[:arr]),
@@ -524,16 +557,16 @@ module RubyNext
524
557
 
525
558
  tail_match =
526
559
  unless tail.children.empty?
527
- match_vars << s(:lvasgn, tail.children[0].children[0])
560
+ match_vars << build_var_assignment(tail.children[0].children[0])
528
561
 
529
562
  match_var_clause(tail.children[0], arr_slice(index + nodes.size, -1))
530
563
  end
531
564
 
532
565
  nodes.each do |node|
533
566
  if node.type == :match_var
534
- match_vars << s(:lvasgn, node.children[0])
567
+ match_vars << build_var_assignment(node.children[0])
535
568
  elsif node.type == :match_as
536
- match_vars << s(:lvasgn, node.children[1].children[0])
569
+ match_vars << build_var_assignment(node.children[1].children[0])
537
570
  end
538
571
  end
539
572
 
@@ -861,6 +894,10 @@ module RubyNext
861
894
  match_var_clause(child, s(:lvar, locals[:hash]))
862
895
  end
863
896
 
897
+ def pin_hash_element(node, index)
898
+ case_eq_hash_element node.children[0], index
899
+ end
900
+
864
901
  def case_eq_hash_element(node, key)
865
902
  case_eq_clause node, hash_value_at(key)
866
903
  end
@@ -968,6 +1005,15 @@ module RubyNext
968
1005
  def inline_blocks(source)
969
1006
  source.gsub(/(?:do|{) \|_, __i__\|\n\s*([^\n]+)\n\s*(?:end|})/, '{ |_, __i__| \1 }')
970
1007
  end
1008
+
1009
+ # Value could be omitted for mass assignment
1010
+ def build_var_assignment(var, value = nil)
1011
+ return s(:lvasgn, *[var, value].compact) unless var.is_a?(::Parser::AST::Node)
1012
+
1013
+ asign_type = :"#{var.type.to_s[0]}vasgn"
1014
+
1015
+ s(asign_type, *[var.children[0], value].compact)
1016
+ end
971
1017
  end
972
1018
  end
973
1019
  end
@@ -8,14 +8,13 @@ module RubyNext
8
8
  SYNTAX_PROBE = "obj = Object.new; def obj.foo() = 42"
9
9
  MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
10
10
 
11
- unless Parser::Meta::NODE_TYPES.include?(:def_e)
12
- def on_def(node)
13
- return on_def_e(node) if node.loc.end.nil?
14
- super(node)
15
- end
11
+ def on_def(node)
12
+ return process_def(node) if endless?(node)
13
+
14
+ super(node)
16
15
  end
17
16
 
18
- def on_def_e(node)
17
+ def process_def(node)
19
18
  context.track! self
20
19
 
21
20
  replace(node.loc.assignment, "; ")
@@ -33,14 +32,12 @@ module RubyNext
33
32
  )
34
33
  end
35
34
 
36
- unless Parser::Meta::NODE_TYPES.include?(:def_e)
37
- def on_defs(node)
38
- return on_defs_e(node) if node.loc.end.nil?
39
- super(node)
40
- end
35
+ def on_defs(node)
36
+ return process_defs(node) if endless?(node)
37
+ super(node)
41
38
  end
42
39
 
43
- def on_defs_e(node)
40
+ def process_defs(node)
44
41
  context.track! self
45
42
 
46
43
  replace(node.loc.assignment, "; ")
@@ -57,6 +54,12 @@ module RubyNext
57
54
  )
58
55
  )
59
56
  end
57
+
58
+ private
59
+
60
+ def endless?(node)
61
+ node.loc.end.nil?
62
+ end
60
63
  end
61
64
  end
62
65
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ruby-next/language/rewriters/pattern_matching"
4
-
5
3
  module RubyNext
6
4
  module Language
7
5
  module Rewriters
@@ -22,7 +20,7 @@ module RubyNext
22
20
  end
23
21
  end
24
22
 
25
- def on_in_match(node)
23
+ def on_match_pattern(node)
26
24
  @has_find_pattern = false
27
25
  process_regular_node(node).then do |new_node|
28
26
  return new_node unless has_find_pattern
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ using RubyNext
7
+
8
+ # Separate pattern matching rewriter for Ruby 2.7 to
9
+ # transpile only `in` patterns
10
+ class InPattern < PatternMatching
11
+ NAME = "pattern-matching-in"
12
+ SYNTAX_PROBE = "1 in 2"
13
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.0.0")
14
+
15
+ # Make case-match no-op
16
+ def on_case_match(node)
17
+ process_regular_node(node)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class AnonymousBlock < Base
7
+ NAME = "anonymous-block"
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo(&) bar(&); end"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
10
+
11
+ BLOCK = :__block__
12
+
13
+ def on_args(node)
14
+ block = node.children.last
15
+
16
+ return super unless block&.type == :blockarg
17
+ return super unless block.children.first.nil?
18
+
19
+ context.track! self
20
+
21
+ replace(block.loc.expression, "&#{BLOCK}")
22
+
23
+ node.updated(
24
+ :args,
25
+ [
26
+ *node.children.slice(0, node.children.index(block)),
27
+ s(:blockarg, BLOCK)
28
+ ]
29
+ )
30
+ end
31
+
32
+ def on_send(node)
33
+ block = extract_block_pass(node)
34
+ return super unless block&.children == [nil]
35
+
36
+ process_block(node, block)
37
+ end
38
+
39
+ def on_super(node)
40
+ block = extract_block_pass(node)
41
+ return super unless block&.children == [nil]
42
+
43
+ process_block(node, block)
44
+ end
45
+
46
+ private
47
+
48
+ def extract_block_pass(node)
49
+ node.children.find { |child| child.is_a?(::Parser::AST::Node) && child.type == :block_pass }
50
+ end
51
+
52
+ def process_block(node, block)
53
+ replace(block.loc.expression, "&#{BLOCK}")
54
+
55
+ process(
56
+ node.updated(
57
+ nil,
58
+ [
59
+ *node.children.take(node.children.index(block)),
60
+ s(:block_pass, s(:lvar, BLOCK))
61
+ ]
62
+ )
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ class EndlessMethodCommand < EndlessMethod
7
+ NAME = "endless-method-command"
8
+ SYNTAX_PROBE = "obj = Object.new; def obj.foo = puts 'Hello'"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
10
+
11
+ def process_def(node)
12
+ return node unless command?(node)
13
+
14
+ super(node)
15
+ end
16
+
17
+ def process_defs(node)
18
+ return node unless command?(node)
19
+
20
+ super(node)
21
+ end
22
+
23
+ private
24
+
25
+ def command?(node)
26
+ buffer = ::Parser::Source::Buffer.new("(endless-method-rewriter)").tap do |buffer|
27
+ buffer.source = node.loc.expression.source
28
+ end
29
+
30
+ parser30.parse(buffer)
31
+ false
32
+ rescue ::Parser::SyntaxError
33
+ true
34
+ end
35
+
36
+ def parser30
37
+ require "parser/ruby30" unless defined?(::Parser::Ruby30)
38
+
39
+ ::Parser::Ruby30.new(Language::Builder.new).tap do |prs|
40
+ prs.diagnostics.all_errors_are_fatal = true
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ using RubyNext
7
+
8
+ # Allow omitting parentheses around patterns in `=>` and `in`
9
+ class OnelinePatternParensless < Base
10
+ NAME = "pattern-matching-oneline-parensless"
11
+ SYNTAX_PROBE = "[1, 2] => a, b"
12
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
13
+
14
+ def on_match_pattern(node)
15
+ _, pattern = *node.children
16
+
17
+ # When no parens, children boundaries are the same as the whole pattern
18
+ if (
19
+ pattern.type == :array_pattern ||
20
+ pattern.type == :hash_pattern
21
+ ) &&
22
+ pattern.children.any? &&
23
+ pattern.loc.column == pattern.children.first.loc.column &&
24
+ pattern.loc.last_column == pattern.children.last.loc.last_column
25
+
26
+ context.track! self
27
+
28
+ left_p, right_p = pattern.type == :array_pattern ? %w([ ]) : %w[{ }]
29
+
30
+ insert_before(pattern.loc.expression, left_p)
31
+ insert_after(pattern.loc.expression, right_p)
32
+ else
33
+ super(node)
34
+ end
35
+ end
36
+
37
+ alias on_match_pattern_p on_match_pattern
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyNext
4
+ module Language
5
+ module Rewriters
6
+ using RubyNext
7
+
8
+ # Separate pattern matching rewriter for Ruby 2.7 and 3.0 to
9
+ # transpile only ^(ivar|cvar|gvar)
10
+ class PinVarsPattern < PatternMatching
11
+ NAME = "pattern-matching-pin-vars"
12
+ SYNTAX_PROBE = "@a = 0; case 0; in ^@a; true; end"
13
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
14
+
15
+ def on_case_match(node)
16
+ @has_pin_vars = false
17
+ process_regular_node(node).then do |new_node|
18
+ return new_node unless has_pin_vars
19
+ super(node)
20
+ end
21
+ end
22
+
23
+ def on_match_pattern(node)
24
+ @has_pin_vars = false
25
+ process_regular_node(node).then do |new_node|
26
+ return new_node unless has_pin_vars
27
+ super(node)
28
+ end
29
+ end
30
+
31
+ def on_match_pattern_p(node)
32
+ @has_pin_vars = false
33
+ process_regular_node(node).then do |new_node|
34
+ return new_node unless has_pin_vars
35
+ super(node)
36
+ end
37
+ end
38
+
39
+ def on_pin(node)
40
+ @has_pin_vars = node.children.first.type != :lvar
41
+ super(node)
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :has_pin_vars
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add binding argument to all self-less eval's
4
+ module RubyNext
5
+ module Language
6
+ module Rewriters
7
+ class RefinementImportMethods < Language::Rewriters::Base
8
+ NAME = "refinement-import-methods"
9
+ SYNTAX_PROBE = "a = Module.new{}; Module.new do; refine String do; import_methods a end; end"
10
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
11
+
12
+ def on_block(node)
13
+ sender, args, body = *node
14
+ receiver, mid, * = *sender
15
+
16
+ return super unless mid == :refine && receiver.nil?
17
+
18
+ return super unless body
19
+
20
+ @within_refinement = true
21
+
22
+ node.updated(
23
+ nil,
24
+ [
25
+ sender,
26
+ args,
27
+ process(body)
28
+ ]
29
+ ).tap do
30
+ @within_refinement = false
31
+ end
32
+ end
33
+
34
+ def on_send(node)
35
+ return super unless @within_refinement
36
+
37
+ _receiver, mid, *children = *node
38
+
39
+ return super unless mid == :import_methods
40
+
41
+ context.track! self
42
+
43
+ updated = node.updated(
44
+ nil,
45
+ [
46
+ s(:const, s(:const, s(:cbase), :RubyNext), :Core),
47
+ mid,
48
+ *children,
49
+ s(:send, nil, :binding)
50
+ ]
51
+ )
52
+
53
+ replace(node.loc.expression, updated)
54
+
55
+ updated
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -5,13 +5,15 @@ module RubyNext
5
5
  module Rewriters
6
6
  class ShorthandHash < Base
7
7
  NAME = "shorthand-hash"
8
- SYNTAX_PROBE = "data = {x}"
9
- MIN_SUPPORTED_VERSION = Gem::Version.new(RubyNext::NEXT_VERSION)
8
+ SYNTAX_PROBE = "data = {x:}"
9
+ MIN_SUPPORTED_VERSION = Gem::Version.new("3.1.0")
10
+
11
+ def on_pair(node)
12
+ return super(node) unless node.children[0].loc.last_column == node.children[1].loc.last_column
10
13
 
11
- def on_ipair(node)
12
14
  context.track! self
13
15
 
14
- ident, = *node.children
16
+ _, ident, = *node.children
15
17
 
16
18
  key = key_from_ident(ident)
17
19
 
@@ -15,6 +15,18 @@ module RubyNext
15
15
 
16
16
  class Base < ::Parser::TreeRewriter
17
17
  class LocalsTracker
18
+ using(Module.new do
19
+ refine ::Parser::AST::Node do
20
+ def to_index
21
+ children&.first || type
22
+ end
23
+ end
24
+
25
+ refine ::Object do
26
+ alias to_index itself
27
+ end
28
+ end)
29
+
18
30
  attr_reader :stacks
19
31
 
20
32
  def initialize
@@ -29,7 +41,7 @@ module RubyNext
29
41
  def [](name, suffix = nil)
30
42
  fetch(name).then do |name|
31
43
  next name unless suffix
32
- :"#{name}#{suffix}__"
44
+ :"#{name}#{suffix.to_index}__"
33
45
  end
34
46
  end
35
47