Class: Parlour::Conversion::RbiToRbs

Inherits:
Converter
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/parlour/conversion/rbi_to_rbs.rb

Overview

Converts RBI types to RBS types.

Instance Attribute Summary collapse

Attributes inherited from Converter

#warnings

Instance Method Summary collapse

Methods inherited from Converter

#add_warning

Constructor Details

#initialize(rbs_gen) ⇒ RbiToRbs

Returns a new instance of RbiToRbs.



9
10
11
12
# File 'lib/parlour/conversion/rbi_to_rbs.rb', line 9

def initialize(rbs_gen)
  super()
  @rbs_gen = rbs_gen
end

Instance Attribute Details

#rbs_genObject (readonly)

Returns the value of attribute rbs_gen.



15
16
17
# File 'lib/parlour/conversion/rbi_to_rbs.rb', line 15

def rbs_gen
  @rbs_gen
end

Instance Method Details

#convert_all(from, to) ⇒ Object



18
19
20
21
22
# File 'lib/parlour/conversion/rbi_to_rbs.rb', line 18

def convert_all(from, to)
  from.children.each do |child|
    convert_object(child, to)
  end        
end

#convert_object(node, new_parent) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/parlour/conversion/rbi_to_rbs.rb', line 30

def convert_object(node, new_parent)        
  case node
  when RbiGenerator::StructClassNamespace
    add_warning 'performing a one-way conversion of an RBI struct to RBS', node

    klass = new_parent.create_class(node.name)
    klass.add_comments(node.comments)

    # Create a constructor
    klass.create_method('initialize', [
      RbsGenerator::MethodSignature.new(
        node.props.map do |prop|
          RbsGenerator::Parameter.new(
            "#{prop.name}:",
            type: prop.type,
            required: !prop.optional,
          )
        end,
        nil,
      )
    ])

    # Make each prop a getter (and setter, if not immutable) attribute
    node.props.each do |prop|
      klass.create_attribute(
        prop.name,
        kind: prop.immutable ? :reader : :accessor,
        type: prop.type,
      )
    end

    klass 

  when RbiGenerator::EnumClassNamespace
    add_warning 'performing a one-way conversion of an RBI enum to RBS', node

    klass = new_parent.create_class(node.name)
    klass.add_comments(node.comments)

    # Define .values
    klass.create_method('values', [
      RbsGenerator::MethodSignature.new([], Types::Array.new(node.name))
    ], class_method: true)

    # Define each enum variant
    node.enums.each do |variant|
      # We don't care about any extra value
      variant = variant[0] if Array === variant

      klass.create_constant(variant, type: node.name)
    end

    klass

  when RbiGenerator::Arbitrary
    add_warning 'converting type of Arbitrary is likely to cause syntax errors; doing it anyway', node
    new_parent.create_arbitrary(
      code: node.code,
    ).add_comments(node.comments)

  when RbiGenerator::Attribute
    if node.class_attribute
      add_warning 'RBS does not support class attributes; dropping', node
      return
    end
    new_parent.create_attribute(
      node.name,
      kind: node.kind,
      type: node.type,
    ).add_comments(node.comments)

  when RbiGenerator::ClassNamespace
    if node.abstract
      add_warning 'RBS does not support abstract classes', node
    end
    klass = new_parent.create_class(
      node.name,
      superclass: node.superclass
    )
    klass.add_comments(node.comments)
    node.children.each do |child|
      convert_object(child, klass)
    end

  when RbiGenerator::Constant
    if node.eigen_constant
      add_warning 'RBS does not support constants on eigenclasses; dropping', node
      return
    end
    new_parent.create_constant(
      node.name,
      type: node.value,
    ).add_comments(node.comments)

  when RbiGenerator::TypeAlias
    new_parent.create_type_alias(
      node.name,
      type: node.type,
    ).add_comments(node.comments)

  when RbiGenerator::Extend
    new_parent.create_extend(node.name).add_comments(node.comments)

  when RbiGenerator::Include
    new_parent.create_include(node.name).add_comments(node.comments)

  when RbiGenerator::Method
    # Convert parameters
    parameters = node.parameters
      .reject { |param| param.kind == :block }
      .map do |param|
        RbsGenerator::Parameter.new(
          param.name,
          type: param.type,
          required: param.default.nil?
        )
      end

    # Find block if there is one
    block_param = node.parameters.find { |param| param.kind == :block }
    if block_param
      if String === block_param.type
        add_warning "block must have a Types::Type for conversion; dropping block", node
        block = nil
      else
        # A nilable proc is an optional block
        block_param_type = block_param.type
        if Types::Nilable === block_param_type && Types::Proc === block_param_type.type
          t = T.cast(block_param_type.type, Types::Proc)
          required = false
          block = RbsGenerator::Block.new(t, required)
        elsif Types::Proc === block_param_type
          t = block_param_type
          required = true
          block = RbsGenerator::Block.new(t, required)
        elsif Types::Untyped === block_param_type
          # Consider there to be a block of unknown types
          block = RbsGenerator::Block.new(
            Types::Proc.new(
              [
                Types::Proc::Parameter.new('*args', Types::Untyped.new),
                Types::Proc::Parameter.new('**kwargs', Types::Untyped.new),
              ],
              Types::Untyped.new,
            ),
            false,
          )
        else
          add_warning 'block type must be a Types::Proc (or nilable one); dropping block', node
        end
      end
    else
      block = nil
    end

    new_parent.create_method(
      node.name,
      [
        RbsGenerator::MethodSignature.new(
          parameters,
          node.return_type,
          block: block,
          type_parameters: node.type_parameters,
        )
      ],
      class_method: node.class_method,
    ).add_comments(node.comments)

  when RbiGenerator::ModuleNamespace
    if node.interface
      rbs_node = new_parent.create_interface(
        node.name,
      )
    else
      rbs_node = new_parent.create_module(
        node.name,
      )
    end
    rbs_node.add_comments(node.comments)
    node.children.each do |child|
      convert_object(child, rbs_node)
    end

  when RbiGenerator::Namespace
    add_warning 'unspecialized namespaces are not supposed to be in the tree; you may run into issues', node
    namespace = RbsGenerator::Namespace.new(rbs_gen)
    namespace.add_comments(node.comments)
    node.children.each do |child|
      convert_object(child, namespace)
    end
    new_parent.children << namespace

  else
    raise "missing conversion for #{node.describe}"
    # TODO: stick a T.absurd here
  end
end