solargraph 0.58.1 → 0.59.0.dev.1

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +40 -36
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.rubocop_todo.yml +27 -49
  8. data/README.md +3 -3
  9. data/Rakefile +1 -0
  10. data/lib/solargraph/api_map/cache.rb +110 -110
  11. data/lib/solargraph/api_map/constants.rb +289 -279
  12. data/lib/solargraph/api_map/index.rb +204 -193
  13. data/lib/solargraph/api_map/source_to_yard.rb +109 -97
  14. data/lib/solargraph/api_map/store.rb +387 -384
  15. data/lib/solargraph/api_map.rb +1000 -945
  16. data/lib/solargraph/complex_type/conformance.rb +176 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +242 -228
  18. data/lib/solargraph/complex_type/unique_type.rb +632 -482
  19. data/lib/solargraph/complex_type.rb +549 -444
  20. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  21. data/lib/solargraph/convention/data_definition.rb +108 -105
  22. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  23. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  24. data/lib/solargraph/convention/struct_definition.rb +168 -164
  25. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  26. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  27. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  28. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  29. data/lib/solargraph/doc_map.rb +200 -439
  30. data/lib/solargraph/equality.rb +34 -34
  31. data/lib/solargraph/gem_pins.rb +97 -98
  32. data/lib/solargraph/language_server/host/dispatch.rb +131 -130
  33. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  34. data/lib/solargraph/language_server/host/sources.rb +100 -99
  35. data/lib/solargraph/language_server/host.rb +883 -878
  36. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +109 -114
  37. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  38. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  39. data/lib/solargraph/language_server/message/text_document/definition.rb +42 -40
  40. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +28 -26
  41. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  42. data/lib/solargraph/language_server/message/text_document/hover.rb +60 -58
  43. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  44. data/lib/solargraph/language_server/message/text_document/type_definition.rb +27 -25
  45. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +25 -23
  46. data/lib/solargraph/library.rb +729 -683
  47. data/lib/solargraph/location.rb +87 -82
  48. data/lib/solargraph/logging.rb +57 -37
  49. data/lib/solargraph/parser/comment_ripper.rb +76 -69
  50. data/lib/solargraph/parser/flow_sensitive_typing.rb +483 -255
  51. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  52. data/lib/solargraph/parser/node_processor.rb +63 -62
  53. data/lib/solargraph/parser/parser_gem/class_methods.rb +167 -149
  54. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  55. data/lib/solargraph/parser/parser_gem/node_methods.rb +506 -486
  56. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  57. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  58. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  59. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  60. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  61. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  62. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +41 -40
  63. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  64. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  65. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  66. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  67. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  68. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  69. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +53 -52
  70. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +296 -291
  71. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  72. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  73. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  74. data/lib/solargraph/parser/region.rb +75 -69
  75. data/lib/solargraph/parser/snippet.rb +17 -17
  76. data/lib/solargraph/pin/base.rb +761 -729
  77. data/lib/solargraph/pin/base_variable.rb +418 -126
  78. data/lib/solargraph/pin/block.rb +126 -104
  79. data/lib/solargraph/pin/breakable.rb +13 -9
  80. data/lib/solargraph/pin/callable.rb +278 -231
  81. data/lib/solargraph/pin/closure.rb +68 -72
  82. data/lib/solargraph/pin/common.rb +94 -79
  83. data/lib/solargraph/pin/compound_statement.rb +55 -0
  84. data/lib/solargraph/pin/conversions.rb +124 -123
  85. data/lib/solargraph/pin/delegated_method.rb +131 -120
  86. data/lib/solargraph/pin/documenting.rb +115 -114
  87. data/lib/solargraph/pin/instance_variable.rb +38 -34
  88. data/lib/solargraph/pin/keyword.rb +16 -20
  89. data/lib/solargraph/pin/local_variable.rb +31 -75
  90. data/lib/solargraph/pin/method.rb +720 -672
  91. data/lib/solargraph/pin/method_alias.rb +42 -34
  92. data/lib/solargraph/pin/namespace.rb +121 -115
  93. data/lib/solargraph/pin/parameter.rb +338 -275
  94. data/lib/solargraph/pin/proxy_type.rb +40 -39
  95. data/lib/solargraph/pin/reference/override.rb +47 -47
  96. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  97. data/lib/solargraph/pin/reference.rb +41 -39
  98. data/lib/solargraph/pin/search.rb +62 -61
  99. data/lib/solargraph/pin/signature.rb +69 -61
  100. data/lib/solargraph/pin/symbol.rb +53 -53
  101. data/lib/solargraph/pin/until.rb +18 -18
  102. data/lib/solargraph/pin/while.rb +18 -18
  103. data/lib/solargraph/pin.rb +46 -44
  104. data/lib/solargraph/pin_cache.rb +665 -245
  105. data/lib/solargraph/position.rb +118 -119
  106. data/lib/solargraph/range.rb +112 -112
  107. data/lib/solargraph/rbs_map/conversions.rb +846 -823
  108. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  109. data/lib/solargraph/rbs_map/stdlib_map.rb +72 -43
  110. data/lib/solargraph/rbs_map.rb +217 -163
  111. data/lib/solargraph/shell.rb +397 -352
  112. data/lib/solargraph/source/chain/call.rb +372 -337
  113. data/lib/solargraph/source/chain/constant.rb +28 -26
  114. data/lib/solargraph/source/chain/hash.rb +35 -34
  115. data/lib/solargraph/source/chain/if.rb +29 -28
  116. data/lib/solargraph/source/chain/instance_variable.rb +34 -13
  117. data/lib/solargraph/source/chain/literal.rb +53 -48
  118. data/lib/solargraph/source/chain/or.rb +31 -23
  119. data/lib/solargraph/source/chain.rb +294 -291
  120. data/lib/solargraph/source/change.rb +89 -82
  121. data/lib/solargraph/source/cursor.rb +172 -166
  122. data/lib/solargraph/source/source_chainer.rb +204 -194
  123. data/lib/solargraph/source/updater.rb +59 -55
  124. data/lib/solargraph/source.rb +524 -498
  125. data/lib/solargraph/source_map/clip.rb +237 -226
  126. data/lib/solargraph/source_map/data.rb +37 -34
  127. data/lib/solargraph/source_map/mapper.rb +282 -259
  128. data/lib/solargraph/source_map.rb +220 -212
  129. data/lib/solargraph/type_checker/problem.rb +34 -32
  130. data/lib/solargraph/type_checker/rules.rb +157 -84
  131. data/lib/solargraph/type_checker.rb +895 -814
  132. data/lib/solargraph/version.rb +1 -1
  133. data/lib/solargraph/workspace/config.rb +257 -255
  134. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  135. data/lib/solargraph/workspace/require_paths.rb +98 -97
  136. data/lib/solargraph/workspace.rb +362 -220
  137. data/lib/solargraph/yard_map/helpers.rb +45 -44
  138. data/lib/solargraph/yard_map/mapper/to_method.rb +134 -130
  139. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  140. data/lib/solargraph/yard_map/mapper.rb +84 -79
  141. data/lib/solargraph/yardoc.rb +97 -87
  142. data/lib/solargraph.rb +126 -105
  143. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  144. data/rbs/fills/tuple/tuple.rbs +28 -0
  145. data/rbs/shims/ast/0/node.rbs +5 -0
  146. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  147. data/rbs_collection.yaml +1 -1
  148. data/solargraph.gemspec +2 -1
  149. metadata +22 -17
  150. data/lib/solargraph/type_checker/checks.rb +0 -124
  151. data/lib/solargraph/type_checker/param_def.rb +0 -37
  152. data/lib/solargraph/yard_map/to_method.rb +0 -89
  153. data/sig/shims/ast/0/node.rbs +0 -5
  154. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  155. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  156. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  157. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  158. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  159. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  160. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  161. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  162. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,482 +1,632 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class ComplexType
