@@ -129,7 +129,7 @@ def visit_attr(node)
129129 @rewriter << Source ::Replace . new (
130130 signature . location . start_offset ,
131131 signature . location . end_offset ,
132- sig . string ( max_line_length : @max_line_length ) ,
132+ pad_out_line_count ( of : sig . string ( max_line_length : @max_line_length ) , to_height_of : signature ) ,
133133 )
134134 rescue ::RBS ::ParsingError , ::RBI ::Error
135135 # Ignore signatures with errors
@@ -180,7 +180,7 @@ def rewrite_def(def_node, comments)
180180 @rewriter << Source ::Replace . new (
181181 signature . location . start_offset ,
182182 signature . location . end_offset ,
183- sig . string ( max_line_length : @max_line_length ) ,
183+ pad_out_line_count ( of : sig . string ( max_line_length : @max_line_length ) , to_height_of : signature ) ,
184184 )
185185 end
186186 end
@@ -193,27 +193,26 @@ def apply_overloads_strategy(signatures, method_name:, location:)
193193 when :translate_all
194194 signatures
195195 when :translate_last
196- kept = signatures . last #: as Spoom::RBS::Signature
197196 others = signatures [ 0 ...-1 ] #: as !nil
197+ others . each { |signature | rewrite_discarded_overload ( signature ) }
198198
199- # Delete all the signatures we didn't keep
200- others . each do |signature |
201- from = adjust_to_line_start ( signature . location . start_offset )
202- to = adjust_to_line_end ( signature . location . end_offset )
203- @rewriter << Source ::Delete . new ( from , to )
204- end
199+ kept = signatures . last #: as Spoom::RBS::Signature
205200 [ kept ]
206201 else # :raise
207202 raise Error , "Method `#{ method_name } ` at #{ location } has multiple overloaded signatures"
208203 end
209204 end
210205
206+ # Called for every overloaded method sig that we discard because it wasn't the last one.
207+ # @abstract
208+ #: (Spoom::RBS::Signature) -> void
209+ def rewrite_discarded_overload ( signature ) = raise
210+
211211 #: (PrismTypes::anyScopeNode) -> void
212212 def apply_class_annotations ( node )
213213 comments = node_rbs_comments ( node )
214214 return if comments . empty?
215215
216- indent = " " * ( node . location . start_column + 2 )
217216 insert_pos = case node
218217 when Prism ::ClassNode
219218 ( node . superclass || node . constant_path ) . location . end_offset
@@ -223,16 +222,14 @@ def apply_class_annotations(node)
223222 node . expression . location . end_offset
224223 end
225224
226- class_annotations = comments . class_annotations
227- if class_annotations . any?
225+ # Only translate (and `extend T::Helpers`) when there's at least one *known* class
226+ # annotation. A node with only unknown annotations (e.g. `@private`) is left untouched.
227+ if comments . class_annotations . any?
228228 unless already_extends? ( node , /^(::)?T::Helpers$/ )
229- @rewriter << Source :: Insert . new ( insert_pos , " \n #{ indent } extend T::Helpers\n " )
229+ extend_with ( " T::Helpers" , into : node , at : insert_pos )
230230 end
231231
232- class_annotations . reverse_each do |annotation |
233- from = adjust_to_line_start ( annotation . location . start_offset )
234- to = adjust_to_line_end ( annotation . location . end_offset )
235-
232+ comments . annotations . reverse_each do |annotation |
236233 content = case annotation . string
237234 when "@abstract"
238235 "abstract!"
@@ -247,15 +244,13 @@ def apply_class_annotations(node)
247244 rbs_type = @type_translator . translate ( srb_type )
248245 "requires_ancestor { #{ rbs_type } }"
249246 else
247+ apply_class_annotation ( annotation , parent_node : node , insert_pos :, sorbet_replacement : nil )
250248 next
251249 end
252250
253- @rewriter << Source ::Delete . new ( from , to )
254-
255- newline = node . body . nil? ? "" : "\n "
256- @rewriter << Source ::Insert . new ( insert_pos , "\n #{ indent } #{ content } #{ newline } " )
251+ apply_class_annotation ( annotation , parent_node : node , insert_pos :, sorbet_replacement : content )
257252 rescue ::RBS ::ParsingError , ::RBI ::Error
258- # Ignore annotations with errors
253+ apply_class_annotation ( annotation , parent_node : node , insert_pos : , sorbet_replacement : nil )
259254 next
260255 end
261256 end
@@ -267,14 +262,11 @@ def apply_class_annotations(node)
267262 next unless signature . string . start_with? ( "[" )
268263
269264 type_params = ::RBS ::Parser . parse_type_params ( signature . string )
265+ rewrite_type_params_signature ( signature , type_params :)
270266 next if type_params . empty?
271267
272- from = adjust_to_line_start ( signature . location . start_offset )
273- to = adjust_to_line_end ( signature . location . end_offset )
274- @rewriter << Source ::Delete . new ( from , to )
275-
276268 unless already_extends? ( node , /^(::)?T::Generic$/ )
277- @rewriter << Source :: Insert . new ( insert_pos , " \n #{ indent } extend T::Generic\n " )
269+ extend_with ( " T::Generic" , into : node , at : insert_pos )
278270 end
279271
280272 type_params . each do |type_param |
@@ -299,8 +291,7 @@ def apply_class_annotations(node)
299291 end
300292 end
301293
302- newline = node . body . nil? ? "" : "\n "
303- @rewriter << Source ::Insert . new ( insert_pos , "\n #{ indent } #{ type_member } #{ newline } " )
294+ insert_type_member ( type_member , parent_node : node , insert_pos :)
304295 rescue ::RBS ::ParsingError , ::RBI ::Error
305296 # Ignore signatures with errors
306297 next
@@ -309,6 +300,27 @@ def apply_class_annotations(node)
309300 end
310301 end
311302
303+ # @param is_known: true if this is an RBS annotation that we recognize
304+ # false for some other `@`-prefixed thing, like a documentation `@param` tag.
305+ # @abstract
306+ #: (
307+ #| Spoom::RBS::Annotation,
308+ #| parent_node: PrismTypes::anyScopeNode,
309+ #| insert_pos: Integer,
310+ #| sorbet_replacement: String?
311+ #| ) -> void
312+ def apply_class_annotation ( annotation , parent_node :, insert_pos :, sorbet_replacement :) = raise
313+
314+ # Rewrites the `#: [...]` type params comment (e.g. delete it, or mark it as translated).
315+ # @abstract
316+ #: (Spoom::RBS::Signature, type_params: Array[::RBS::AST::TypeParam]) -> void
317+ def rewrite_type_params_signature ( signature , type_params :) = raise
318+
319+ # Inserts a single `type_member` declaration into the class/module body.
320+ # @abstract
321+ #: (String type_member, parent_node: PrismTypes::anyScopeNode, insert_pos: Integer) -> void
322+ def insert_type_member ( type_member , parent_node :, insert_pos :) = raise
323+
312324 #: (Array[Spoom::RBS::Annotation], RBI::Sig) -> void
313325 def apply_member_annotations ( annotations , sig )
314326 annotations . each do |annotation |
@@ -329,10 +341,25 @@ def apply_member_annotations(annotations, sig)
329341 sig . is_overridable = true
330342 when "@without_runtime"
331343 sig . without_runtime = true
344+ else
345+ rewrite_annotation ( annotation , is_known : false )
346+ next
332347 end
348+
349+ rewrite_annotation ( annotation , is_known : true )
333350 end
334351 end
335352
353+ # @param is_known: true if this is an RBS annotation that we recognize
354+ # false for some other `@`-prefixed thing, like a documentation `@param` tag.
355+ # @overridable
356+ #: (Spoom::RBS::Annotation, is_known: bool) -> void
357+ def rewrite_annotation ( annotation , is_known :) = nil # no-op
358+
359+ # @abstract
360+ #: (String mixin_name, into: PrismTypes::anyScopeNode, at: Integer) -> void
361+ def extend_with ( mixin_name , into :, at :) = raise
362+
336363 #: (PrismTypes::anyScopeNode, Regexp) -> bool
337364 def already_extends? ( node , constant_regex )
338365 node . child_nodes . any? do |c |
@@ -414,12 +441,22 @@ def apply_type_aliases(comments)
414441
415442 @rewriter << Source ::Delete . new ( from , to )
416443 content = "#{ indent } #{ alias_name } = T.type_alias { #{ sorbet_type . to_rbi } }\n "
444+ content = pad_out_line_count ( of : content , to_height_of : type_alias )
417445 @rewriter << Source ::Insert . new ( insert_pos , content )
418446 rescue ::RBS ::ParsingError , ::RBI ::Error
419447 # Ignore type aliases with errors
420448 next
421449 end
422450 end
451+
452+ # @overridable
453+ #: (of: String, to_height_of: Spoom::RBS::Comment) -> String
454+ def pad_out_line_count ( of :, to_height_of :)
455+ replacement = of
456+
457+ # no-op implementation
458+ replacement
459+ end
423460 end
424461 end
425462 end
0 commit comments