solargraph 0.54.1 → 0.56.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +3 -1
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +70 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/cache.rb +10 -1
  8. data/lib/solargraph/api_map/index.rb +175 -0
  9. data/lib/solargraph/api_map/store.rb +79 -126
  10. data/lib/solargraph/api_map.rb +247 -93
  11. data/lib/solargraph/bench.rb +17 -1
  12. data/lib/solargraph/complex_type/type_methods.rb +13 -1
  13. data/lib/solargraph/complex_type/unique_type.rb +118 -9
  14. data/lib/solargraph/complex_type.rb +48 -18
  15. data/lib/solargraph/convention/base.rb +3 -3
  16. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  17. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  18. data/lib/solargraph/convention/struct_definition.rb +101 -0
  19. data/lib/solargraph/convention.rb +5 -3
  20. data/lib/solargraph/doc_map.rb +274 -56
  21. data/lib/solargraph/equality.rb +33 -0
  22. data/lib/solargraph/gem_pins.rb +53 -37
  23. data/lib/solargraph/language_server/host/message_worker.rb +31 -11
  24. data/lib/solargraph/language_server/host.rb +24 -13
  25. data/lib/solargraph/language_server/message/base.rb +19 -12
  26. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  27. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  28. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  29. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  30. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  31. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  32. data/lib/solargraph/library.rb +13 -11
  33. data/lib/solargraph/location.rb +28 -0
  34. data/lib/solargraph/logging.rb +1 -0
  35. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  36. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  37. data/lib/solargraph/parser/node_methods.rb +15 -1
  38. data/lib/solargraph/parser/node_processor.rb +3 -1
  39. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  40. data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
  41. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
  42. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  43. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  46. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
  47. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  48. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  49. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  51. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  52. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  53. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +4 -3
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  57. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  58. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  59. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
  60. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  61. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  63. data/lib/solargraph/parser/region.rb +1 -1
  64. data/lib/solargraph/parser.rb +1 -0
  65. data/lib/solargraph/pin/base.rb +326 -43
  66. data/lib/solargraph/pin/base_variable.rb +18 -10
  67. data/lib/solargraph/pin/block.rb +3 -3
  68. data/lib/solargraph/pin/breakable.rb +9 -0
  69. data/lib/solargraph/pin/callable.rb +77 -6
  70. data/lib/solargraph/pin/closure.rb +18 -1
  71. data/lib/solargraph/pin/common.rb +5 -0
  72. data/lib/solargraph/pin/delegated_method.rb +20 -1
  73. data/lib/solargraph/pin/documenting.rb +16 -0
  74. data/lib/solargraph/pin/keyword.rb +7 -2
  75. data/lib/solargraph/pin/local_variable.rb +15 -6
  76. data/lib/solargraph/pin/method.rb +172 -42
  77. data/lib/solargraph/pin/namespace.rb +17 -9
  78. data/lib/solargraph/pin/parameter.rb +61 -11
  79. data/lib/solargraph/pin/proxy_type.rb +12 -6
  80. data/lib/solargraph/pin/reference/override.rb +10 -6
  81. data/lib/solargraph/pin/reference/require.rb +2 -2
  82. data/lib/solargraph/pin/signature.rb +42 -0
  83. data/lib/solargraph/pin/singleton.rb +1 -1
  84. data/lib/solargraph/pin/symbol.rb +3 -2
  85. data/lib/solargraph/pin/until.rb +18 -0
  86. data/lib/solargraph/pin/while.rb +18 -0
  87. data/lib/solargraph/pin.rb +4 -1
  88. data/lib/solargraph/pin_cache.rb +185 -0
  89. data/lib/solargraph/position.rb +16 -0
  90. data/lib/solargraph/range.rb +16 -0
  91. data/lib/solargraph/rbs_map/conversions.rb +199 -66
  92. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  93. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  94. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  95. data/lib/solargraph/rbs_map.rb +74 -16
  96. data/lib/solargraph/shell.rb +19 -18
  97. data/lib/solargraph/source/chain/array.rb +12 -8
  98. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  99. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  100. data/lib/solargraph/source/chain/call.rb +62 -25
  101. data/lib/solargraph/source/chain/constant.rb +1 -1
  102. data/lib/solargraph/source/chain/hash.rb +9 -3
  103. data/lib/solargraph/source/chain/head.rb +1 -1
  104. data/lib/solargraph/source/chain/if.rb +6 -1
  105. data/lib/solargraph/source/chain/link.rb +19 -5
  106. data/lib/solargraph/source/chain/literal.rb +27 -2
  107. data/lib/solargraph/source/chain/or.rb +1 -1
  108. data/lib/solargraph/source/chain/z_super.rb +1 -1
  109. data/lib/solargraph/source/chain.rb +106 -62
  110. data/lib/solargraph/source/cursor.rb +1 -11
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source.rb +2 -1
  113. data/lib/solargraph/source_map/clip.rb +4 -2
  114. data/lib/solargraph/source_map/mapper.rb +9 -5
  115. data/lib/solargraph/source_map.rb +0 -17
  116. data/lib/solargraph/type_checker/checks.rb +4 -0
  117. data/lib/solargraph/type_checker.rb +41 -14
  118. data/lib/solargraph/version.rb +1 -1
  119. data/lib/solargraph/views/_method.erb +10 -10
  120. data/lib/solargraph/views/_namespace.erb +3 -3
  121. data/lib/solargraph/views/document.erb +10 -10
  122. data/lib/solargraph/workspace/config.rb +7 -3
  123. data/lib/solargraph/workspace.rb +16 -6
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -2
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +5 -2
  127. data/lib/solargraph/yard_map/mapper.rb +5 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +6 -9
  130. data/lib/solargraph.rb +20 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +6 -5
  134. metadata +56 -29
  135. data/lib/solargraph/cache.rb +0 -77
