solargraph 0.53.4 → 0.54.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/lib/solargraph/api_map/cache.rb +2 -12
  4. data/lib/solargraph/api_map/store.rb +14 -5
  5. data/lib/solargraph/api_map.rb +67 -24
  6. data/lib/solargraph/complex_type/type_methods.rb +70 -39
  7. data/lib/solargraph/complex_type/unique_type.rb +187 -73
  8. data/lib/solargraph/complex_type.rb +105 -40
  9. data/lib/solargraph/doc_map.rb +19 -3
  10. data/lib/solargraph/gem_pins.rb +9 -1
  11. data/lib/solargraph/language_server/host/dispatch.rb +8 -1
  12. data/lib/solargraph/language_server/host/message_worker.rb +29 -3
  13. data/lib/solargraph/language_server/host/sources.rb +1 -61
  14. data/lib/solargraph/language_server/host.rb +21 -68
  15. data/lib/solargraph/language_server/message/base.rb +1 -1
  16. data/lib/solargraph/language_server/message/initialize.rb +14 -0
  17. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  18. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  19. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
  20. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  21. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  22. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  23. data/lib/solargraph/language_server/progress.rb +135 -0
  24. data/lib/solargraph/language_server.rb +1 -0
  25. data/lib/solargraph/library.rb +144 -113
  26. data/lib/solargraph/location.rb +14 -1
  27. data/lib/solargraph/parser/node_processor/base.rb +3 -2
  28. data/lib/solargraph/parser/node_processor.rb +1 -0
  29. data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
  30. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -7
  31. data/lib/solargraph/parser/parser_gem/node_methods.rb +1 -5
  32. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  33. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  34. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +53 -0
  35. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +6 -4
  36. data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
  37. data/lib/solargraph/parser.rb +2 -5
  38. data/lib/solargraph/pin/base.rb +15 -1
  39. data/lib/solargraph/pin/base_variable.rb +35 -6
  40. data/lib/solargraph/pin/block.rb +48 -11
  41. data/lib/solargraph/pin/callable.rb +147 -0
  42. data/lib/solargraph/pin/closure.rb +8 -3
  43. data/lib/solargraph/pin/common.rb +2 -6
  44. data/lib/solargraph/pin/conversions.rb +3 -2
  45. data/lib/solargraph/pin/delegated_method.rb +5 -1
  46. data/lib/solargraph/pin/documenting.rb +2 -0
  47. data/lib/solargraph/pin/instance_variable.rb +2 -2
  48. data/lib/solargraph/pin/method.rb +54 -32
  49. data/lib/solargraph/pin/namespace.rb +4 -4
  50. data/lib/solargraph/pin/parameter.rb +14 -39
  51. data/lib/solargraph/pin/proxy_type.rb +1 -1
  52. data/lib/solargraph/pin/signature.rb +3 -129
  53. data/lib/solargraph/pin.rb +4 -1
  54. data/lib/solargraph/range.rb +2 -4
  55. data/lib/solargraph/rbs_map/conversions.rb +79 -42
  56. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  57. data/lib/solargraph/rbs_map.rb +11 -3
  58. data/lib/solargraph/shell.rb +35 -15
  59. data/lib/solargraph/source/chain/array.rb +6 -5
  60. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  61. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  62. data/lib/solargraph/source/chain/call.rb +78 -50
  63. data/lib/solargraph/source/chain/link.rb +9 -0
  64. data/lib/solargraph/source/chain/or.rb +1 -1
  65. data/lib/solargraph/source/chain.rb +60 -16
  66. data/lib/solargraph/source/cursor.rb +13 -2
  67. data/lib/solargraph/source/updater.rb +1 -0
  68. data/lib/solargraph/source.rb +102 -129
  69. data/lib/solargraph/source_map/clip.rb +4 -4
  70. data/lib/solargraph/source_map/data.rb +30 -0
  71. data/lib/solargraph/source_map/mapper.rb +3 -2
  72. data/lib/solargraph/source_map.rb +37 -15
  73. data/lib/solargraph/type_checker/rules.rb +6 -1
  74. data/lib/solargraph/type_checker.rb +50 -25
  75. data/lib/solargraph/version.rb +1 -1
  76. data/lib/solargraph/views/environment.erb +3 -5
  77. data/lib/solargraph/workspace/config.rb +2 -1
  78. data/lib/solargraph/workspace.rb +13 -0
  79. metadata +6 -3
  80. data/lib/solargraph/language_server/host/cataloger.rb +0 -57
