@@ -14,6 +14,16 @@ def initialize(ruby_contents, file:, max_line_length: nil)
1414 @max_line_length = max_line_length
1515 end
1616
17+ # @override
18+ #: (Prism::ProgramNode node) -> void
19+ def visit_program_node ( node )
20+ # Process all type aliases from the entire file first
21+ apply_type_aliases ( @comments )
22+
23+ # Now process the rest of the file with type aliases available
24+ super
25+ end
26+
1727 # @override
1828 #: (Prism::ClassNode node) -> void
1929 def visit_class_node ( node )
@@ -274,6 +284,78 @@ def already_extends?(node, constant_regex)
274284 true
275285 end
276286 end
287+
288+ #: (Array[Prism::Comment]) -> Array[RBS::TypeAlias]
289+ def collect_type_aliases ( comments )
290+ type_aliases = [ ] #: Array[RBS::TypeAlias]
291+
292+ return type_aliases if comments . empty?
293+
294+ continuation_comments = [ ] #: Array[Prism::Comment]
295+
296+ comments . reverse_each do |comment |
297+ string = comment . slice
298+
299+ if string . start_with? ( "#:" )
300+ string = string . delete_prefix ( "#:" ) . strip
301+ location = comment . location
302+
303+ if string . start_with? ( "type " )
304+ continuation_comments . reverse_each do |continuation_comment |
305+ string = "#{ string } #{ continuation_comment . slice . delete_prefix ( "#|" ) } "
306+ location = location . join ( continuation_comment . location )
307+ end
308+
309+ type_aliases << Spoom ::RBS ::TypeAlias . new ( string , location )
310+ end
311+
312+ # Clear the continuation comments regardless of whether we found a type alias or not
313+ continuation_comments . clear
314+ elsif string . start_with? ( "#|" )
315+ continuation_comments << comment
316+ else
317+ continuation_comments . clear
318+ end
319+ end
320+
321+ type_aliases
322+ end
323+
324+ #: (Array[Prism::Comment]) -> void
325+ def apply_type_aliases ( comments )
326+ type_aliases = collect_type_aliases ( comments )
327+
328+ type_aliases . each do |type_alias |
329+ indent = " " * type_alias . location . start_column
330+ insert_pos = adjust_to_line_start ( type_alias . location . start_offset )
331+
332+ from = insert_pos
333+ to = adjust_to_line_end ( type_alias . location . end_offset )
334+
335+ *, decls = ::RBS ::Parser . parse_signature ( type_alias . string )
336+
337+ # We only expect there to be a single type alias declaration
338+ next unless decls . size == 1 && decls . first . is_a? ( ::RBS ::AST ::Declarations ::TypeAlias )
339+
340+ rbs_type = decls . first
341+ sorbet_type = RBI ::RBS ::TypeTranslator . translate ( rbs_type . type )
342+
343+ alias_name = ::RBS ::TypeName . new (
344+ namespace : rbs_type . name . namespace ,
345+ name : rbs_type . name . name . to_s . gsub ( /(?:^|_)([a-z\d ]*)/i ) do |match |
346+ match = match . delete_prefix ( "_" )
347+ !match . empty? ? match [ 0 ] . upcase . concat ( match [ 1 ..-1 ] ) : +""
348+ end ,
349+ )
350+
351+ @rewriter << Source ::Delete . new ( from , to )
352+ content = "#{ indent } #{ alias_name } = T.type_alias { #{ sorbet_type . to_rbi } }\n "
353+ @rewriter << Source ::Insert . new ( insert_pos , content )
354+ rescue ::RBS ::ParsingError , ::RBI ::Error
355+ # Ignore type aliases with errors
356+ next
357+ end
358+ end
277359 end
278360 end
279361 end
0 commit comments