rbi 0.3.9 → 0.3.10

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.
@@ -13,19 +13,21 @@ module RBI
13
13
 
14
14
  case node
15
15
  when Tree
16
- kinds = node.nodes.map { |child| group_kind(child) }
17
- kinds.uniq!
18
-
19
- groups = {}
20
- kinds.each { |kind| groups[kind] = Group.new(kind) }
21
-
22
- node.nodes.dup.each do |child|
16
+ # Single-pass: visit each child, compute its group_kind once, and classify.
17
+ # We avoid calling `child.detach` in a loop (which is O(n) per call due to
18
+ # Array#delete), and instead clear the parent's nodes array in O(1) after
19
+ # all children have been classified.
20
+ groups = {} #: Hash[Group::Kind, Group]
21
+ node.nodes.each do |child|
23
22
  visit(child)
24
- child.detach
25
- groups[group_kind(child)] << child
23
+ kind = group_kind(child)
24
+ group = groups[kind] ||= Group.new(kind)
25
+ child.parent_tree = nil
26
+ group << child
26
27
  end
28
+ node.nodes.clear
27
29
 
28
- groups.each { |_, group| node << group }
30
+ groups.each_value { |group| node << group }
29
31
  end
30
32
  end
31
33
 
@@ -464,11 +464,11 @@ module RBI
464
464
  # @override
465
465
  #: (Node other) -> bool
466
466
  def compatible_with?(other)
467
- return false unless other.is_a?(Method)
468
- return false unless name == other.name
469
- return false unless params == other.params
470
-
471
- sigs.empty? || other.sigs.empty? || sigs == other.sigs
467
+ other.is_a?(Method) &&
468
+ name == other.name &&
469
+ params == other.params &&
470
+ at_most_one_side_anonymous?(other) &&
471
+ (sigs.empty? || other.sigs.empty? || sigs == other.sigs)
472
472
  end
473
473
 
474
474
  # @override
@@ -477,10 +477,27 @@ module RBI
477
477
  return unless other.is_a?(Method)
478
478
 
479
479
  super
480
+
481
+ # If self is the all-anonymous side (since compatible_with? ensures
482
+ # at most one side is), adopt other's non-anonymous param names and sigs.
483
+ if params.all?(&:anonymous?)
484
+ @params = other.params.dup
485
+ @sigs = other.sigs.dup unless other.sigs.empty?
486
+ end
487
+
480
488
  other.sigs.each do |sig|
481
489
  sigs << sig unless sigs.include?(sig)
482
490
  end
483
491
  end
492
+
493
+ private
494
+
495
+ #: (Method other) -> bool
496
+ def at_most_one_side_anonymous?(other)
497
+ (params.all?(&:anonymous?) && other.params.none?(&:anonymous?)) ||
498
+ (params.none?(&:anonymous?) && other.params.all?(&:anonymous?)) ||
499
+ (params.none?(&:anonymous?) && other.params.none?(&:anonymous?))
500
+ end
484
501
  end
485
502
 
486
503
  class Mixin
@@ -15,24 +15,33 @@ module RBI
15
15
  protected_group = VisibilityGroup.new(Protected.new)
16
16
  private_group = VisibilityGroup.new(Private.new)
17
17
 
18
- node.nodes.dup.each do |child|
18
+ # Classify children in a single pass, avoiding O(n) Array#delete
19
+ # calls from `detach` in a loop.
20
+ remaining = []
21
+ node.nodes.each do |child|
19
22
  visit(child)
20
- next unless child.is_a?(Attr) || child.is_a?(Method)
21
-
22
- child.detach
23
- case child.visibility
24
- when Protected
25
- protected_group << child
26
- when Private
27
- private_group << child
23
+ if child.is_a?(Attr) || child.is_a?(Method)
24
+ child.parent_tree = nil
25
+ case child.visibility
26
+ when Protected
27
+ protected_group << child
28
+ when Private
29
+ private_group << child
30
+ else
31
+ public_group << child
32
+ end
28
33
  else
29
- public_group << child
34
+ remaining << child
30
35
  end
31
36
  end
32
37
 
33
- node << public_group unless public_group.empty?
34
- node << protected_group unless protected_group.empty?
35
- node << private_group unless private_group.empty?
38
+ has_groups = !public_group.empty? || !protected_group.empty? || !private_group.empty?
39
+ if has_groups
40
+ node.nodes.replace(remaining)
41
+ node << public_group unless public_group.empty?
42
+ node << protected_group unless protected_group.empty?
43
+ node << private_group unless private_group.empty?
44
+ end
36
45
  end
37
46
  end
38
47
  end
