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