@@ -12,6 +12,7 @@ module Solargraph
12
12
  autoload :Cache, 'solargraph/api_map/cache'
13
13
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
14
14
  autoload :Store, 'solargraph/api_map/store'
15
+ autoload :Index, 'solargraph/api_map/index'
15
16
 
16
17
  # @return [Array<String>]
17
18
  attr_reader :unresolved_requires
@@ -48,6 +49,15 @@ module Solargraph
48
49
  equality_fields.hash
49
50
  end
50
51
 
52
+ def to_s
53
+ self.class.to_s
54
+ end
55
+
56
+ # avoid enormous dump
57
+ def inspect
58
+ to_s
59
+ end
60
+
51
61
  # @param pins [Array<Pin::Base>]
52
62
  # @return [self]
53
63
  def index pins
@@ -56,7 +66,7 @@ module Solargraph
56
66
  @source_map_hash = {}
57
67
  implicit.clear
58
68
  cache.clear
59
- @store = Store.new(@@core_map.pins + pins)
69
+ store.update @@core_map.pins, pins
60
70
  self
61
71
  end
62
72
 
@@ -75,29 +85,36 @@ module Solargraph
75
85
  # @param bench [Bench]
76
86
  # @return [self]
77
87
  def catalog bench
78
- old_api_hash = @source_map_hash&.values&.map(&:api_hash)
79
- need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
80
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
81
- pins = bench.source_maps.flat_map(&:pins).flatten
88
+ @source_map_hash = bench.source_map_hash
89
+ iced_pins = bench.icebox.flat_map(&:pins)
90
+ live_pins = bench.live_map&.pins || []
82
91
  implicit.clear
83
92
  source_map_hash.each_value do |map|
84
93
  implicit.merge map.environ
85
94
  end
86
95
  unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
87
- if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
88
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
89
- @unresolved_requires = unresolved_requires
90
- need_to_uncache = true
96
+ recreate_docmap = @unresolved_requires != unresolved_requires ||
97
+ @doc_map&.uncached_yard_gemspecs&.any? ||
98
+ @doc_map&.uncached_rbs_collection_gemspecs&.any? ||
99
+ @doc_map&.rbs_collection_path != bench.workspace.rbs_collection_path
100
+ if recreate_docmap
101
+ @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
102
+ @unresolved_requires = @doc_map.unresolved_requires
91
103
  end
92
- @store = Store.new(@@core_map.pins + @doc_map.pins + implicit.pins + pins)
93
- @cache.clear if need_to_uncache
94
-
104
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
95
105
  @missing_docs = [] # @todo Implement missing docs
96
106
  self
97
107
  end
98
108
 