@@ -17,49 +17,87 @@ module Solargraph
17
17
  #
18
18
  # @param name [String] The name of the type
19
19
  # @param substring [String] The substring of the type
20
- def initialize name, substring = ''
20
+ # @param make_rooted [Boolean, nil]
21
+ # @return [UniqueType]
22
+ def self.parse name, substring = '', make_rooted: nil
23
+ if name.start_with?(':::')
24
+ raise "Illegal prefix: #{name}"
25
+ end
21
26
  if name.start_with?('::')
22
- @name = name[2..-1]
23
- @rooted = true
27
+ name = name[2..-1]
28
+ rooted = true
29
+ elsif !can_root_name?(name)
30
+ rooted = true
24
31
  else
25
- @name = name
26
- @rooted = false
32
+ rooted = false
27
33
  end
28
- @substring = substring
29
- @tag = @name + substring
30
- # @type [Array<ComplexType>]
31
- @key_types = []
34
+ rooted = make_rooted unless make_rooted.nil?
35
+
32
36
  # @type [Array<ComplexType>]
33
- @subtypes = []
37
+ key_types = []
34
38
  # @type [Array<ComplexType>]
35
- @all_params = []
36
- return unless parameters?
37
- # @todo we should be able to probe the type of 'subs' without
38
- # hoisting the definition outside of the if statement
39
- subs = if @substring.start_with?('<(') && @substring.end_with?(')>')
40
- ComplexType.parse(substring[2..-3], partial: true)
41
- else
42
- ComplexType.parse(substring[1..-2], partial: true)
43
- end
44
- if hash_parameters?
45
- raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
46
- # @todo should be able to resolve map; both types have it
47
- # with same return type
48
- # @sg-ignore
49
- @key_types.concat subs[0].map { |u| ComplexType.new([u]) }
50
- # @sg-ignore
51
- @subtypes.concat subs[1].map { |u| ComplexType.new([u]) }
52
- else
53
- @subtypes.concat subs
39
+ subtypes = []
40
+ parameters_type = nil
41
+ unless substring.empty?
42
+ subs = ComplexType.parse(substring[1..-2], partial: true)
43
+ parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
44
+ if parameters_type == :hash
45
+ raise ComplexTypeError, "Bad hash type" unless !subs.is_a?(ComplexType) and subs.length == 2 and !subs[0].is_a?(UniqueType) and !subs[1].is_a?(UniqueType)
46
+ # @todo should be able to resolve map; both types have it
47
+ # with same return type
48
+ # @sg-ignore
49
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
50
+ # @sg-ignore
51
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
52
+ else
53
+ subtypes.concat subs
54
+ end
54
55
  end
55
- @all_params.concat @key_types
56
- @all_params.concat @subtypes
56
+ new(name, key_types, subtypes, rooted: rooted, parameters_type: parameters_type)
57
+ end
58
+
59
+ # @param name [String]
60
+ # @param key_types [Array<ComplexType>]
61
+ # @param subtypes [Array<ComplexType>]
62
+ # @param rooted [Boolean]
63
+ # @param parameters_type [Symbol, nil]
64
+ def initialize(name, key_types = [], subtypes = [], rooted:, parameters_type: nil)
65
+ if parameters_type.nil?
66
+ raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
67
+ end
68
+ raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
69
+ @name = name
70
+ @key_types = key_types
71
+ @subtypes = subtypes
72
+ @rooted = rooted
73
+ @all_params = []
74
+ @all_params.concat key_types
75
+ @all_params.concat subtypes
76
+ @parameters_type = parameters_type
57
77
  end
