rbi 0.3.9 → 0.3.11

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,29 @@ 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), or if self has no sigs but other does, adopt
483
+ # other's non-anonymous param names and sigs.
484
+ if params.all?(&:anonymous?) || (sigs.empty? && !other.sigs.empty?)
485
+ @params = other.params.dup
486
+ @sigs = other.sigs.dup unless other.sigs.empty?
487
+ return
488
+ end
489
+
480
490
  other.sigs.each do |sig|
481
491
  sigs << sig unless sigs.include?(sig)
482
492
  end
483
493
  end
494
+
495
+ private
496
+
497
+ #: (Method other) -> bool
498
+ def at_most_one_side_anonymous?(other)
499
+ (params.all?(&:anonymous?) && other.params.none?(&:anonymous?)) ||
500
+ (params.none?(&:anonymous?) && other.params.all?(&:anonymous?)) ||
501
+ (params.none?(&:anonymous?) && other.params.none?(&:anonymous?))
502
+ end
484
503
  end
485
504
 
486
505
  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?(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/type.rb CHANGED
@@ -112,7 +112,7 @@ module RBI
112
112
  # @override
113
113
  #: -> String
114
114
  def to_rbi
115
- "T::Boolean"
115
+ "::T::Boolean"
116
116
  end
117
117
 
118
118
  # @override
@@ -258,7 +258,7 @@ module RBI
258
258
  # @override
259
259
  #: -> String
260
260
  def to_rbi
261
- "T::Class[#{@type}]"
261
+ "::T::Class[#{@type}]"
262
262
  end
263
263
 
264
264
  # @override
@@ -294,7 +294,7 @@ module RBI
294
294
  # @override
295
295
  #: -> String
296
296
  def to_rbi
297
- "T::Module[#{@type}]"
297
+ "::T::Module[#{@type}]"
298
298
  end
299
299
 
300
300
  # @override
@@ -498,7 +498,7 @@ module RBI
498
498
  return type.simplify unless type.is_a?(Any)
499
499
 
500
500
  types = type.types.map(&:simplify)
501
- return Untyped.new if types.any? { |type| type.is_a?(Untyped) }
501
+ return Untyped.new if types.any?(Untyped)
502
502
 
503
503
  has_true_class = types.any? { |type| type.is_a?(Simple) && type.name == "TrueClass" }
504
504
  has_false_class = types.any? { |type| type.is_a?(Simple) && type.name == "FalseClass" }
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.11"
6
6
  end