109
+ # @todo need to model type def statement in chains as a symbol so
110
+ # that this overload of 'protected' will typecheck @sg-ignore
111
+ # @sg-ignore
99
112
  protected def equality_fields
100
- [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
113
+ [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
114
+ end
115
+
116
+ def doc_map
117
+ @doc_map ||= DocMap.new([], [])
101
118
  end
102
119
 
103
120
  # @return [::Array<Gem::Specification>]
@@ -105,6 +122,16 @@ module Solargraph
105
122
  @doc_map&.uncached_gemspecs || []
106
123
  end
107
124
 
125
+ # @return [::Array<Gem::Specification>]
126
+ def uncached_rbs_collection_gemspecs
127
+ @doc_map.uncached_rbs_collection_gemspecs
128
+ end
129
+
130
+ # @return [::Array<Gem::Specification>]
131
+ def uncached_yard_gemspecs
132
+ @doc_map.uncached_yard_gemspecs
133
+ end
134
+
108
135
  # @return [Array<Pin::Base>]
109
136
  def core_pins
110
137
  @@core_map.pins
@@ -159,6 +186,18 @@ module Solargraph
159
186
  api_map
160
187
  end
161
188
 
189
+ def cache_all!(out)
190
+ @doc_map.cache_all!(out)
191
+ end
192
+
193
+ def cache_gem(gemspec, rebuild: false, out: nil)
194
+ @doc_map.cache(gemspec, rebuild: rebuild, out: out)
195
+ end
196
+
197
+ class << self
198
+ include Logging
199
+ end
200
+
162
201
  # Create an ApiMap with a workspace in the specified directory and cache
163
202
  # any missing gems.
164
203
  #
@@ -169,21 +208,20 @@ module Solargraph
169
208
  # @param directory [String]
170
209
  # @param out [IO] The output stream for messages
171
210
  # @return [ApiMap]
172
- def self.load_with_cache directory, out = IO::NULL
211
+ def self.load_with_cache directory, out
173
212
  api_map = load(directory)
174
- return api_map if api_map.uncached_gemspecs.empty?
175
-
176
- api_map.uncached_gemspecs.each do |gemspec|
177
- out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
178
- pins = GemPins.build(gemspec)
179
- Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
213
+ if api_map.uncached_gemspecs.empty?
214
+ logger.info { "All gems cached for #{directory}" }
215
+ return api_map
180
216
  end
217
+
218
+ api_map.cache_all!(out)
181
219
  load(directory)
182
220
  end
183
221
 
184
222
  # @return [Array<Solargraph::Pin::Base>]
185
223
  def pins
186
- store.pins
224
+ store.pins.clone.freeze
187
225
  end
188
226
 
189
227
  # An array of pins based on Ruby keywords (`if`, `end`, etc.).
@@ -255,15 +293,19 @@ module Solargraph
255
293
  # Should not be prefixed with '::'.
256
294
  # @return [String, nil] fully qualified tag
257
295
  def qualify tag, context_tag = ''
258
- return tag if ['self', nil].include?(tag)
296
+ return tag if ['Boolean', 'self', nil].include?(tag)
259
297
 
260
- context_type = ComplexType.parse(context_tag).force_rooted
298
+ context_type = ComplexType.try_parse(context_tag).force_rooted
261
299
  return unless context_type
262
300
 
263
301
  type = ComplexType.try_parse(tag)
264
302
  return unless type
303
+ return tag if type.literal?
304
+
305
+ context_type = ComplexType.try_parse(context_tag)
306
+ return unless context_type
265
307
 
266
- fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
308
+ fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
267
309
  return unless fqns
268
310
 
269
311
  fqns + type.substring
@@ -310,6 +352,11 @@ module Solargraph
310
352
  result
311
353
  end
312
354
 
355
+ # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
356
+ def visible_pins(*args, **kwargs, &blk)
357
+ Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
358
+ end
359
+
313
360
  # Get an array of class variable pins for a namespace.
314
361
  #
315
362
  # @param namespace [String] A fully qualified namespace
@@ -341,8 +388,17 @@ module Solargraph
341
388
  # @param deep [Boolean] True to include superclasses, mixins, etc.
342
389
  # @return [Array<Solargraph::Pin::Method>]
343
390
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
391
+ if rooted_tag.start_with? 'Array('
392
+ # Array() are really tuples - use our fill, as the RBS repo
393
+ # does not give us definitions for it
394
+ rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})"
395
+ end
396
+ rooted_type = ComplexType.try_parse(rooted_tag)
397
+ fqns = rooted_type.namespace
398
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
344
399
  cached = cache.get_methods(rooted_tag, scope, visibility, deep)