58
78
 
59
79
  def to_s
60
80
  tag
61
81
  end
62
82
 
83
+ def eql?(other)
84
+ self.class == other.class &&
85
+ @name == other.name &&
86
+ @key_types == other.key_types &&
87
+ @subtypes == other.subtypes &&
88
+ @rooted == other.rooted? &&
89
+ @all_params == other.all_params &&
90
+ @parameters_type == other.parameters_type
91
+ end
92
+
93
+ def ==(other)
94
+ eql?(other)
95
+ end
96
+
97
+ def hash
98
+ [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
99
+ end
100
+
63
101
  # @return [Array<UniqueType>]
64
102
  def items
65
103
  [self]
@@ -76,11 +114,25 @@ module Solargraph
76
114
 
77
115
  # @return [String]
78
116
  def to_rbs
79
- if ['Tuple', 'Array'].include?(name) && fixed_parameters?
117
+ if duck_type?
118
+ 'untyped'
119
+ elsif name == 'Boolean'
120
+ 'bool'
121
+ elsif name.downcase == 'nil'
122
+ 'nil'
123
+ elsif name == GENERIC_TAG_NAME
124
+ all_params.first.name
125
+ elsif ['Class', 'Module'].include?(name)
126
+ rbs_name
127
+ elsif ['Tuple', 'Array'].include?(name) && fixed_parameters?
80
128
  # tuples don't have a name; they're just [foo, bar, baz].
81
129
  if substring == '()'
82
130
  # but there are no zero element tuples, so we go with an array
83
- 'Array[]'
131
+ if rooted?
132
+ '::Array[]'
133
+ else
134
+ 'Array[]'
135
+ end
84
136
  else
85
137
  # already generated surrounded by []
86
138
  parameters_as_rbs
@@ -90,16 +142,37 @@ module Solargraph
90
142
  end
91
143
  end
92
144
 
145
+ # @return [Boolean]
146
+ def parameters?
147
+ !all_params.empty?
148
+ end
149
+
150
+ # @param types [Array<UniqueType, ComplexType>]
151
+ # @return [String]
152
+ def rbs_union(types)
153
+ if types.length == 1
154
+ types.first.to_rbs
155
+ else
156
+ "(#{types.map(&:to_rbs).join(' | ')})"
157
+ end
158
+ end
159
+
93
160
  # @return [String]
94
161
  def parameters_as_rbs
95
- parameters? ? "[#{all_params.map { |s| s.to_rbs }.join(', ')}]" : ''
162
+ return '' unless parameters?
163
+
164
+ return "[#{all_params.map(&:to_rbs).join(', ')}]" if key_types.empty?
165
+
166
+ # handle, e.g., Hash[K, V] case
167
+ key_types_str = rbs_union(key_types)
168
+ subtypes_str = rbs_union(subtypes)
169
+ "[#{key_types_str}, #{subtypes_str}]"
96
170
  end
97
171
 
98
172
  def generic?
99
173
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
100
174
  end
101
175
 
102
-
103
176
  # @param generics_to_resolve [Enumerable<String>]
104
177
  # @param context_type [UniqueType, nil]
105
178
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
@@ -182,39 +255,35 @@ module Solargraph
182
255
  end
183
256
 
184
257
  # @param new_name [String, nil]
258
+ # @param make_rooted [Boolean, nil]
185
259
  # @param new_key_types [Array<UniqueType>, nil]
260
+ # @param rooted [Boolean, nil]
186
261
  # @param new_subtypes [Array<UniqueType>, nil]
187
262
  # @return [self]
188
- def recreate(new_name: nil, new_key_types: nil, new_subtypes: nil)
263
+ def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
264
+ raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
265
+
189
266
  new_name ||= name
190
267
  new_key_types ||= @key_types
191
268
  new_subtypes ||= @subtypes
192
- if new_key_types.none?(&:defined?) && new_subtypes.none?(&:defined?)
193
- # if all subtypes are undefined, erase down to the non-parametric type
194
- UniqueType.new(new_name)
195
- elsif new_key_types.empty? && new_subtypes.empty?
196
- UniqueType.new(new_name)
197
- elsif hash_parameters?
198
- UniqueType.new(new_name, "{#{new_key_types.join(', ')} => #{new_subtypes.join(', ')}}")
199
- elsif @substring.start_with?('<(')
200
- # @todo This clause is probably wrong, and if so, fixing it
201
- # will be some level of breaking change. Probably best
202
- # handled before real tuple support is rolled out and
203
- # folks start relying on it more.
204
- #
205
- # (String) is a one element tuple in https://yardoc.org/types
206
- # <String> is an array of zero or more Strings in https://yardoc.org/types
207
- # Array<(String)> could be an Array of one-element tuples or a
208
- # one element tuple. https://yardoc.org/types treats it
209
- # as the former.
210
- # Array<(String), Integer> is not ambiguous if we accept
211
- # (String) as a tuple type, but not currently understood
212
- # by Solargraph.
213
- UniqueType.new(new_name, "<(#{new_subtypes.join(', ')})>")
214
- elsif fixed_parameters?
215
- UniqueType.new(new_name, "(#{new_subtypes.join(', ')})")
216
- else
217
- UniqueType.new(new_name, "<#{new_subtypes.join(', ')}>")
269
+ make_rooted = @rooted if make_rooted.nil?
270
+ UniqueType.new(new_name, new_key_types, new_subtypes, rooted: make_rooted, parameters_type: parameters_type)
271
+ end
272
+
273
+ # @return [String]
274
+ def rooted_tags
275
+ rooted_tag
276
+ end
277
+
278
+ # @return [String]
279
+ def tags
280
+ tag
281
+ end
282
+
283
+ # @return [self]
284
+ def force_rooted
285
+ transform do |t|
286
+ t.recreate(make_rooted: true)
218
287
  end
219
288
  end
220
289
 
@@ -225,19 +294,35 @@ module Solargraph
225
294
  # @yieldreturn [self]
226
295
  # @return [self]
227
296
  def transform(new_name = nil, &transform_type)
228
- new_key_types = @key_types.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
229
- new_subtypes = @subtypes.flat_map { |ct| ct.map { |ut| ut.transform(&transform_type) } }.compact
230
- new_type = recreate(new_name: new_name || rooted_name, new_key_types: new_key_types, new_subtypes: new_subtypes)
297
+ raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
298
+ if name == ComplexType::GENERIC_TAG_NAME
299
+ # doesn't make sense to manipulate the name of the generic
300
+ new_key_types = @key_types
301
+ new_subtypes = @subtypes
302
+ else
303
+ new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
304
+ new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
305
+ end
306
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
231
307
  yield new_type
232
308
  end
233
309
 
234
- # Transform references to the 'self' type to the specified concrete namespace
235
- # @param dst [String]
236
- # @return [UniqueType]
237
- def self_to dst
310
+ # Generate a ComplexType that fully qualifies this type's namespaces.
311
+ #
312
+ # @param api_map [ApiMap] The ApiMap that performs qualification
313
+ # @param context [String] The namespace from which to resolve names
314
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
315
+ def qualify api_map, context = ''
238
316
  transform do |t|
239
- next t if t.name != 'self'
240
- t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
317
+ next t if t.name == GENERIC_TAG_NAME
318
+ next t if t.duck_type? || t.void? || t.undefined?
319
+ recon = (t.rooted? ? '' : context)
320
+ fqns = api_map.qualify(t.name, recon)
321
+ if fqns.nil?
322
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
323
+ next UniqueType::UNDEFINED
324
+ end
325
+ t.recreate(new_name: fqns, make_rooted: true)
241
326
  end
242
327
  end
243
328
 
@@ -245,8 +330,37 @@ module Solargraph
245
330
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
246
331
  end
247
332
 
248
- UNDEFINED = UniqueType.new('undefined')
249
- BOOLEAN = UniqueType.new('Boolean')
333
+ # @param dst [ComplexType]
334
+ # @return [self]
335
+ def self_to_type dst
336
+ object_type_dst = dst.reduce_class_type
337
+ transform do |t|
338
+ next t if t.name != 'self'
339
+ object_type_dst
340
+ end
341
+ end
342
+
343
+ def all_rooted?
344
+ return true if name == GENERIC_TAG_NAME
345
+ rooted? && all_params.all?(&:rooted?)
346
+ end
347
+
348
+ def rooted?
349
+ !can_root_name? || @rooted
350
+ end
351
+
352
+ def can_root_name?(name_to_check = name)
353
+ self.class.can_root_name?(name_to_check)
354
+ end
355
+
356
+ # @param name [String]
357
+ def self.can_root_name?(name)
358
+ # name is not lowercase
359
+ !name.empty? && name != name.downcase
360
+ end
361
+
362
+ UNDEFINED = UniqueType.new('undefined', rooted: false)
363
+ BOOLEAN = UniqueType.new('Boolean', rooted: true)
250
364
  end
251
365
  end
252
366
  end
@@ -11,20 +11,34 @@ module Solargraph
11
11
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
12
12
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
13
13
 
14
- # @param types [Array<[UniqueType, ComplexType]>]
14
+ # @param types [Array<UniqueType, ComplexType>]
15
15
  def initialize types = [UniqueType::UNDEFINED]
16
16
  # @todo @items here should not need an annotation
17
17
  # @type [Array<UniqueType>]
18
18
  @items = types.flat_map(&:items).uniq(&:to_s)
19
19
  end
20
20
 
21
+ def eql?(other)
22
+ self.class == other.class &&
23
+ @items == other.items
24
+ end
25
+
26
+ def ==(other)
27
+ self.eql?(other)
28
+ end
29
+
30
+ def hash
31
+ [self.class, @items].hash
32
+ end
33
+
21
34
  # @param api_map [ApiMap]
22
35
  # @param context [String]
23
36
  # @return [ComplexType]
24
37
  def qualify api_map, context = ''
25
38
  red = reduce_object
26
39
  types = red.items.map do |t|
27
- next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
40
+ next t if ['nil', 'void', 'undefined'].include?(t.name)
41
+ next t if ['::Boolean'].include?(t.rooted_name)
28
42
  t.qualify api_map, context
29
43
  end
30
44
  ComplexType.new(types).reduce_object
@@ -52,6 +66,16 @@ module Solargraph
52
66
  (@items.length > 1 ? ')' : ''))