5
- # An individual type signature. A complex type can consist of multiple
6
- # unique types.
7
- #
8
- class UniqueType
9
- include TypeMethods
10
- include Equality
11
-
12
- attr_reader :all_params, :subtypes, :key_types
13
-
14
- # @sg-ignore Fix "Not enough arguments to Module#protected"
15
- protected def equality_fields
16
- [@name, @all_params, @subtypes, @key_types]
17
- end
18
-
19
- # Create a UniqueType with the specified name and an optional substring.
20
- # The substring is the parameter section of a parametrized type, e.g.,
21
- # for the type `Array<String>`, the name is `Array` and the substring is
22
- # `<String>`.
23
- #
24
- # @param name [String] The name of the type
25
- # @param substring [String] The substring of the type
26
- # @param make_rooted [Boolean, nil]
27
- # @return [UniqueType]
28
- def self.parse name, substring = '', make_rooted: nil
29
- if name.start_with?(':::')
30
- raise ComplexTypeError, "Illegal prefix: #{name}"
31
- end
32
- if name.start_with?('::')
33
- name = name[2..-1]
34
- rooted = true
35
- elsif !can_root_name?(name)
36
- rooted = true
37
- else
38
- rooted = false
39
- end
40
- rooted = make_rooted unless make_rooted.nil?
41
-
42
- # @type [Array<ComplexType>]
43
- key_types = []
44
- # @type [Array<ComplexType>]
45
- subtypes = []
46
- parameters_type = nil
47
- unless substring.empty?
48
- subs = ComplexType.parse(substring[1..-2], partial: true)
49
- parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
50
- if parameters_type == :hash
51
- raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
52
- key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
53
- subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
54
- elsif parameters_type == :list && name == 'Hash'
55
- # Treat Hash<A, B> as Hash{A => B}
56
- if subs.length != 2
57
- raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters"
58
- end
59
- key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
60
- subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
61
- else
62
- subtypes.concat subs
63
- end
64
- end
65
- new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
66
- end
67
-
68
- # @param name [String]
69
- # @param key_types [Array<ComplexType>]
70
- # @param subtypes [Array<ComplexType>]
71
- # @param rooted [Boolean]
72
- # @param parameters_type [Symbol, nil]
73
- def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil)
74
- if parameters_type.nil?
75
- raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
76
- end
77
- raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
78
- @name = name
79
- @parameters_type = parameters_type
80
- if implicit_union?
81
- @key_types = key_types.uniq
82
- @subtypes = subtypes.uniq
83
- else
84
- @key_types = key_types
85
- @subtypes = subtypes
86
- end
87
- @rooted = rooted
88
- @all_params = []
89
- @all_params.concat @key_types
90
- @all_params.concat @subtypes
91
- end
92
-
93
- def implicit_union?
94
- # @todo use api_map to establish number of generics in type;
95
- # if only one is allowed but multiple are passed in, treat
96
- # those as implicit unions
97
- ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
98
- end
99
-
100
- def to_s
101
- tag
102
- end
103
-
104
- # @return [self]
105
- def simplify_literals
106
- transform do |t|
107
- next t unless t.literal?
108
- t.recreate(new_name: t.non_literal_name)
109
- end
110
- end
111
-
112
- def literal?
113
- non_literal_name != name
114
- end
115
-
116
- # @return [String]
117
- def non_literal_name
118
- @non_literal_name ||= determine_non_literal_name
119
- end
120
-
121
- # @return [String]
122
- def determine_non_literal_name
123
- # https://github.com/ruby/rbs/blob/master/docs/syntax.md
124
- #
125
- # _literal_ ::= _string-literal_
126
- # | _symbol-literal_
127
- # | _integer-literal_
128
- # | `true`
129
- # | `false`
130
- return name if name.empty?
131
- return 'NilClass' if name == 'nil'
132
- return 'Boolean' if ['true', 'false'].include?(name)
133
- return 'Symbol' if name[0] == ':'
134
- return 'String' if ['"', "'"].include?(name[0])
135
- return 'Integer' if name.match?(/^-?\d+$/)
136
- name
137
- end
138
-
139
- def eql?(other)
140
- self.class == other.class &&
141
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
142
- @name == other.name &&
143
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
144
- @key_types == other.key_types &&
145
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
146
- @subtypes == other.subtypes &&
147
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
148
- @rooted == other.rooted? &&
149
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
150
- @all_params == other.all_params &&
151
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
152
- @parameters_type == other.parameters_type
153
- end
154
-
155
- def ==(other)
156
- eql?(other)
157
- end
158
-
159
- def hash
160
- [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
161
- end
162
-
163
- # @return [Array<UniqueType>]
164
- def items
165
- [self]
166
- end
167
-
168
- # @return [String]
169
- def rbs_name
170
- if name == 'undefined'
171
- 'untyped'
172
- elsif literal?
173
- name
174
- else
175
- rooted_name
176
- end
177
- end
178
-
179
- # @return [String]
180
- def desc
181
- rooted_tags
182
- end
183
-
184
- # @return [String]
185
- def to_rbs
186
- if duck_type?
187
- 'untyped'
188
- elsif name == 'Boolean'
189
- 'bool'
190
- elsif name.downcase == 'nil'
191
- 'nil'
192
- elsif name == GENERIC_TAG_NAME
193
- all_params.first.name
194
- elsif ['Class', 'Module'].include?(name)
195
- rbs_name
196
- elsif ['Tuple', 'Array'].include?(name) && fixed_parameters?
197
- # tuples don't have a name; they're just [foo, bar, baz].
198
- if substring == '()'
199
- # but there are no zero element tuples, so we go with an array
200
- if rooted?
201
- '::Array[]'
202
- else
203
- 'Array[]'
204
- end
205
- else
206
- # already generated surrounded by []
207
- parameters_as_rbs
208
- end
209
- else
210
- "#{rbs_name}#{parameters_as_rbs}"
211
- end
212
- end
213
-
214
- # @return [Boolean]
215
- def parameters?
216
- !all_params.empty?
217
- end
218
-
219
- # @param types [Array<UniqueType, ComplexType>]
220
- # @return [String]
221
- def rbs_union(types)
222
- if types.length == 1
223
- types.first.to_rbs
224
- else
225
- "(#{types.map(&:to_rbs).join(' | ')})"
226
- end
227
- end
228
-
229
- # @return [String]
230
- def parameters_as_rbs
231
- return '' unless parameters?
232
-
233
- return "[#{all_params.map(&:to_rbs).join(', ')}]" if key_types.empty?
234
-
235
- # handle, e.g., Hash[K, V] case
236
- key_types_str = rbs_union(key_types)
237
- subtypes_str = rbs_union(subtypes)
238
- "[#{key_types_str}, #{subtypes_str}]"
239
- end
240
-
241
- def generic?
242
- name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
243
- end
244
-
245
- # @param api_map [ApiMap] The ApiMap that performs qualification
246
- # @param atype [ComplexType] type which may be assigned to this type
247
- def can_assign?(api_map, atype)
248
- logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
249
- downcasted_atype = atype.downcast_to_literal_if_possible
250
- out = downcasted_atype.all? do |autype|
251
- autype.name == name || api_map.super_and_sub?(name, autype.name)
252
- end
253
- logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
254
- out
255
- end
256
-
257
- # @return [UniqueType]
258
- def downcast_to_literal_if_possible
259
- SINGLE_SUBTYPE.fetch(rooted_tag, self)
260
- end
261
-
262
- # @param generics_to_resolve [Enumerable<String>]
263
- # @param context_type [UniqueType, nil]
264
- # @param resolved_generic_values [Hash{String => ComplexType, ComplexType::UniqueType}] Added to as types are encountered or resolved
265
- # @return [UniqueType, ComplexType]
266
- def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
267
- if name == ComplexType::GENERIC_TAG_NAME
268
- type_param = subtypes.first&.name
269
- return self unless generics_to_resolve.include? type_param
270
- unless context_type.nil? || !resolved_generic_values[type_param].nil?
271
- new_binding = true
272
- resolved_generic_values[type_param] = context_type
273
- end
274
- if new_binding
275
- resolved_generic_values.transform_values! do |complex_type|
276
- complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
277
- end
278
- end
279
- return resolved_generic_values[type_param] || self
280
- end
281
-
282
- # @todo typechecking should complain when the method being called has no @yieldparam tag
283
- new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:key_types)
284
- new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:subtypes)
285
- recreate(new_key_types: new_key_types, new_subtypes: new_subtypes)
286
- end
287
-
288
- # @param generics_to_resolve [Enumerable<String>]
289
- # @param context_type [UniqueType, nil]
290
- # @param resolved_generic_values [Hash{String => ComplexType}]
291
- # @yieldreturn [Array<ComplexType>]
292
- # @return [Array<ComplexType>]
293
- def resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values)
294
- types = yield self
295
- types.each_with_index.flat_map do |ct, i|
296
- ct.items.flat_map do |ut|
297
- context_params = yield context_type if context_type
298
- if context_params && context_params[i]
299
- type_arg = context_params[i]
300
- type_arg.map do |new_unique_context_type|
301
- ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, resolved_generic_values: resolved_generic_values
302
- end
303
- else
304
- ut.resolve_generics_from_context generics_to_resolve, nil, resolved_generic_values: resolved_generic_values
305
- end
306
- end
307
- end
308
- end
309
-
310
- # Probe the concrete type for each of the generic type
311
- # parameters used in this type, and return a new type if
312
- # possible.
313
- #
314
- # @param definitions [Pin::Namespace, Pin::Method] The module/class/method which uses generic types
315
- # @param context_type [ComplexType] The receiver type
316
- # @return [UniqueType, ComplexType]
317
- def resolve_generics definitions, context_type
318
- return self if definitions.nil? || definitions.generics.empty?
319
-
320
- transform(name) do |t|
321
- if t.name == GENERIC_TAG_NAME
322
- generic_name = t.subtypes.first&.name
323
- idx = definitions.generics.index(generic_name)
324
- next t if idx.nil?
325
- if context_type.parameters_type == :hash
326
- if idx == 0
327
- next ComplexType.new(context_type.key_types)
328
- elsif idx == 1
329
- next ComplexType.new(context_type.subtypes)
330
- else
331
- next ComplexType::UNDEFINED
332
- end
333
- elsif context_type.all?(&:implicit_union?)
334
- if idx == 0 && !context_type.all_params.empty?
335
- ComplexType.new(context_type.all_params)
336
- else
337
- ComplexType::UNDEFINED
338
- end
339
- else
340
- context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
341
- end
342
- else
343
- t
344
- end
345
- end
346
- end
347
-
348
- # @yieldparam t [self]
349
- # @yieldreturn [self]
350
- # @return [Array<self>]
351
- def map &block
352
- [block.yield(self)]
353
- end
354
-
355
- # @return [Array<UniqueType>]
356
- def to_a
357
- [self]
358
- end
359
-
360
- # @param new_name [String, nil]
361
- # @param make_rooted [Boolean, nil]
362
- # @param new_key_types [Array<ComplexType>, nil]
363
- # @param rooted [Boolean, nil]
364
- # @param new_subtypes [Array<ComplexType>, nil]
365
- # @return [self]
366
- def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
367
- raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
368
-
369
- new_name ||= name
370
- new_key_types ||= @key_types
371
- new_subtypes ||= @subtypes
372
- make_rooted = @rooted if make_rooted.nil?
373
- UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
374
- end
375
-
376
- # @return [String]
377
- def rooted_tags
378
- rooted_tag
379
- end
380
-
381
- # @return [String]
382
- def tags
383
- tag
384
- end
385
-
386
- # @return [self]
387
- def force_rooted
388
- transform do |t|
389
- t.recreate(make_rooted: true)
390
- end
391
- end
392
-
393
- # Apply the given transformation to each subtype and then finally to this type
394
- #
395
- # @param new_name [String, nil]
396
- # @yieldparam t [UniqueType]
397
- # @yieldreturn [self]
398
- # @return [self]
399
- def transform(new_name = nil, &transform_type)
400
- raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
401
- if name == ComplexType::GENERIC_TAG_NAME
402
- # doesn't make sense to manipulate the name of the generic
403
- new_key_types = @key_types
404
- new_subtypes = @subtypes
405
- else
406
- new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
407
- new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
408
- end
409
- new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
410
- yield new_type
411
- end
412
-
413
- # Generate a ComplexType that fully qualifies this type's namespaces.
414
- #
415
- # @param api_map [ApiMap] The ApiMap that performs qualification
416
- # @param context [String] The namespace from which to resolve names
417
- # @return [self, ComplexType, UniqueType] The generated ComplexType
418
- def qualify api_map, *gates
419
- transform do |t|
420
- next t if t.name == GENERIC_TAG_NAME
421
- next t if t.duck_type? || t.void? || t.undefined? || t.literal?
422
- open = t.rooted? ? [''] : gates
423
- fqns = api_map.qualify(t.non_literal_name, *open)
424
- if fqns.nil?
425
- next UniqueType::BOOLEAN if t.tag == 'Boolean'
426
- next UniqueType::UNDEFINED
427
- end
428
- t.recreate(new_name: fqns, make_rooted: true)
429
- end
430
- end
431
-
432
- def selfy?
433
- @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
434
- end
435
-
436
- # @param dst [ComplexType]
437
- # @return [self]
438
- def self_to_type dst
439
- object_type_dst = dst.reduce_class_type
440
- transform do |t|
441
- next t if t.name != 'self'
442
- object_type_dst
443
- end
444
- end
445
-
446
- def all_rooted?
447
- return true if name == GENERIC_TAG_NAME
448
- rooted? && all_params.all?(&:rooted?)
449
- end
450
-
451
- def rooted?
452
- !can_root_name? || @rooted
453
- end
454
-
455
- # @param name_to_check [String]
456
- def can_root_name?(name_to_check = name)
457
- self.class.can_root_name?(name_to_check)
458
- end
459
-
460
- # @param name [String]
461
- def self.can_root_name?(name)
462
- # name is not lowercase
463
- !name.empty? && name != name.downcase
464
- end
465
-
466
- UNDEFINED = UniqueType.new('undefined', rooted: false)
467
- BOOLEAN = UniqueType.new('Boolean', rooted: true)
468
- TRUE = UniqueType.new('true', rooted: true)
469
- FALSE = UniqueType.new('false', rooted: true)
470
- NIL = UniqueType.new('nil', rooted: true)
471
- # @type [Hash{String => UniqueType}]
472
- SINGLE_SUBTYPE = {
473
- '::TrueClass' => UniqueType::TRUE,
474
- '::FalseClass' => UniqueType::FALSE,
475
- '::NilClass' => UniqueType::NIL
476
- }.freeze
477
-
478
-
479
- include Logging
480
- end
481
- end
482
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class ComplexType
5
+ # An individual type signature. A complex type can consist of multiple
6
+ # unique types.
7
+ #
8
+ class UniqueType
9
+ include TypeMethods
10
+ include Equality
11
+
12
+ attr_reader :all_params, :subtypes, :key_types
13
+
14
+ protected def equality_fields
15
+ [@name, @all_params, @subtypes, @key_types]
16
+ end
17
+
18
+ # Create a UniqueType with the specified name and an optional substring.
19
+ # The substring is the parameter section of a parametrized type, e.g.,
20
+ # for the type `Array<String>`, the name is `Array` and the substring is
21
+ # `<String>`.
22
+ #
23
+ # @param name [String] The name of the type
24
+ # @param substring [String] The substring of the type
25
+ # @param make_rooted [Boolean, nil]
26
+ # @return [UniqueType]
27
+ def self.parse name, substring = '', make_rooted: nil
28
+ if name.start_with?(':::')
29
+ raise ComplexTypeError, "Illegal prefix: #{name}"
30
+ end
31
+ if name.start_with?('::')
32
+ name = name[2..-1]
33
+ rooted = true
34
+ elsif !can_root_name?(name)
35
+ rooted = true
36
+ else
37
+ rooted = false
38
+ end
39
+ rooted = make_rooted unless make_rooted.nil?
40
+
41
+ # @type [Array<ComplexType>]
42
+ key_types = []
43
+ # @type [Array<ComplexType>]
44
+ subtypes = []
45
+ parameters_type = nil
46
+ unless substring.empty?
47
+ subs = ComplexType.parse(substring[1..-2], partial: true)
48
+ # @sg-ignore Need to add nil check here
49
+ parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
50
+ if parameters_type == :hash
51
+ raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring}" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
52
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
53
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
54
+ elsif parameters_type == :list && name == 'Hash'
55
+ # Treat Hash<A, B> as Hash{A => B}
56
+ if subs.length != 2
57
+ raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters"
58
+ end
59
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
60
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
61
+ else
62
+ subtypes.concat subs
63
+ end
64
+ end
65
+ # @sg-ignore Need to add nil check here
66
+ new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
67
+ end
68
+
69
+ # @param name [String]
70
+ # @param key_types [Array<ComplexType>]
71
+ # @param subtypes [Array<ComplexType>]
72
+ # @param rooted [Boolean]
73
+ # @param parameters_type [Symbol, nil]
74
+ def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil)
75
+ if parameters_type.nil?
76
+ raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
77
+ end
78
+ raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
79
+ @name = name
80
+ @parameters_type = parameters_type
81
+ if implicit_union?
82
+ @key_types = key_types.uniq
83
+ @subtypes = subtypes.uniq
84
+ else
85
+ @key_types = key_types
86
+ @subtypes = subtypes
87
+ end
88
+ @rooted = rooted
89
+ @all_params = []
90
+ @all_params.concat @key_types
91
+ @all_params.concat @subtypes
92
+ end
93
+
94
+ def implicit_union?
95
+ # @todo use api_map to establish number of generics in type;
96
+ # if only one is allowed but multiple are passed in, treat
97
+ # those as implicit unions
98
+ ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
99
+ end
100
+
101
+ def to_s
102
+ tag
103
+ end
104
+
105
+ # @return [self]
106
+ def simplify_literals
107
+ transform do |t|
108
+ next t unless t.literal?
109
+ t.recreate(new_name: t.non_literal_name)
110
+ end
111
+ end
112
+
113
+ # @param exclude_types [ComplexType, nil]
114
+ # @param api_map [ApiMap]
115
+ # @return [ComplexType, self]
116
+ def exclude exclude_types, api_map
117
+ return self if exclude_types.nil?
118
+
119
+ types = items - exclude_types.items
120
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
121
+ ComplexType.new(types)
122
+ end
123
+
124
+ # @see https://en.wikipedia.org/wiki/Intersection_type
125
+ #
126
+ # @param intersection_type [ComplexType, ComplexType::UniqueType, nil]
127
+ # @param api_map [ApiMap]
128
+ # @return [self, ComplexType]
129
+ def intersect_with intersection_type, api_map
130
+ return self if intersection_type.nil?
131
+ return intersection_type if undefined?
132
+ types = []
133
+ # try to find common types via conformance
134
+ items.each do |ut|
135
+ intersection_type.each do |int_type|
136
+ if ut.conforms_to?(api_map, int_type, :assignment)
137
+ types << ut
138
+ elsif int_type.conforms_to?(api_map, ut, :assignment)
139
+ types << int_type
140
+ end
141
+ end
142
+ end
143
+ types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
144
+ ComplexType.new(types)
145
+ end
146
+
147
+ def simplifyable_literal?
148
+ literal? && name != 'nil'
149
+ end
150
+
151
+ def literal?
152
+ non_literal_name != name
153
+ end
154
+
155
+ # @return [String]
156
+ def non_literal_name
157
+ @non_literal_name ||= determine_non_literal_name
158
+ end
159
+
160
+ # @return [self]
161
+ def without_nil
162
+ return UniqueType::UNDEFINED if nil_type?
163
+
164
+ self
165
+ end
166
+
167
+ # @return [String]
168
+ def determine_non_literal_name
169
+ # https://github.com/ruby/rbs/blob/master/docs/syntax.md
170
+ #
171
+ # _literal_ ::= _string-literal_
172
+ # | _symbol-literal_
173
+ # | _integer-literal_
174
+ # | `true`
175
+ # | `false`
176
+ return name if name.empty?
177
+ return 'NilClass' if name == 'nil'
178
+ return 'Boolean' if ['true', 'false'].include?(name)
179
+ return 'Symbol' if name[0] == ':'
180
+ # @sg-ignore Need to add nil check here
181
+ return 'String' if ['"', "'"].include?(name[0])
182
+ return 'Integer' if name.match?(/^-?\d+$/)
183
+ name
184
+ end
185
+
186
+ def eql?(other)
187
+ self.class == other.class &&
188
+ # @sg-ignore flow sensitive typing should support .class == .class
189
+ @name == other.name &&
190
+ # @sg-ignore flow sensitive typing should support .class == .class
191
+ @key_types == other.key_types &&
192
+ # @sg-ignore flow sensitive typing should support .class == .class
193
+ @subtypes == other.subtypes &&
194
+ # @sg-ignore flow sensitive typing should support .class == .class
195
+ @rooted == other.rooted? &&
196
+ # @sg-ignore flow sensitive typing should support .class == .class
197
+ @all_params == other.all_params &&
198
+ # @sg-ignore flow sensitive typing should support .class == .class
199
+ @parameters_type == other.parameters_type
200
+ end
201
+
202
+ def ==(other)
203
+ eql?(other)
204
+ end
205
+
206
+ # https://www.playfulpython.com/type-hinting-covariance-contra-variance/
207
+
208
+ # "[Expected] type variables that are COVARIANT can be substituted with
209
+ # a more specific [inferred] type without causing errors"
210
+ #
211
+ # "[Expected] type variables that are CONTRAVARIANT can be substituted
212
+ # with a more general [inferred] type without causing errors"
213
+ #
214
+ # "[Expected] types where neither is possible are INVARIANT"
215
+ #
216
+ # @param _situation [:method_call, :return_type]
217
+ # @param default [Symbol] The default variance to return if the type is not one of the special cases
218
+ #
219
+ # @return [:invariant, :covariant, :contravariant]
220
+ def parameter_variance _situation, default = :covariant
221
+ # @todo RBS can specify variance - maybe we can use that info
222
+ # and also let folks specify?
223
+ #
224
+ # Array/Set: ideally invariant, since we don't know if user is
225
+ # going to add new stuff into it or read it. But we don't
226
+ # have a way to specify, so we use covariant
227
+ # Enumerable: covariant: can't be changed, so we can pass
228
+ # in more specific subtypes
229
+ # Hash: read-only would be covariant, read-write would be
230
+ # invariant if we could distinguish that - should default to
231
+ # covariant
232
+ # contravariant?: Proc - can be changed, so we can pass
233
+ # in less specific super types
234
+ if ['Hash', 'Tuple', 'Array', 'Set', 'Enumerable'].include?(name) && fixed_parameters?
235
+ :covariant
236
+ else
237
+ default
238
+ end
239
+ end
240
+
241
+ # Whether this is an RBS interface like _ToAry or _Each.
242
+ def interface?
243
+ name.start_with?('_')
244
+ end
245
+
246
+ # @param other [UniqueType]
247
+ def erased_version_of?(other)
248
+ name == other.name && (all_params.empty? || all_params.all?(&:undefined?))
249
+ end
250
+
251
+ # @param api_map [ApiMap]
252
+ # @param expected [ComplexType::UniqueType, ComplexType]
253
+ # @param situation [:method_call, :assignment, :return_type]
254
+ # @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic>]
255
+ # @param variance [:invariant, :covariant, :contravariant]
256
+ def conforms_to?(api_map, expected, situation, rules = [],
257
+ variance: erased_variance(situation))
258
+ return true if undefined? && rules.include?(:allow_undefined)
259
+
260
+ # @todo teach this to validate duck types as inferred type
261
+ return true if duck_type?
262
+
263
+ # complex types as expectations are unions - we only need to
264
+ # match one of their unique types
265
+ expected.any? do |expected_unique_type|
266
+ # :nocov:
267
+ unless expected_unique_type.instance_of?(UniqueType)
268
+ raise "Expected type must be a UniqueType, got #{expected_unique_type.class} in #{expected.inspect}"
269
+ end
270
+ # :nocov:
271
+ conformance = Conformance.new(api_map, self, expected_unique_type, situation,
272
+ rules, variance: variance)
273
+ conformance.conforms_to_unique_type?
274
+ end
275
+ end
276
+
277
+ def hash
278
+ [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
279
+ end
280
+
281
+ # @return [self]
282
+ def erase_parameters
283
+ UniqueType.new(name, rooted: rooted?, parameters_type: parameters_type)
284
+ end
285
+
286
+ # @return [Array<UniqueType>]
287
+ def items
288
+ [self]
289
+ end
290
+
291
+ # @return [String]
292
+ def rbs_name
293
+ if name == 'undefined'
294
+ 'untyped'
295
+ elsif literal?
296
+ name
297
+ else
298
+ rooted_name
299
+ end
300
+ end
301
+
302
+ # @return [String]
303
+ def desc
304
+ rooted_tags
305
+ end
306
+
307
+ # @sg-ignore Need better if/elseanalysis
308
+ # @return [String]
309
+ def to_rbs
310
+ if duck_type?
311
+ 'untyped'
312
+ elsif name == 'Boolean'
313
+ 'bool'
314
+ elsif name.downcase == 'nil'
315
+ 'nil'
316
+ elsif name == GENERIC_TAG_NAME
317
+ all_params.first&.name
318
+ elsif ['Class', 'Module'].include?(name)
319
+ rbs_name
320
+ elsif ['Tuple', 'Array'].include?(name) && fixed_parameters?
321
+ # tuples don't have a name; they're just [foo, bar, baz].
322
+ if substring == '()'
323
+ # but there are no zero element tuples, so we go with an array
324
+ if rooted?
325
+ '::Array[]'
326
+ else
327
+ 'Array[]'
328
+ end
329
+ else
330
+ # already generated surrounded by []
331
+ parameters_as_rbs
332
+ end
333
+ else
334
+ "#{rbs_name}#{parameters_as_rbs}"
335
+ end
336
+ end
337
+
338
+ # @return [Boolean]
339
+ def parameters?
340
+ !all_params.empty?
341
+ end
342
+
343
+ # @param types [Array<UniqueType, ComplexType>]
344
+ # @return [String]
345
+ def rbs_union(types)
346
+ if types.length == 1
347
+ types.first.to_rbs
348
+ else
349
+ "(#{types.map(&:to_rbs).join(' | ')})"
350
+ end
351
+ end
352
+
353
+ # @return [String]
354
+ def parameters_as_rbs
355
+ return '' unless parameters?
356
+
357
+ return "[#{all_params.map(&:to_rbs).join(', ')}]" if key_types.empty?
358
+
359
+ # handle, e.g., Hash[K, V] case
360
+ key_types_str = rbs_union(key_types)
361
+ subtypes_str = rbs_union(subtypes)
362
+ "[#{key_types_str}, #{subtypes_str}]"
363
+ end
364
+
365
+ def generic?
366
+ name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
367
+ end
368
+
369
+ def nullable?
370
+ nil_type?
371
+ end
372
+
373
+ # @yieldreturn [Boolean]
374
+ def all? &block
375
+ block.yield self
376
+ end
377
+
378
+ # @return [UniqueType]
379
+ def downcast_to_literal_if_possible
380
+ SINGLE_SUBTYPE.fetch(rooted_tag, self)
381
+ end
382
+
383
+ # @param generics_to_resolve [Enumerable<String>]
384
+ # @param context_type [ComplexType, UniqueType, nil]
385
+ # @param resolved_generic_values [Hash{String => ComplexType, ComplexType::UniqueType}] Added to as types are encountered or resolved
386
+ # @return [UniqueType, ComplexType]
387
+ def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
388
+ if name == ComplexType::GENERIC_TAG_NAME
389
+ type_param = subtypes.first&.name
390
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
391
+ return self unless generics_to_resolve.include? type_param
392
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
393
+ unless context_type.nil? || !resolved_generic_values[type_param].nil?
394
+ new_binding = true
395
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
396
+ resolved_generic_values[type_param] = context_type
397
+ end
398
+ if new_binding
399
+ resolved_generic_values.transform_values! do |complex_type|
400
+ complex_type.resolve_generics_from_context(generics_to_resolve, nil, resolved_generic_values: resolved_generic_values)
401
+ end
402
+ end
403
+ # @sg-ignore flow sensitive typing needs to eliminate literal from union with [:bar].include?(foo)
404
+ return resolved_generic_values[type_param] || self
405
+ end
406
+
407
+ # @todo typechecking should complain when the method being called has no @yieldparam tag
408
+ new_key_types = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:key_types)
409
+ new_subtypes = resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values, &:subtypes)
410
+ recreate(new_key_types: new_key_types, new_subtypes: new_subtypes)
411
+ end
412
+
413
+ # @param generics_to_resolve [Enumerable<String>]
414
+ # @param context_type [UniqueType, ComplexType, nil]
415
+ # @param resolved_generic_values [Hash{String => ComplexType}]
416
+ # @yieldreturn [Array<ComplexType>]
417
+ # @return [Array<ComplexType>]
418
+ def resolve_param_generics_from_context(generics_to_resolve, context_type, resolved_generic_values)
419
+ types = yield self
420
+ types.each_with_index.flat_map do |ct, i|
421
+ ct.items.flat_map do |ut|
422
+ context_params = yield context_type if context_type
423
+ if context_params && context_params[i]
424
+ type_arg = context_params[i]
425
+ type_arg.map do |new_unique_context_type|
426
+ ut.resolve_generics_from_context generics_to_resolve, new_unique_context_type, resolved_generic_values: resolved_generic_values
427
+ end
428
+ else
429
+ ut.resolve_generics_from_context generics_to_resolve, nil, resolved_generic_values: resolved_generic_values
430
+ end
431
+ end
432
+ end
433
+ end
434
+
435
+ # Probe the concrete type for each of the generic type
436
+ # parameters used in this type, and return a new type if
437
+ # possible.
438
+ #
439
+ # @param definitions [Pin::Namespace, Pin::Method] The module/class/method which uses generic types
440
+ # @param context_type [ComplexType] The receiver type
441
+ # @return [UniqueType, ComplexType]
442
+ def resolve_generics definitions, context_type
443
+ return self if definitions.nil? || definitions.generics.empty?
444
+
445
+ transform(name) do |t|
446
+ if t.name == GENERIC_TAG_NAME
447
+ generic_name = t.subtypes.first&.name
448
+ idx = definitions.generics.index(generic_name)
449
+ next t if idx.nil?
450
+ if context_type.parameters_type == :hash
451
+ if idx == 0
452
+ next ComplexType.new(context_type.key_types)
453
+ elsif idx == 1
454
+ next ComplexType.new(context_type.subtypes)
455
+ else
456
+ next ComplexType::UNDEFINED
457
+ end
458
+ elsif context_type.all?(&:implicit_union?)
459
+ if idx == 0 && !context_type.all_params.empty?
460
+ ComplexType.new(context_type.all_params)
461
+ else
462
+ ComplexType::UNDEFINED
463
+ end
464
+ else
465
+ # @sg-ignore Need to add nil check here
466
+ context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
467
+ end
468
+ else
469
+ t
470
+ end
471
+ end
472
+ end
473
+
474
+ # @yieldparam t [self]
475
+ # @yieldreturn [self]
476
+ # @return [Array<self>]
477
+ def map &block
478
+ [block.yield(self)]
479
+ end
480
+
481
+ # @yieldparam t [self]
482
+ # @yieldreturn [self]
483
+ # @return [Enumerable<self>]
484
+ def each &block
485
+ [self].each &block
486
+ end
487
+
488
+ # @return [Array<UniqueType>]
489
+ def to_a
490
+ [self]
491
+ end
492
+
493
+ # @param new_name [String, nil]
494
+ # @param make_rooted [Boolean, nil]
495
+ # @param new_key_types [Array<ComplexType>, nil]
496
+ # @param rooted [Boolean, nil]
497
+ # @param new_subtypes [Array<ComplexType>, nil]
498
+ # @return [self]
499
+ def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
500
+ raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
501
+
502
+ new_name ||= name
503
+ new_key_types ||= @key_types
504
+ new_subtypes ||= @subtypes
505
+ make_rooted = @rooted if make_rooted.nil?
506
+ # @sg-ignore flow sensitive typing needs better handling of ||= on lvars
507
+ UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
508
+ end
509
+
510
+ # @return [String]
511
+ def rooted_tags
512
+ rooted_tag
513
+ end
514
+
515
+ # @return [String]
516
+ def tags
517
+ tag
518
+ end
519
+
520
+ # @return [self]
521
+ def force_rooted
522
+ transform do |t|
523
+ t.recreate(make_rooted: true)
524
+ end
525
+ end
526
+
527
+ # Apply the given transformation to each subtype and then finally to this type
528
+ #
529
+ # @param new_name [String, nil]
530
+ # @yieldparam t [UniqueType]
531
+ # @yieldreturn [self]
532
+ # @return [self]
533
+ def transform(new_name = nil, &transform_type)
534
+ raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
535
+ if name == ComplexType::GENERIC_TAG_NAME
536
+ # doesn't make sense to manipulate the name of the generic
537
+ new_key_types = @key_types
538
+ new_subtypes = @subtypes
539
+ else
540
+ new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
541
+ new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
542
+ end
543
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
544
+ yield new_type
545
+ end
546
+
547
+ # Generate a ComplexType that fully qualifies this type's namespaces.
548
+ #
549
+ # @param api_map [ApiMap] The ApiMap that performs qualification
550
+ # @param context [String] The namespace from which to resolve names
551
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
552
+ def qualify api_map, *gates
553
+ transform do |t|
554
+ next t if t.name == GENERIC_TAG_NAME
555
+ next t if t.duck_type? || t.void? || t.undefined? || t.literal?
556
+ open = t.rooted? ? [''] : gates
557
+ fqns = api_map.qualify(t.non_literal_name, *open)
558
+ if fqns.nil?
559
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
560
+ next UniqueType::UNDEFINED
561
+ end
562
+ t.recreate(new_name: fqns, make_rooted: true)
563
+ end
564
+ end
565
+
566
+ def selfy?
567
+ @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
568
+ end
569
+
570
+ # @param dst [ComplexType]
571
+ # @return [self]
572
+ def self_to_type dst
573
+ object_type_dst = dst.reduce_class_type
574
+ transform do |t|
575
+ next t if t.name != 'self'
576
+ object_type_dst
577
+ end
578
+ end
579
+
580
+ # @yieldreturn [Boolean]
581
+ def any? &block
582
+ block.yield self
583
+ end
584
+
585
+ # @return [ComplexType]
586
+ def reduce_class_type
587
+ new_items = items.flat_map do |type|
588
+ next type unless ['Module', 'Class'].include?(type.name)
589
+ next type if type.all_params.empty?
590
+
591
+ type.all_params
592
+ end
593
+ ComplexType.new(new_items)
594
+ end
595
+
596
+ def all_rooted?
597
+ return true if name == GENERIC_TAG_NAME
598
+ rooted? && all_params.all?(&:rooted?)
599
+ end
600
+
601
+ def rooted?
602
+ !can_root_name? || @rooted
603
+ end
604
+
605
+ # @param name_to_check [String]
606
+ def can_root_name?(name_to_check = name)
607
+ self.class.can_root_name?(name_to_check)
608
+ end
609
+
610
+ # @param name [String]
611
+ def self.can_root_name?(name)
612
+ # name is not lowercase
613
+ !name.empty? && name != name.downcase
614
+ end
615
+
616
+ UNDEFINED = UniqueType.new('undefined', rooted: false)
617
+ BOOLEAN = UniqueType.new('Boolean', rooted: true)
618
+ TRUE = UniqueType.new('true', rooted: true)
619
+ FALSE = UniqueType.new('false', rooted: true)
620
+ NIL = UniqueType.new('nil', rooted: true)
621
+ # @type [Hash{String => UniqueType}]
622
+ SINGLE_SUBTYPE = {
623
+ '::TrueClass' => UniqueType::TRUE,
624
+ '::FalseClass' => UniqueType::FALSE,
625
+ '::NilClass' => UniqueType::NIL
626
+ }.freeze
627
+
628
+
629
+ include Logging
630
+ end
631
+ end
632
+ end