docscribe 1.0.0 → 1.2.0

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +692 -180
  3. data/exe/docscribe +2 -74
  4. data/lib/docscribe/cli/config_builder.rb +62 -0
  5. data/lib/docscribe/cli/init.rb +58 -0
  6. data/lib/docscribe/cli/options.rb +204 -0
  7. data/lib/docscribe/cli/run.rb +415 -0
  8. data/lib/docscribe/cli.rb +31 -0
  9. data/lib/docscribe/config/defaults.rb +71 -0
  10. data/lib/docscribe/config/emit.rb +126 -0
  11. data/lib/docscribe/config/filtering.rb +160 -0
  12. data/lib/docscribe/config/loader.rb +59 -0
  13. data/lib/docscribe/config/rbs.rb +51 -0
  14. data/lib/docscribe/config/sorbet.rb +87 -0
  15. data/lib/docscribe/config/sorting.rb +23 -0
  16. data/lib/docscribe/config/template.rb +176 -0
  17. data/lib/docscribe/config/utils.rb +102 -0
  18. data/lib/docscribe/config.rb +20 -230
  19. data/lib/docscribe/infer/ast_walk.rb +28 -0
  20. data/lib/docscribe/infer/constants.rb +11 -0
  21. data/lib/docscribe/infer/literals.rb +55 -0
  22. data/lib/docscribe/infer/names.rb +43 -0
  23. data/lib/docscribe/infer/params.rb +62 -0
  24. data/lib/docscribe/infer/raises.rb +68 -0
  25. data/lib/docscribe/infer/returns.rb +171 -0
  26. data/lib/docscribe/infer.rb +110 -259
  27. data/lib/docscribe/inline_rewriter/collector.rb +845 -0
  28. data/lib/docscribe/inline_rewriter/doc_block.rb +383 -0
  29. data/lib/docscribe/inline_rewriter/doc_builder.rb +605 -0
  30. data/lib/docscribe/inline_rewriter/source_helpers.rb +228 -0
  31. data/lib/docscribe/inline_rewriter/tag_sorter.rb +244 -0
  32. data/lib/docscribe/inline_rewriter.rb +604 -425
  33. data/lib/docscribe/parsing.rb +120 -0
  34. data/lib/docscribe/types/provider_chain.rb +37 -0
  35. data/lib/docscribe/types/rbs/provider.rb +213 -0
  36. data/lib/docscribe/types/rbs/type_formatter.rb +132 -0
  37. data/lib/docscribe/types/signature.rb +65 -0
  38. data/lib/docscribe/types/sorbet/base_provider.rb +217 -0
  39. data/lib/docscribe/types/sorbet/rbi_provider.rb +35 -0
  40. data/lib/docscribe/types/sorbet/source_provider.rb +25 -0
  41. data/lib/docscribe/version.rb +1 -1
  42. data/lib/docscribe.rb +1 -0
  43. metadata +85 -17
  44. data/.rspec +0 -3
  45. data/.rubocop.yml +0 -11
  46. data/.rubocop_todo.yml +0 -73
  47. data/CODE_OF_CONDUCT.md +0 -84
  48. data/Gemfile +0 -6
  49. data/Gemfile.lock +0 -73
  50. data/Rakefile +0 -12
  51. data/rakelib/docs.rake +0 -73
  52. data/stingray_docs_internal.gemspec +0 -41
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docscribe
4
+ module Infer
5
+ # Return type inference and rescue-conditional return extraction.
6
+ module Returns
7
+ module_function
8
+
9
+ # Infer a return type from a full method definition source string.
10
+ #
11
+ # The source must parse to a `:def` or `:defs` node. If parsing fails or inference
12
+ # is uncertain, the fallback type is returned.
13
+ #
14
+ # @note module_function: when included, also defines #infer_return_type (instance visibility: private)
15
+ # @param [String, nil] method_source full method definition source
16
+ # @raise [Parser::SyntaxError]
17
+ # @return [String]
18
+ def infer_return_type(method_source)
19
+ return FALLBACK_TYPE if method_source.nil? || method_source.strip.empty?
20
+
21
+ buffer = Parser::Source::Buffer.new('(method)')
22
+ buffer.source = method_source
23
+ root = Docscribe::Parsing.parse_buffer(buffer)
24
+ return FALLBACK_TYPE unless root && %i[def defs].include?(root.type)
25
+
26
+ body = root.children.last
27
+ last_expr_type(body, fallback_type: FALLBACK_TYPE, nil_as_optional: true) || FALLBACK_TYPE
28
+ rescue Parser::SyntaxError
29
+ FALLBACK_TYPE
30
+ end
31
+
32
+ # Infer a method's normal return type from an already parsed def/defs node.
33
+ #
34
+ # @note module_function: when included, also defines #infer_return_type_from_node (instance visibility: private)
35
+ # @param [Parser::AST::Node] node `:def` or `:defs` node
36
+ # @return [String]
37
+ def infer_return_type_from_node(node)
38
+ body =
39
+ case node.type
40
+ when :def then node.children[2]
41
+ when :defs then node.children[3]
42
+ end
43
+
44
+ return FALLBACK_TYPE unless body
45
+
46
+ last_expr_type(body, fallback_type: FALLBACK_TYPE, nil_as_optional: true) || FALLBACK_TYPE
47
+ end
48
+
49
+ # Return a structured return-type spec for a method node.
50
+ #
51
+ # The result includes:
52
+ # - `:normal` => normal/happy-path return type
53
+ # - `:rescues` => array of `[exception_names, return_type]` pairs for rescue branches
54
+ #
55
+ # @note module_function: when included, also defines #returns_spec_from_node (instance visibility: private)
56
+ # @param [Parser::AST::Node] node `:def` or `:defs` node
57
+ # @param [String] fallback_type type used when inference is uncertain
58
+ # @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
59
+ # @return [Hash]
60
+ def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true)
61
+ body =
62
+ case node.type
63
+ when :def then node.children[2]
64
+ when :defs then node.children[3]
65
+ end
66
+
67
+ spec = { normal: FALLBACK_TYPE, rescues: [] }
68
+ return spec unless body
69
+
70
+ if body.type == :rescue
71
+ main_body = body.children[0]
72
+ spec[:normal] =
73
+ last_expr_type(main_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional) || FALLBACK_TYPE
74
+
75
+ body.children.each do |ch|
76
+ next unless ch.is_a?(Parser::AST::Node) && ch.type == :resbody
77
+
78
+ exc_list, _asgn, rescue_body = *ch
79
+ exc_names = Raises.exception_names_from_rescue_list(exc_list)
80
+ rtype =
81
+ last_expr_type(rescue_body, fallback_type: fallback_type, nil_as_optional: nil_as_optional) ||
82
+ fallback_type
83
+ spec[:rescues] << [exc_names, rtype]
84
+ end
85
+ else
86
+ spec[:normal] =
87
+ last_expr_type(body, fallback_type: fallback_type, nil_as_optional: nil_as_optional) || FALLBACK_TYPE
88
+ end
89
+
90
+ spec
91
+ end
92
+
93
+ # Infer the type of the last expression in a node.
94
+ #
95
+ # Supports:
96
+ # - `begin` groups
97
+ # - `if` branches
98
+ # - `case` expressions
99
+ # - explicit `return`
100
+ # - literal-like expressions via {Literals.type_from_literal}
101
+ #
102
+ # @note module_function: when included, also defines #last_expr_type (instance visibility: private)
103
+ # @param [Parser::AST::Node, nil] node expression node
104
+ # @param [String] fallback_type type used when inference is uncertain
105
+ # @param [Boolean] nil_as_optional whether `nil` unions should be rendered as optional types
106
+ # @return [String, nil]
107
+ def last_expr_type(node, fallback_type:, nil_as_optional:)
108
+ return nil unless node
109
+
110
+ case node.type
111
+ when :begin
112
+ last_expr_type(node.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
113
+
114
+ when :if
115
+ t = last_expr_type(node.children[1], fallback_type: fallback_type, nil_as_optional: nil_as_optional)
116
+ e = last_expr_type(node.children[2], fallback_type: fallback_type, nil_as_optional: nil_as_optional)
117
+ unify_types(t, e, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
118
+
119
+ when :case
120
+ branches = node.children[1..].compact.flat_map do |child|
121
+ if child.type == :when
122
+ last_expr_type(child.children.last, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
123
+ else
124
+ last_expr_type(child, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
125
+ end
126
+ end.compact
127
+
128
+ if branches.empty?
129
+ fallback_type
130
+ else
131
+ branches.reduce do |a, b|
132
+ unify_types(a, b, fallback_type: fallback_type, nil_as_optional: nil_as_optional)
133
+ end
134
+ end
135
+
136
+ when :return
137
+ Literals.type_from_literal(node.children.first, fallback_type: fallback_type)
138
+
139
+ else
140
+ Literals.type_from_literal(node, fallback_type: fallback_type)
141
+ end
142
+ end
143
+
144
+ # Unify two inferred types into a single type string.
145
+ #
146
+ # Rules:
147
+ # - identical types remain unchanged
148
+ # - `nil` unions may become optional types if enabled
149
+ # - otherwise falls back conservatively to `fallback_type`
150
+ #
151
+ # @note module_function: when included, also defines #unify_types (instance visibility: private)
152
+ # @param [String, nil] a
153
+ # @param [String, nil] b
154
+ # @param [String] fallback_type
155
+ # @param [Boolean] nil_as_optional
156
+ # @return [String, nil]
157
+ def unify_types(a, b, fallback_type:, nil_as_optional:)
158
+ a ||= fallback_type
159
+ b ||= fallback_type
160
+ return a if a == b
161
+
162
+ if a == 'nil' || b == 'nil'
163
+ non_nil = (a == 'nil' ? b : a)
164
+ return nil_as_optional ? "#{non_nil}?" : "#{non_nil}, nil"
165
+ end
166
+
167
+ fallback_type
168
+ end
169
+ end
170
+ end
171
+ end
@@ -1,301 +1,152 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parser/current'
3
+ # NOTE: parser/base references Racc::Parser in some environments, so require runtime first.
4
+ require 'racc/parser'
5
+ require 'ast'
6
+ require 'parser/ast/node'
7
+ require 'parser/source/buffer'
8
+ require 'parser/base'
9
+
10
+ require 'docscribe/parsing'
11
+
12
+ require_relative 'infer/constants'
13
+ require_relative 'infer/ast_walk'
14
+ require_relative 'infer/names'
15
+ require_relative 'infer/literals'
16
+ require_relative 'infer/params'
17
+ require_relative 'infer/returns'
18
+ require_relative 'infer/raises'
4
19
 
5
20
  module Docscribe
21
+ # Best-effort inference utilities used to generate YARD tags.
22
+ #
23
+ # This module is intentionally heuristic:
24
+ # - it aims to be useful for common Ruby patterns
25
+ # - it prefers safe fallback behavior when uncertain
26
+ # - when inference cannot be specific, it falls back to `Object`
27
+ #
28
+ # External signature sources such as RBS and Sorbet are applied later in the
29
+ # doc builder and can override these inferred types.
6
30
  module Infer
7
31
  class << self
8
- # +Docscribe::Infer.infer_raises_from_node+ -> Object
32
+ # Infer exception classes raised or rescued within an AST node.
9
33
  #
10
- # Method documentation.
11
- #
12
- # @param [Object] node Param documentation.
13
- # @return [Object]
34
+ # @param [Parser::AST::Node] node
35
+ # @return [Array<String>]
14
36
  def infer_raises_from_node(node)
15
- raises = []
16
- walk = lambda do |n|
17
- return unless n.is_a?(Parser::AST::Node)
18
-
19
- case n.type
20
- when :rescue
21
- n.children.each { |ch| walk.call(ch) }
22
- when :resbody
23
- exc_list = n.children[0]
24
- if exc_list.nil?
25
- raises << 'StandardError'
26
- elsif exc_list.type == :array
27
- exc_list.children.each { |e| (c = const_full_name(e)) && (raises << c) }
28
- else
29
- (c = const_full_name(exc_list)) && (raises << c)
30
- end
31
- n.children.each { |ch| walk.call(ch) if ch.is_a?(Parser::AST::Node) }
32
- when :send
33
- recv, meth, *args = *n
34
- if recv.nil? && %i[raise fail].include?(meth)
35
- if args.empty?
36
- raises << 'StandardError'
37
- else
38
- c = const_full_name(args[0])
39
- raises << (c || 'StandardError')
40
- end
41
- end
42
- n.children.each { |ch| walk.call(ch) if ch.is_a?(Parser::AST::Node) }
43
- else
44
- n.children.each { |ch| walk.call(ch) if ch.is_a?(Parser::AST::Node) }
45
- end
46
- end
47
- walk.call(node)
48
- raises.uniq
37
+ Raises.infer_raises_from_node(node)
49
38
  end
50
39
 
51
- # +Docscribe::Infer.infer_param_type+ -> Object
40
+ # Infer a parameter type from its internal name form and optional default
41
+ # expression.
52
42
  #
53
- # Method documentation.
43
+ # The internal parameter name may include:
44
+ # - `*` for rest args
45
+ # - `**` for keyword rest args
46
+ # - `&` for block args
47
+ # - trailing `:` for keyword args
54
48
  #
55
- # @param [Object] name Param documentation.
56
- # @param [Object] default_str Param documentation.
57
- # @return [Object]
58
- def infer_param_type(name, default_str)
59
- # splats and kwargs are driven by name shape
60
- return 'Array' if name.start_with?('*') && !name.start_with?('**')
61
- return 'Hash' if name.start_with?('**')
62
- return 'Proc' if name.start_with?('&')
63
-
64
- # keyword arg e.g. "verbose:" — default_str might be nil or something
65
- is_kw = name.end_with?(':')
66
-
67
- node = parse_expr(default_str)
68
- ty = type_from_literal(node)
69
-
70
- # If kw with no default, still show Object (or Hash for options:)
71
- if is_kw && default_str.nil?
72
- return (name == 'options:' ? 'Hash' : 'Object')
73
- end
74
-
75
- # If param named options and default is {}, call it Hash
76
- return 'Hash' if name == 'options:' && (default_str == '{}' || ty == 'Hash')
77
-
78
- ty
49
+ # @param [String] name internal parameter name representation
50
+ # @param [String, nil] default_str source for the default expression
51
+ # @param [String] fallback_type
52
+ # @param [Boolean] treat_options_keyword_as_hash
53
+ # @return [String]
54
+ def infer_param_type(name, default_str, fallback_type: FALLBACK_TYPE, treat_options_keyword_as_hash: true)
55
+ Params.infer_param_type(
56
+ name,
57
+ default_str,
58
+ fallback_type: fallback_type,
59
+ treat_options_keyword_as_hash: treat_options_keyword_as_hash
60
+ )
79
61
  end
80
62
 
81
- # +Docscribe::Infer.parse_expr+ -> Object
82
- #
83
- # Method documentation.
63
+ # Parse a standalone expression source string for inference helpers.
84
64
  #
85
- # @param [Object] src Param documentation.
86
- # @raise [Parser::SyntaxError]
87
- # @return [Object]
88
- # @return [nil] if Parser::SyntaxError
65
+ # @param [String, nil] src
66
+ # @return [Parser::AST::Node, nil]
89
67
  def parse_expr(src)
90
- return nil if src.nil? || src.strip.empty?
91
-
92
- buffer = Parser::Source::Buffer.new('(param)')
93
- buffer.source = src
94
- Parser::CurrentRuby.new.parse(buffer)
95
- rescue Parser::SyntaxError
96
- nil
68
+ Params.parse_expr(src)
97
69
  end
98
70
 
99
- # +Docscribe::Infer.infer_return_type+ -> Object
100
- #
101
- # Method documentation.
71
+ # Infer a return type from full method source.
102
72
  #
103
- # @param [Object] method_source Param documentation.
104
- # @raise [Parser::SyntaxError]
105
- # @return [Object]
106
- # @return [String] if Parser::SyntaxError
73
+ # @param [String, nil] method_source
74
+ # @return [String]
107
75
  def infer_return_type(method_source)
108
- return 'Object' if method_source.nil? || method_source.strip.empty?
109
-
110
- buffer = Parser::Source::Buffer.new('(method)')
111
- buffer.source = method_source
112
- root = Parser::CurrentRuby.new.parse(buffer)
113
- return 'Object' unless root && %i[def defs].include?(root.type)
114
-
115
- body = root.children.last # method body node
116
- ty = last_expr_type(body)
117
- ty || 'Object'
118
- rescue Parser::SyntaxError
119
- 'Object'
76
+ Returns.infer_return_type(method_source)
120
77
  end
121
78
 
122
- # +Docscribe::Infer.infer_return_type_from_node+ -> Object
79
+ # Infer a return type from an already parsed `:def` / `:defs` node.
123
80
  #
124
- # Method documentation.
125
- #
126
- # @param [Object] node Param documentation.
127
- # @return [Object]
81
+ # @param [Parser::AST::Node] node
82
+ # @return [String]
128
83
  def infer_return_type_from_node(node)
129
- body =
130
- case node.type
131
- when :def then node.children[2] # [name, args, body]
132
- when :defs then node.children[3] # [recv, name, args, body]
133
- end
134
- return 'Object' unless body
135
-
136
- ty = last_expr_type(body)
137
- ty || 'Object'
84
+ Returns.infer_return_type_from_node(node)
138
85
  end
139
86
 
140
- # +Docscribe::Infer.returns_spec_from_node+ -> Object
141
- #
142
- # Method documentation.
143
- #
144
- # @param [Object] node Param documentation.
145
- # @return [Object]
146
- def returns_spec_from_node(node)
147
- # Returns a Hash like: { normal: 'Type', rescues: [[['Foo','Bar'], 'Type'], ...] }
148
- body =
149
- case node.type
150
- when :def then node.children[2] # [name, args, body]
151
- when :defs then node.children[3] # [recv, name, args, body]
152
- end
153
-
154
- spec = { normal: 'Object', rescues: [] }
155
- return spec unless body
156
-
157
- if body.type == :rescue
158
- # child[0] is the main body (before rescue)
159
- main_body = body.children[0]
160
- spec[:normal] = last_expr_type(main_body) || 'Object'
161
-
162
- # :resbody nodes hold exception list, optional var, and rescue body
163
- body.children.each do |ch|
164
- next unless ch.is_a?(Parser::AST::Node) && ch.type == :resbody
165
-
166
- exc_list, _asgn, rescue_body = *ch
167
-
168
- exc_names = []
169
- if exc_list.nil?
170
- exc_names << 'StandardError'
171
- elsif exc_list.type == :array
172
- exc_list.children.each do |e|
173
- name = const_full_name(e)
174
- exc_names << (name || 'StandardError')
175
- end
176
- else
177
- name = const_full_name(exc_list)
178
- exc_names << (name || 'StandardError')
179
- end
180
-
181
- rtype = last_expr_type(rescue_body) || 'Object'
182
- spec[:rescues] << [exc_names, rtype]
183
- end
184
- else
185
- spec[:normal] = last_expr_type(body) || 'Object'
186
- end
187
-
188
- spec
87
+ # Return structured normal/rescue return information for a method node.
88
+ #
89
+ # Result shape:
90
+ # - `:normal` => the normal return type
91
+ # - `:rescues` => rescue-branch conditional return info
92
+ #
93
+ # @param [Parser::AST::Node] node
94
+ # @param [String] fallback_type
95
+ # @param [Boolean] nil_as_optional
96
+ # @return [Hash]
97
+ def returns_spec_from_node(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true)
98
+ Returns.returns_spec_from_node(
99
+ node,
100
+ fallback_type: fallback_type,
101
+ nil_as_optional: nil_as_optional
102
+ )
189
103
  end
190
104
 
191
- # +Docscribe::Infer.last_expr_type+ -> Object
192
- #
193
- # Method documentation.
194
- #
195
- # @param [Object] node Param documentation.
196
- # @return [Object]
197
- def last_expr_type(node)
198
- return nil unless node
199
-
200
- case node.type
201
- when :begin
202
- last = node.children.last
203
- last_expr_type(last)
204
- when :if
205
- t = last_expr_type(node.children[1])
206
- e = last_expr_type(node.children[2])
207
- unify_types(t, e)
208
- when :case
209
- # check whens and else
210
- branches = node.children[1..].compact.flat_map do |child|
211
- if child && child.type == :when
212
- last_expr_type(child.children.last)
213
- else
214
- last_expr_type(child)
215
- end
216
- end
217
- branches.compact!
218
- branches.empty? ? 'Object' : branches.reduce { |a, b| unify_types(a, b) }
219
- when :return
220
- type_from_literal(node.children.first)
221
- else
222
- type_from_literal(node)
223
- end
105
+ # Infer the type of the last expression in an AST node.
106
+ #
107
+ # @param [Parser::AST::Node, nil] node
108
+ # @param [String] fallback_type
109
+ # @param [Boolean] nil_as_optional
110
+ # @return [String, nil]
111
+ def last_expr_type(node, fallback_type: FALLBACK_TYPE, nil_as_optional: true)
112
+ Returns.last_expr_type(
113
+ node,
114
+ fallback_type: fallback_type,
115
+ nil_as_optional: nil_as_optional
116
+ )
224
117
  end
225
118
 
226
- # +Docscribe::Infer.const_full_name+ -> Object
227
- #
228
- # Method documentation.
119
+ # Convert a constant AST node into its fully qualified name.
229
120
  #
230
- # @param [Object] n Param documentation.
231
- # @return [Object]
121
+ # @param [Parser::AST::Node, nil] n
122
+ # @return [String, nil]
232
123
  def const_full_name(n)
233
- return nil unless n.is_a?(Parser::AST::Node)
234
-
235
- case n.type
236
- when :const
237
- scope, name = *n
238
- scope_name = const_full_name(scope)
239
- if scope_name && !scope_name.empty?
240
- "#{scope_name}::#{name}"
241
- elsif scope_name == '' # leading ::
242
- "::#{name}"
243
- else
244
- name.to_s
245
- end
246
- when :cbase
247
- '' # represents leading :: scope
248
- end
124
+ Names.const_full_name(n)
249
125
  end
250
126
 
251
- # +Docscribe::Infer.type_from_literal+ -> Object
252
- #
253
- # Method documentation.
127
+ # Infer a YARD-ish type string from a literal AST node.
254
128
  #
255
- # @param [Object] node Param documentation.
256
- # @return [Object]
257
- def type_from_literal(node)
258
- return 'Object' unless node
259
-
260
- case node.type
261
- when :int then 'Integer'
262
- when :float then 'Float'
263
- when :str, :dstr then 'String'
264
- when :sym then 'Symbol'
265
- when :true, :false then 'Boolean' # rubocop:disable Lint/BooleanSymbol
266
- when :nil then 'nil'
267
- when :array then 'Array'
268
- when :hash then 'Hash'
269
- when :regexp then 'Regexp'
270
- when :const
271
- node.children.last.to_s
272
- when :send
273
- recv, meth, = node.children
274
- if meth == :new && recv && recv.type == :const
275
- recv.children.last.to_s
276
- else
277
- 'Object'
278
- end
279
- else
280
- 'Object'
281
- end
129
+ # @param [Parser::AST::Node, nil] node
130
+ # @param [String] fallback_type
131
+ # @return [String]
132
+ def type_from_literal(node, fallback_type: FALLBACK_TYPE)
133
+ Literals.type_from_literal(node, fallback_type: fallback_type)
282
134
  end
283
135
 
284
- # +Docscribe::Infer.unify_types+ -> String
136
+ # Unify two inferred type strings conservatively.
285
137
  #
286
- # Method documentation.
287
- #
288
- # @param [Object] a Param documentation.
289
- # @param [Object] b Param documentation.
138
+ # @param [String, nil] a
139
+ # @param [String, nil] b
140
+ # @param [String] fallback_type
141
+ # @param [Boolean] nil_as_optional
290
142
  # @return [String]
291
- def unify_types(a, b)
292
- a ||= 'Object'
293
- b ||= 'Object'
294
- return a if a == b
295
- # nil-union => Optional
296
- return "#{a == 'nil' ? b : a}?" if a == 'nil' || b == 'nil'
297
-
298
- 'Object'
143
+ def unify_types(a, b, fallback_type: FALLBACK_TYPE, nil_as_optional: true)
144
+ Returns.unify_types(
145
+ a,
146
+ b,
147
+ fallback_type: fallback_type,
148
+ nil_as_optional: nil_as_optional
149
+ )
299
150
  end
300
151
  end
301
152
  end