53
67
  end
54
68
 
69
+ # @param dst [ComplexType]
70
+ # @return [ComplexType]
71
+ def self_to_type dst
72
+ object_type_dst = dst.reduce_class_type
73
+ transform do |t|
74
+ next t if t.name != 'self'
75
+ object_type_dst
76
+ end
77
+ end
78
+
55
79
  # @yieldparam [UniqueType]
56
80
  # @return [Array]
57
81
  def map &block
@@ -86,6 +110,10 @@ module Solargraph
86
110
  @items
87
111
  end
88
112
 
113
+ def tags
114
+ @items.map(&:tag).join(', ')
115
+ end
116
+
89
117
  # @param index [Integer]
90
118
  # @return [UniqueType]
91
119
  def [](index)
@@ -126,6 +154,10 @@ module Solargraph
126
154
  map(&:tag).join(', ')
127
155
  end
128
156
 
157
+ def rooted_tags
158
+ map(&:rooted_tag).join(', ')
159
+ end
160
+
129
161
  def all? &block
130
162
  @items.all? &block
131
163
  end
@@ -147,24 +179,23 @@ module Solargraph
147
179
  # @yieldreturn [UniqueType]
148
180
  # @return [ComplexType]
149
181
  def transform(new_name = nil, &transform_type)
