Skip to content

Commit a15bdc2

Browse files
committed
Only delete extend T::Helpers when needed
Signed-off-by: Alexandre Terrasa <alexandre.terrasa@shopify.com>
1 parent c1a1964 commit a15bdc2

2 files changed

Lines changed: 109 additions & 18 deletions

File tree

lib/spoom/sorbet/translate/sorbet_sigs_to_rbs_comments.rb

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ def initialize(ruby_contents, file:, positional_names:)
1212
super(ruby_contents, file: file)
1313

1414
@positional_names = positional_names #: bool
15-
@nesting = [] #: Array[Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode]
1615
@last_sigs = [] #: Array[[Prism::CallNode, RBI::Sig]]
16+
@class_annotations = [] #: Array[Prism::CallNode]
1717
@type_members = [] #: Array[String]
18+
@extend_t_helpers = [] #: Array[Prism::CallNode]
19+
@extend_t_generics = [] #: Array[Prism::CallNode]
20+
@seen_mixes_in_class_methods = false #: bool
1821
end
1922

2023
# @override
@@ -71,7 +74,9 @@ def visit_call_node(node)
7174
when "extend"
7275
visit_extend(node)
7376
when "abstract!", "interface!", "sealed!", "final!", "requires_ancestor"
74-
visit_class_annotation(node)
77+
@class_annotations << node
78+
when "mixes_in_class_methods"
79+
@seen_mixes_in_class_methods = true
7580
else
7681
super
7782
end
@@ -97,19 +102,39 @@ def visit_constant_write_node(node)
97102

98103
#: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode) { -> void } -> void
99104
def visit_scope(node, &block)
100-
@nesting << node
105+
old_class_annotations = @class_annotations
106+
@class_annotations = []
101107
old_type_members = @type_members
102108
@type_members = []
109+
old_extend_t_helpers = @extend_t_helpers
110+
@extend_t_helpers = []
111+
old_extend_t_generics = @extend_t_generics
112+
@extend_t_generics = []
113+
old_seen_mixes_in_class_methods = @seen_mixes_in_class_methods
114+
@seen_mixes_in_class_methods = false
103115

104116
yield
105117

118+
delete_extend_t_generics
119+
106120
if @type_members.any?
107121
indent = " " * node.location.start_column
108122
@rewriter << Source::Insert.new(node.location.start_offset, "#: [#{@type_members.join(", ")}]\n#{indent}")
109123
end
110124

125+
unless @seen_mixes_in_class_methods
126+
delete_extend_t_helpers
127+
end
128+
129+
@class_annotations.each do |call|
130+
apply_class_annotation(node, call)
131+
end
132+
133+
@class_annotations = old_class_annotations
111134
@type_members = old_type_members
112-
@nesting.pop
135+
@extend_t_helpers = old_extend_t_helpers
136+
@extend_t_generics = old_extend_t_generics
137+
@seen_mixes_in_class_methods = old_seen_mixes_in_class_methods
113138
end
114139

115140
#: (Prism::CallNode) -> void
@@ -160,16 +185,17 @@ def visit_extend(node)
160185

161186
arg = node.arguments&.arguments&.first
162187
return unless arg.is_a?(Prism::ConstantPathNode)
163-
return unless arg.slice.match?(/^(::)?T::Helpers$/) || arg.slice.match?(/^(::)?T::Generic$/)
164188

165-
from = adjust_to_line_start(node.location.start_offset)
166-
to = adjust_to_line_end(node.location.end_offset)
167-
to = adjust_to_new_line(to)
168-
@rewriter << Source::Delete.new(from, to)
189+
case arg.slice
190+
when /^(::)?T::Helpers$/
191+
@extend_t_helpers << node
192+
when /^(::)?T::Generic$/
193+
@extend_t_generics << node
194+
end
169195
end
170196

171-
#: (Prism::CallNode node) -> void
172-
def visit_class_annotation(node)
197+
#: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode, Prism::CallNode) -> void
198+
def apply_class_annotation(parent, node)
173199
unless node.message == "abstract!" || node.message == "interface!" || node.message == "sealed!" ||
174200
node.message == "final!" || node.message == "requires_ancestor"
175201
raise Error, "Expected abstract!, interface!, sealed!, final!, or requires_ancestor"
@@ -178,18 +204,17 @@ def visit_class_annotation(node)
178204
return unless node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)
179205
return unless node.arguments.nil?
180206