345
400
  return cached.clone unless cached.nil?
401
+ # @type [Array<Solargraph::Pin::Method>]
346
402
  result = []
347
403
  skip = Set.new
348
404
  if rooted_tag == ''
@@ -363,26 +419,47 @@ module Solargraph
363
419
  init_pin = get_method_stack(rooted_tag, 'initialize').first
364
420
  next pin unless init_pin
365
421
 
366
- type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
367
- Pin::Method.new(
422
+ type = ComplexType::SELF
423
+ new_pin = Pin::Method.new(
368
424
  name: 'new',
369
425
  scope: :class,
370
426
  location: init_pin.location,
371
- parameters: init_pin.parameters,
372
- signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
373
427
  return_type: type,
374
428
  comments: init_pin.comments,
375
- closure: init_pin.closure
376
- # @todo Hack to force TypeChecker#internal_or_core?
377
- ).tap { |pin| pin.source = :rbs }
429
+ closure: init_pin.closure,
430
+ source: init_pin.source,
431
+ type_location: init_pin.type_location,
432
+ )
433
+ new_pin.parameters = init_pin.parameters.map do |init_param|
434
+ param = init_param.clone
435
+ param.closure = new_pin
436
+ param.reset_generated!
437
+ param
438
+ end.freeze
439
+ new_pin.signatures = init_pin.signatures.map do |init_sig|
440
+ sig = init_sig.proxy(type)
441
+ sig.parameters = init_sig.parameters.map do |param|
442
+ param = param.clone
443
+ param.closure = new_pin
444
+ param.reset_generated!
445
+ param
446
+ end.freeze
447
+ sig.closure = new_pin
448
+ sig.reset_generated!
449
+ sig
450
+ end.freeze
451
+ new_pin
378
452
  end
379
453
  end
380
454
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
381
455
  result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
382
456
  end
383
- resolved = resolve_method_aliases(result, visibility)
384
- cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
385
- resolved
457
+ result = resolve_method_aliases(result, visibility)
458
+ if namespace_pin && rooted_tag != rooted_type.name
459
+ result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
460
+ end
461
+ cache.set_methods(rooted_tag, scope, visibility, deep, result)
462
+ result
386
463
  end
387
464
 
388
465
  # Get an array of method pins for a complex type.
@@ -410,7 +487,7 @@ module Solargraph
410
487
  result = Set.new
411
488
  complex_type.each do |type|
412
489
  if type.duck_type?
413
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1])
490
+ result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
414
491
  result.merge get_methods('Object')
415
492
  else
416
493
  unless type.nil? || type.name == 'void'
@@ -438,8 +515,13 @@ module Solargraph
438
515
  # @param name [String] Method name to look up
439
516
  # @param scope [Symbol] :instance or :class
440
517
  # @return [Array<Solargraph::Pin::Method>]