182
+ raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
150
183
  ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) })
151
184
  end
152
185
 
186
+ # @return [self]
187
+ def force_rooted
188
+ transform do |t|
189
+ t.recreate(make_rooted: true)
190
+ end
191
+ end
192
+
153
193
  # @param definitions [Pin::Namespace, Pin::Method]
154
194
  # @param context_type [ComplexType]
155
195
  # @return [ComplexType]
156
196
  def resolve_generics definitions, context_type
157
197
  result = @items.map { |i| i.resolve_generics(definitions, context_type) }
158
- ComplexType.parse(*result.map(&:tag))
159
- end
160
-
161
- # @param dst [String]
162
- # @return [ComplexType]
163
- def self_to dst
164
- return self unless selfy?
165
- red = reduce_class(dst)
166
- result = @items.map { |i| i.self_to red }
167
- ComplexType.parse(*result.map(&:tag))
198
+ ComplexType.new(result)
168
199
  end
169
200
 
170
201
  def nullable?
@@ -176,35 +207,49 @@ module Solargraph
176
207
  @items.first.all_params || []
177
208
  end
178
209
 
210
+ # @return [ComplexType]
211
+ def reduce_class_type
212
+ new_items = items.flat_map do |type|
213
+ next type unless ['Module', 'Class'].include?(type.name)
214
+
215
+ type.all_params
216
+ end
217
+ ComplexType.new(new_items)
218
+ end
219
+
220
+ # every type and subtype in this union have been resolved to be
221
+ # fully qualified
222
+ def all_rooted?
223
+ all?(&:all_rooted?)
224
+ end
225
+
226
+ # every top-level type has resolved to be fully qualified; see
227
+ # #all_rooted? to check their subtypes as well
228
+ def rooted?
229
+ all?(&:rooted?)
230
+ end
231
+
179
232
  attr_reader :items