@@ -13,16 +13,24 @@ module RBI
13
13
  when Tree
14
14
  singleton_class = SingletonClass.new
15
15
 
16
- node.nodes.dup.each do |child|
16
+ # Collect singleton methods and remaining nodes in a single pass,
17
+ # avoiding O(n) Array#delete calls from `detach` in a loop.
18
+ remaining = []
19
+ node.nodes.each do |child|
17
20
  visit(child)
18
- next unless child.is_a?(Method) && child.is_singleton
19
-
20
- child.detach
21
- child.is_singleton = false
22
- singleton_class << child
21
+ if child.is_a?(Method) && child.is_singleton
22
+ child.parent_tree = nil
23
+ child.is_singleton = false
24
+ singleton_class << child
25
+ else
26
+ remaining << child
27
+ end
23
28
  end
24
29
 
25
- node << singleton_class unless singleton_class.empty?
30
+ unless singleton_class.empty?
31
+ node.nodes.replace(remaining)
32
+ node << singleton_class
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -12,32 +12,29 @@ module RBI
12
12
  return unless node.is_a?(Tree)
13
13
 
14
14
  visit_all(node.nodes)
15
- original_order = node.nodes.map.with_index.to_h
16
15
 
17
- # The child nodes could contain private/protected markers. If so, they should not be moved in the file.
18
- # Otherwise, some methods could see their privacy change. To avoid that problem, divide the array of child
19
- # nodes into chunks based on whether any Visibility nodes appear, and sort the chunks independently. This
20
- # applies the ordering rules from the node_rank method as much as possible, while preserving visibility.
21
- sorted_nodes = node.nodes.chunk do |n|
22
- n.is_a?(Visibility)
23
- end.flat_map do |_, nodes|
24
- nodes.sort! do |a, b|
25
- # First we try to compare the nodes by their node rank (based on the node type)
26
- res = node_rank(a) <=> node_rank(b)
27
- next res if res != 0 # we can sort the nodes by their rank, let's stop here
16
+ nodes = node.nodes
17
+ return if nodes.size <= 1
28
18
 
29
- # Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name
30
- res = node_name(a) <=> node_name(b)
31
- next res if res && res != 0 # we can sort the nodes by their name, let's stop here
19
+ # Fast path: if no Visibility nodes are present (common after group_nodes!/nest_non_public_members!),
20
+ # skip the chunk and sort directly.
21
+ has_visibility = nodes.any? { |n| n.is_a?(Visibility) }
32
22
 
33
- # Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then,
34
- original_order_a = original_order[a] #: as !nil
35
- original_order_b = original_order[b] #: as !nil
36
- original_order_a <=> original_order_b # we keep the original order
23
+ if has_visibility
24
+ # The child nodes contain private/protected markers. Divide into chunks
25
+ # and sort each chunk independently to preserve visibility semantics.
26
+ sorted_nodes = nodes.chunk { |n| n.is_a?(Visibility) }.flat_map do |_, chunk_nodes|
27
+ chunk_nodes.sort_by!.with_index do |n, i|
28
+ [node_rank(n), node_name(n) || "", i]
29
+ end
30
+ end
31
+ nodes.replace(sorted_nodes)
32
+ else
33
+ # No visibility nodes — sort in place with Schwartzian transform.
34
+ nodes.sort_by!.with_index do |n, i|
35
+ [node_rank(n), node_name(n) || "", i]
37
36
  end
38
37
  end
39
-
40
- node.nodes.replace(sorted_nodes)
41
38
  end
42
39
 
43
40
  private
@@ -51,7 +51,7 @@ module RBI
51
51
 
52
52
  #: (Method, RBSComment) -> Sig
53
53
  def translate_rbs_method_type(node, comment)
54
- method_type = ::RBS::Parser.parse_method_type(comment.text)
54
+ method_type = ::RBS::Parser.parse_method_type(comment.text, require_eof: true)
55
55
  translator = RBS::MethodTypeTranslator.new(node)
56
56
  translator.visit(method_type)
57
57
  translator.result
@@ -59,7 +59,7 @@ module RBI
59
59
 
60
60
  #: (Attr, RBSComment) -> Sig
61
61
  def translate_rbs_attr_type(node, comment)
62
- attr_type = ::RBS::Parser.parse_type(comment.text)
62
+ attr_type = ::RBS::Parser.parse_type(comment.text, require_eof: true)
63
63
  sig = Sig.new
64
64
 
65
65
  if node.is_a?(AttrWriter)
data/lib/rbi/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RBI
5
- VERSION = "0.3.9"
5
+ VERSION = "0.3.10"
6
6
  end