441
- def get_method_stack rooted_tag, name, scope: :instance
442
- get_methods(rooted_tag, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
518
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
519
+ rooted_type = ComplexType.parse(rooted_tag)
520
+ fqns = rooted_type.namespace
521
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
522
+ methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
523
+ methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
524
+ methods
443
525
  end
444
526
 
445
527
  # Get an array of all suggestions that match the specified path.
@@ -474,13 +556,8 @@ module Solargraph
474
556
  .select { |path| path.downcase.include?(query.downcase) }
475
557
  end
476
558
 
477
- # Get YARD documentation for the specified path.
478
- #
479
- # @example
480
- # api_map.document('String#split')
481
- #
482
- # @todo This method is likely superfluous. Calling get_path_pins directly
483
- # should be sufficient.
559
+ # @deprecated This method is likely superfluous. Calling #get_path_pins
560
+ # directly should be sufficient.
484
561
  #
485
562
  # @param path [String] The path to find
486
563
  # @return [Enumerable<Pin::Base>]
@@ -574,6 +651,19 @@ module Solargraph
574
651
  store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
575
652
  end
576
653
 
654
+ # @param pins [Enumerable<Pin::Base>]
655
+ # @param visibility [Enumerable<Symbol>]
656
+ # @return [Array<Pin::Base>]
657
+ def resolve_method_aliases pins, visibility = [:public, :private, :protected]
658
+ with_resolved_aliases = pins.map do |pin|
659
+ resolved = resolve_method_alias(pin)
660
+ next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
661
+ resolved
662
+ end.compact
663
+ logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" }
664
+ GemPins.combine_method_pins_by_path(with_resolved_aliases)
665
+ end
666
+
577
667
  private
578
668
 
579
669
  # A hash of source maps with filename keys.
@@ -601,6 +691,7 @@ module Solargraph
601
691
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
602
692
  fqns = rooted_type.namespace
603
693
  fqns_generic_params = rooted_type.all_params
694
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
604
695
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
605
696
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
606
697
  return [] if skip.include?(reqstr)
@@ -615,43 +706,26 @@ module Solargraph
615
706
  # Store#get_methods doesn't know about full tags, just
616
707
  # namespaces; resolving the generics in the method pins is this
617
708
  # class' responsibility
618
- raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
619
- namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
620
- methods = if namespace_pin && rooted_tag != fqns
621
- methods = raw_methods.map do |method_pin|
622
- method_pin.resolve_generics(namespace_pin, rooted_type)
623
- end
624
- else
625
- raw_methods
626
- end
709
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
627
710
  result.concat methods
628
711
  if deep
629
712
  if scope == :instance
630
713
  store.get_includes(fqns).reverse.each do |include_tag|
631
714
  rooted_include_tag = qualify(include_tag, rooted_tag)
632
- # Ensure the types returned by the included methods are
633
- # relative to the generics passed to the include. e.g.,
634
- # Foo<String> might include Enumerable<String>
635
- #
636
- # @todo perform the same translation in the other areas
637
- # here after adding a spec and handling things correctly
638
- # in ApiMap::Store and RbsMap::Conversions
639
- resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
640
- methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
641
- result.concat methods
715
+ result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
642
716
  end
643
- fqsc = qualify_superclass(fqns)
644
- unless fqsc.nil?
645
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
717
+ rooted_sc_tag = qualify_superclass(rooted_tag)
718
+ unless rooted_sc_tag.nil?
719
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
646
720
  end
647
721
  else
648
722
  store.get_extends(fqns).reverse.each do |em|
649
723
  fqem = qualify(em, fqns)
650
724
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
651
725
  end
652
- fqsc = qualify_superclass(fqns)
653
- unless fqsc.nil?
654
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
726
+ rooted_sc_tag = qualify_superclass(rooted_tag)
727
+ unless rooted_sc_tag.nil?
728
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true)
655
729
  end
656
730
  unless no_core || fqns.empty?
657
731
  type = get_namespace_type(fqns)
@@ -667,6 +741,41 @@ module Solargraph
667
741
  result
668
742
  end
669
743
 
744
+ # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
745
+ # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
746
+ # parameter - used to pull generics information
747
+ # @param type [ComplexType] The type which is having its
748
+ # methods supplemented from fq_reference_tag
749
+ # @param scope [Symbol] :class or :instance
750
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
751
+ # @param deep [Boolean]
752
+ # @param skip [Set<String>]
753
+ # @param no_core [Boolean] Skip core classes if true
754
+ # @return [Array<Pin::Base>]
755
+ def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
756
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
757
+
758
+ # Ensure the types returned by the methods in the referenced
759
+ # type are relative to the generic values passed in the
760
+ # reference. e.g., Foo<String> might include Enumerable<String>
761
+ #
762
+ # @todo perform the same translation in the other areas
763
+ # here after adding a spec and handling things correctly
764
+ # in ApiMap::Store and RbsMap::Conversions for each
765
+ resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type)
766
+ # @todo Can inner_get_methods be cached? Lots of lookups of base types going on.
767
+ methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core)
768
+ if namespace_pin && !resolved_reference_type.all_params.empty?
769
+ reference_pin = store.get_path_pins(resolved_reference_type.name).select { |p| p.is_a?(Pin::Namespace) }.first
770
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" }
771
+ methods = methods.map do |method_pin|
772
+ method_pin.resolve_generics(reference_pin, resolved_reference_type)
773
+ end
774
+ end
775
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" }
776
+ methods
777
+ end
778
+
670
779
  # @param fqns [String]