180
233
 
234
+ def rooted?
235
+ @items.all?(&:rooted?)
236
+ end
237
+
181
238
  protected
182
239
 
183
240
  # @return [ComplexType]
184
241
  def reduce_object
185
- return self if name != 'Object' || subtypes.empty?
186
- ComplexType.try_parse(reduce_class(subtypes.join(', ')))
242
+ new_items = items.flat_map do |ut|
243
+ next [ut] if ut.name != 'Object' || ut.subtypes.empty?
244
+ ut.subtypes
245
+ end
246
+ ComplexType.new(new_items)
187
247
  end
188
248
 
189
249
  def bottom?
190
250
  @items.all?(&:bot?)
191
251
  end
192
252
 
193
- private
194
-
195
- # @todo This is a quick and dirty hack that forces `self` keywords
196
- # to reference an instance of their class and never the class itself.
197
- # This behavior may change depending on which result is expected
198
- # from YARD conventions. See https://github.com/lsegal/yard/issues/1257
199
- # @param dst [String]
200
- # @return [String]
201
- def reduce_class dst
202
- while dst =~ /^(Class|Module)\<(.*?)\>$/
203
- dst = dst.sub(/^(Class|Module)\</, '').sub(/\>$/, '')
204
- end
205
- dst
206
- end
207
-
208
253
  class << self
209
254
  # Parse type strings into a ComplexType.
210
255
  #
@@ -220,10 +265,15 @@ module Solargraph
220
265
  #
221
266
  # @param *strings [Array<String>] The type definitions to parse
222
267
  # @return [ComplexType]