181-
klass = @nesting.last #: as Prism::Node
182-
indent = " " * klass.location.start_column
207+
indent = " " * parent.location.start_column
183208

184209
case node.message
185210
when "abstract!"
186-
@rewriter << Source::Insert.new(klass.location.start_offset, "# @abstract\n#{indent}")
211+
@rewriter << Source::Insert.new(parent.location.start_offset, "# @abstract\n#{indent}")
187212
when "interface!"
188-
@rewriter << Source::Insert.new(klass.location.start_offset, "# @interface\n#{indent}")
213+
@rewriter << Source::Insert.new(parent.location.start_offset, "# @interface\n#{indent}")
189214
when "sealed!"
190-
@rewriter << Source::Insert.new(klass.location.start_offset, "# @sealed\n#{indent}")
215+
@rewriter << Source::Insert.new(parent.location.start_offset, "# @sealed\n#{indent}")
191216
when "final!"
192-
@rewriter << Source::Insert.new(klass.location.start_offset, "# @final\n#{indent}")
217+
@rewriter << Source::Insert.new(parent.location.start_offset, "# @final\n#{indent}")
193218
when "requires_ancestor"
194219
block = node.block
195220
return unless block.is_a?(Prism::BlockNode)
@@ -200,7 +225,7 @@ def visit_class_annotation(node)
200225

201226
arg = body.body.first #: as Prism::Node
202227
srb_type = RBI::Type.parse_node(arg)
203-
@rewriter << Source::Insert.new(klass.location.start_offset, "# @requires_ancestor: #{srb_type.rbs_string}\n#{indent}")
228+
@rewriter << Source::Insert.new(parent.location.start_offset, "# @requires_ancestor: #{srb_type.rbs_string}\n#{indent}")
204229
end
205230

206231
from = adjust_to_line_start(node.location.start_offset)
@@ -287,6 +312,30 @@ def build_type_member_string(node)
287312

288313
type_member
289314
end
315+
316+
#: -> void
317+
def delete_extend_t_helpers
318+
@extend_t_helpers.each do |helper|
319+
from = adjust_to_line_start(helper.location.start_offset)
320+
to = adjust_to_line_end(helper.location.end_offset)
321+
to = adjust_to_new_line(to)
322+
@rewriter << Source::Delete.new(from, to)
323+
end
324+
325+
@extend_t_helpers.clear
326+
end
327+
328+
#: -> void
329+
def delete_extend_t_generics
330+
@extend_t_generics.each do |generic|
331+
from = adjust_to_line_start(generic.location.start_offset)
332+
to = adjust_to_line_end(generic.location.end_offset)
333+
to = adjust_to_new_line(to)
334+
@rewriter << Source::Delete.new(from, to)
335+
end
336+
337+
@extend_t_generics.clear
338+
end
290339
end
291340
end
292341
end

test/spoom/sorbet/translate/sorbet_sigs_to_rbs_comments_test.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,48 @@ class << self
237237
RB
238238
end
239239

240+
def test_translate_to_rbs_helpers_do_not_remove_extend_helpers_if_no_helper_was_removed
241+
contents = <<~RB
242+
module Foo
243+
extend T::Helpers
244+
245+
mixes_in_class_methods Bar
246+
247+
module Bar
248+
def bar
249+
end
250+
end
251+
end
252+
253+
module Baz
254+
extend T::Helpers
255+
256+
mixes_in_class_methods Foo::Bar
257+
requires_ancestor { Foo::Bar }
258+
end
259+
RB
260+
261+
assert_equal(<<~RB, sorbet_sigs_to_rbs_comments(contents))
262+
module Foo
263+
extend T::Helpers
264+
265+
mixes_in_class_methods Bar
266+
267+
module Bar
268+
def bar
269+
end
270+
end
271+
end
272+
273+
# @requires_ancestor: Foo::Bar
274+
module Baz
275+
extend T::Helpers
276+
277+
mixes_in_class_methods Foo::Bar
278+
end
279+
RB
280+
end
281+
240282
def test_translate_to_rbs_generics
241283
contents = <<~RB
242284
class A

0 commit comments

Comments
 (0)