671
780
  # @param visibility [Array<Symbol>]
672
781
  # @param skip [Set<String>]
@@ -702,15 +811,19 @@ module Solargraph
702
811
  qualify namespace, context.split('::')[0..-2].join('::')
703
812
  end
704
813
 
705
- # @param fqsub [String]
814
+ # @param fq_tag [String]
706
815
  # @return [String, nil]
707
- def qualify_superclass fqsub
708
- sup = store.get_superclass(fqsub)
709
- return nil if sup.nil?
710
- parts = fqsub.split('::')
816
+ def qualify_superclass fq_sub_tag
817
+ fq_sub_type = ComplexType.try_parse(fq_sub_tag)
818
+ fq_sub_ns = fq_sub_type.name
819
+ sup_tag = store.get_superclass(fq_sub_tag)
820
+ sup_type = ComplexType.try_parse(sup_tag)
821
+ sup_ns = sup_type.name
822
+ return nil if sup_tag.nil?
823
+ parts = fq_sub_ns.split('::')
711
824
  last = parts.pop
712
- parts.pop if last == sup
713
- qualify(sup, parts.join('::'))
825
+ parts.pop if last == sup_ns
826
+ qualify(sup_tag, parts.join('::'))
714
827
  end
715
828
 
716
829
  # @param name [String] Namespace to fully qualify
@@ -768,8 +881,8 @@ module Solargraph
768
881
 
769
882
  # Sort an array of pins to put nil or undefined variables last.
770
883
  #
771
- # @param pins [Enumerable<Solargraph::Pin::Base>]
772
- # @return [Enumerable<Solargraph::Pin::Base>]
884
+ # @param pins [Enumerable<Pin::BaseVariable>]
885
+ # @return [Enumerable<Pin::BaseVariable>]
773
886
  def prefer_non_nil_variables pins
774
887
  result = []
775
888
  nil_pins = []
@@ -783,37 +896,78 @@ module Solargraph
783
896
  result + nil_pins
784
897
  end
785
898
 
786
- # @param pins [Enumerable<Pin::Base>]
787
- # @param visibility [Enumerable<Symbol>]
788
- # @return [Array<Pin::Base>]
789
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
790
- pins.map do |pin|
791
- resolved = resolve_method_alias(pin)
792
- next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
793
- resolved
794
- end.compact
795
- end
796
-
797
899
  # @param pin [Pin::MethodAlias, Pin::Base]
798
900
  # @return [Pin::Method]
799
901
  def resolve_method_alias pin
800
902
  return pin unless pin.is_a?(Pin::MethodAlias)
801
903
  return nil if @method_alias_stack.include?(pin.path)
802
904
  @method_alias_stack.push pin.path
803
- origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
905
+ origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope, preserve_generics: true).first
804
906
  @method_alias_stack.pop
805
907
  return nil if origin.nil?
806
908
  args = {
807
909
  location: pin.location,
910
+ type_location: origin.type_location,
808
911
  closure: pin.closure,
809
912
  name: pin.name,
810
913
  comments: origin.comments,
811
914
  scope: origin.scope,
915
+ # context: pin.context,
812
916
  visibility: origin.visibility,
813
- signatures: origin.signatures,
814
- attribute: origin.attribute?
917
+ signatures: origin.signatures.map(&:clone).freeze,
918
+ attribute: origin.attribute?,
919
+ generics: origin.generics.clone,
920
+ return_type: origin.return_type,
921
+ source: :resolve_method_alias
815
922
  }