223
- # @overload parse(*strings, partial: false)
224
- # @todo Need ability to use a literal true as a type below
225
- # @param partial [Boolean] True if the string is part of a another type
226
- # @return [Array<UniqueType>]
268
+ # # @overload parse(*strings, partial: false)
269
+ # # @todo Need ability to use a literal true as a type below
270
+ # # @param partial [Boolean] True if the string is part of a another type
271
+ # # @return [Array<UniqueType>]
272
+ # @sg-ignore
273
+ # @todo To be able to select the right signature above,
274
+ # Chain::Call needs to know the decl type (:arg, :optarg,
275
+ # :kwarg, etc) of the arguments given, instead of just having
276
+ # an array of Chains as the arguments.
227
277
  def parse *strings, partial: false
228
278
  # @type [Hash{Array<String> => ComplexType}]
229
279
  @cache ||= {}
@@ -250,7 +300,7 @@ module Solargraph
250
300
  elsif base.end_with?('=')
251
301
  raise ComplexTypeError, "Invalid hash thing" unless key_types.nil?
252
302
  # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
253
- types.push UniqueType.new(base[0..-2].strip)
303
+ types.push UniqueType.parse(base[0..-2].strip, subtype_string)
254
304
  # @todo this should either expand key_type's type
255
305
  # automatically or complain about not being
256
306
  # compatible with key_type's type in type checking
@@ -281,7 +331,7 @@ module Solargraph
281
331
  next
282
332
  elsif char == ',' && point_stack == 0 && curly_stack == 0 && paren_stack == 0
283
333
  # types.push ComplexType.new([UniqueType.new(base.strip, subtype_string.strip)])
284
- types.push UniqueType.new(base.strip, subtype_string.strip)
334
+ types.push UniqueType.parse(base.strip, subtype_string.strip)
285
335
  base.clear
286
336
  subtype_string.clear
287
337
  next
@@ -294,7 +344,7 @@ module Solargraph
294
344
  end
295
345
  raise ComplexTypeError, "Unclosed subtype in #{type_string}" if point_stack != 0 || curly_stack != 0 || paren_stack != 0
296
346
  # types.push ComplexType.new([UniqueType.new(base, subtype_string)])
297
- types.push UniqueType.new(base.strip, subtype_string.strip)
347
+ types.push UniqueType.parse(base.strip, subtype_string.strip)
298
348
  end
299
349
  unless key_types.nil?
300
350
  raise ComplexTypeError, "Invalid use of key/value parameters" unless partial
@@ -311,18 +361,33 @@ module Solargraph
311
361
  def try_parse *strings
312
362
  parse *strings
313
363
  rescue ComplexTypeError => e
314
- Solargraph.logger.info "Error parsing complex type: #{e.message}"
364
+ Solargraph.logger.info "Error parsing complex type `#{strings.join(', ')}`: #{e.message}"
315
365
  ComplexType::UNDEFINED
316
366
  end
317
367
  end
318
368
 
319
369
  VOID = ComplexType.parse('void')
320
370
  UNDEFINED = ComplexType.parse('undefined')
321
- SYMBOL = ComplexType.parse('Symbol')
322
- ROOT = ComplexType.parse('Class<>')
371
+ SYMBOL = ComplexType.parse('::Symbol')
372
+ ROOT = ComplexType.parse('::Class<>')
323
373
  NIL = ComplexType.parse('nil')
324
374
  SELF = ComplexType.parse('self')
325
- BOOLEAN = ComplexType.parse('Boolean')
375
+ BOOLEAN = ComplexType.parse('::Boolean')
326
376
  BOT = ComplexType.parse('bot')
377
+
378
+ private
379
+
380
+ # @todo This is a quick and dirty hack that forces `self` keywords
381
+ # to reference an instance of their class and never the class itself.
382
+ # This behavior may change depending on which result is expected
383
+ # from YARD conventions. See https://github.com/lsegal/yard/issues/1257
384
+ # @param dst [String]
385
+ # @return [String]
386
+ def reduce_class dst
387
+ while dst =~ /^(Class|Module)\<(.*?)\>$/
388
+ dst = dst.sub(/^(Class|Module)\</, '').sub(/\>$/, '')
389
+ end
390
+ dst
391
+ end
327
392
  end
328
393
  end