816
- Pin::Method.new **args
923
+ out = Pin::Method.new **args
924
+ out.signatures.each do |sig|
925
+ sig.parameters = sig.parameters.map(&:clone).freeze
926
+ sig.source = :resolve_method_alias
927
+ sig.parameters.each do |param|
928
+ param.closure = out
929
+ param.source = :resolve_method_alias
930
+ param.reset_generated!
931
+ end
932
+ sig.closure = out
933
+ sig.reset_generated!
934
+ end
935
+ logger.debug { "ApiMap#resolve_method_alias(pin=#{pin}) - returning #{out} from #{origin}" }
936
+ out
937
+ end
938
+
939
+ include Logging
940
+
941
+ private
942
+
943
+ # @param namespace_pin [Pin::Namespace]
944
+ # @param rooted_type [ComplexType]
945
+ # @param pins [Enumerable<Pin::Base>]
946
+ # @return [Array<Pin::Base>]
947
+ def erase_generics(namespace_pin, rooted_type, pins)
948
+ return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
949
+
950
+ logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
951
+ pins.map do |method_pin|
952
+ method_pin.erase_generics(namespace_pin.generics)
953
+ end
954
+ end
955
+
956
+ # @param namespace_pin [Pin::Namespace]
957
+ # @param rooted_type [ComplexType]
958
+ def should_erase_generics_when_done?(namespace_pin, rooted_type)
959
+ has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
960
+ end
961
+
962
+ # @param namespace_pin [Pin::Namespace]
963
+ def has_generics?(namespace_pin)
964
+ namespace_pin && !namespace_pin.generics.empty?
965
+ end
966
+
967
+ # @param namespace_pin [Pin::Namespace]
968
+ # @param rooted_type [ComplexType]
969
+ def can_resolve_generics?(namespace_pin, rooted_type)
970
+ has_generics?(namespace_pin) && !rooted_type.all_params.empty?
817
971
  end
818
972
  end
819
973
  end
@@ -11,18 +11,34 @@ module Solargraph
11
11
  # @return [Workspace]
12
12
  attr_reader :workspace
13
13
 
14
+ # @return [SourceMap]
15
+ attr_reader :live_map
16
+
14
17
  # @return [Set<String>]
15
18
  attr_reader :external_requires
16
19
 
17
20
  # @param source_maps [Array<SourceMap>, Set<SourceMap>]
18
21
  # @param workspace [Workspace]
22
+ # @param live_map [SourceMap, nil]
19
23
  # @param external_requires [Array<String>, Set<String>]
20
- def initialize source_maps: [], workspace: Workspace.new, external_requires: []
24
+ def initialize source_maps: [], workspace: Workspace.new, live_map: nil, external_requires: []
21
25
  @source_maps = source_maps.to_set
22
26
  @workspace = workspace
27
+ @live_map = live_map
23
28
  @external_requires = external_requires.reject { |path| workspace.would_require?(path) }
24
29
  .compact
25
30
  .to_set
26
31
  end
32
+
33
+ # @return [Hash{String => SourceMap}]
34
+ def source_map_hash
35
+ # @todo Work around #to_h bug in current Ruby head (3.5) with #map#to_h
36
+ @source_map_hash ||= source_maps.map { |s| [s.filename, s] }
37
+ .to_h
38
+ end
39
+
40
+ def icebox
41
+ @icebox ||= (source_maps - [live_map])
42
+ end
27
43
  end
28
44
  end
@@ -88,6 +88,7 @@ module Solargraph
88
88
  # @return [Symbol, nil]
89
89
  attr_reader :parameters_type
90
90
 
91
+ # @type [Hash{String => Symbol}]
91
92
  PARAMETERS_TYPE_BY_STARTING_TAG = {
92
93
  '{' => :hash,
93
94
  '(' => :fixed,
@@ -129,6 +130,13 @@ module Solargraph
129
130
  end.call
130
131
  end
131
132
 
133
+ def namespace_type
134
+ return ComplexType.parse('::Object') if duck_type?
135
+ return ComplexType.parse('::NilClass') if nil_type?
136
+ return subtypes.first if (name == 'Class' || name == 'Module') && !subtypes.empty?
137
+ self
138
+ end
139
+
132
140
  # @return [String]
133
141
  def rooted_namespace
134
142
  return namespace unless rooted? && can_root_name?(namespace)
@@ -164,7 +172,11 @@ module Solargraph
164
172
  elsif fixed_parameters?
165
173
  "(#{subtypes_str})"
166
174
  else
167
- "<#{subtypes_str}>"
175
+ if name == 'Hash'
176
+ "<#{key_types_str}, #{subtypes_str}>"
177
+ else
178
+ "<#{key_types_str}#{subtypes_str}>"
179
+ end
168
180
  end
169
181
  end
170
182