@@ -18,9 +18,11 @@ module Solargraph
18
18
 
19
19
  # @param requires [Array<String>]
20
20
  # @param preferences [Array<Gem::Specification>]
21
- def initialize(requires, preferences)
21
+ # @param rbs_path [String, Pathname, nil]
22
+ def initialize(requires, preferences, rbs_path = nil)
22
23
  @requires = requires.compact
23
24
  @preferences = preferences.compact
25
+ @rbs_path = rbs_path
24
26
  generate
25
27
  end
26
28
 
@@ -39,6 +41,7 @@ module Solargraph
39
41
  @gems_in_memory ||= {}
40
42
  end
41
43
 
44
+ # @return [Set<Gem::Specification>]
42
45
  def dependencies
43
46
  @dependencies ||= (gemspecs.flat_map { |spec| fetch_dependencies(spec) } - gemspecs).to_set
44
47
  end
@@ -57,6 +60,7 @@ module Solargraph
57
60
  end
58
61
  end
59
62
  dependencies.each { |dep| try_cache dep }
63
+ @uncached_gemspecs.uniq!
60
64
  end
61
65
 
62
66
  # @return [Hash{String => Gem::Specification, nil}]
@@ -75,7 +79,8 @@ module Solargraph
75
79
  return if try_gem_in_memory(gemspec)
76
80
  cache_file = File.join('gems', "#{gemspec.name}-#{gemspec.version}.ser")
77
81
  if Cache.exist?(cache_file)
78
- gempins = Cache.load(cache_file)
82
+ cached = Cache.load(cache_file)
83
+ gempins = update_from_collection(gemspec, cached)
79
84
  self.class.gems_in_memory[gemspec] = gempins
80
85
  @pins.concat gempins
81
86
  else
@@ -93,7 +98,7 @@ module Solargraph
93
98
  @pins.concat map.pins
94
99
  else
95
100
  # @todo Temporarily ignoring unresolved `require 'set'`
96
- Solargraph.logger.warn "Require path #{path} could not be resolved" unless path == 'set'
101
+ Solargraph.logger.debug "Require path #{path} could not be resolved" unless path == 'set'
97
102
  end
98
103
  end
99
104
 
@@ -107,6 +112,17 @@ module Solargraph
107
112
  true
108
113
  end
109
114
 
115
+ def update_from_collection gemspec, gempins
116
+ return gempins unless @rbs_path && File.directory?(@rbs_path)
117
+ return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
118
+
119
+ rbs_map = RbsMap.new(gemspec.name, gemspec.version, directories: [@rbs_path])
120
+ return gempins unless rbs_map.resolved?
121
+
122
+ Solargraph.logger.info "Updating #{gemspec.name} #{gemspec.version} from collection"
123
+ GemPins.combine(gempins, rbs_map)
124
+ end
125
+
110
126
  # @param path [String]
111
127
  # @return [Gem::Specification, nil]
112
128
  def resolve_path_to_gemspec path
@@ -16,13 +16,21 @@ module Solargraph
16
16
  def self.build(gemspec)
17
17
  yard_pins = build_yard_pins(gemspec)
18
18
  rbs_map = RbsMap.from_gemspec(gemspec)
19
+ combine yard_pins, rbs_map
20
+ end
21
+
22
+ # @param yard_pins [Array<Pin::Base>]
23
+ # @param rbs_map [RbsMap]
24
+ # @return [Array<Pin::Base>]
25
+ def self.combine(yard_pins, rbs_map)
19
26
  in_yard = Set.new
20
27
  combined = yard_pins.map do |yard|
21
28
  in_yard.add yard.path
22
29
  next yard unless yard.is_a?(Pin::Method)
30
+
23
31
  rbs = rbs_map.path_pin(yard.path, Pin::Method)
24
32
  next yard unless rbs
25
- # @todo Could not include: attribute and anon_splat
33
+
26
34
  # @sg-ignore
27
35
  yard.class.new(
28
36
  location: yard.location,