solargraph 0.54.4 → 0.56.2

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 +62 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/index.rb +24 -16
  8. data/lib/solargraph/api_map/store.rb +48 -23
  9. data/lib/solargraph/api_map.rb +175 -77
  10. data/lib/solargraph/bench.rb +17 -1
  11. data/lib/solargraph/complex_type/type_methods.rb +6 -1
  12. data/lib/solargraph/complex_type/unique_type.rb +98 -9
  13. data/lib/solargraph/complex_type.rb +35 -6
  14. data/lib/solargraph/convention/base.rb +3 -3
  15. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
  16. data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
  17. data/lib/solargraph/convention/data_definition.rb +104 -0
  18. data/lib/solargraph/convention/gemspec.rb +2 -1
  19. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  20. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  21. data/lib/solargraph/convention/struct_definition.rb +141 -0
  22. data/lib/solargraph/convention.rb +5 -3
  23. data/lib/solargraph/doc_map.rb +277 -57
  24. data/lib/solargraph/gem_pins.rb +53 -37
  25. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  26. data/lib/solargraph/language_server/host.rb +12 -2
  27. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  28. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  29. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  30. data/lib/solargraph/library.rb +45 -17
  31. data/lib/solargraph/location.rb +21 -0
  32. data/lib/solargraph/logging.rb +1 -0
  33. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  34. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  35. data/lib/solargraph/parser/node_methods.rb +14 -0
  36. data/lib/solargraph/parser/node_processor/base.rb +9 -4
  37. data/lib/solargraph/parser/node_processor.rb +21 -8
  38. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  39. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  40. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  41. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  44. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  46. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  47. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  48. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  49. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  51. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  52. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  53. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  57. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  58. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  59. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  60. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  61. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  63. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  64. data/lib/solargraph/parser/region.rb +1 -1
  65. data/lib/solargraph/parser.rb +1 -0
  66. data/lib/solargraph/pin/base.rb +316 -28
  67. data/lib/solargraph/pin/base_variable.rb +16 -9
  68. data/lib/solargraph/pin/block.rb +2 -0
  69. data/lib/solargraph/pin/breakable.rb +9 -0
  70. data/lib/solargraph/pin/callable.rb +74 -3
  71. data/lib/solargraph/pin/closure.rb +18 -1
  72. data/lib/solargraph/pin/common.rb +5 -0
  73. data/lib/solargraph/pin/delegated_method.rb +20 -1
  74. data/lib/solargraph/pin/documenting.rb +16 -0
  75. data/lib/solargraph/pin/keyword.rb +7 -2
  76. data/lib/solargraph/pin/local_variable.rb +15 -6
  77. data/lib/solargraph/pin/method.rb +169 -43
  78. data/lib/solargraph/pin/namespace.rb +17 -9
  79. data/lib/solargraph/pin/parameter.rb +60 -11
  80. data/lib/solargraph/pin/proxy_type.rb +12 -6
  81. data/lib/solargraph/pin/reference/override.rb +10 -6
  82. data/lib/solargraph/pin/reference/require.rb +2 -2
  83. data/lib/solargraph/pin/signature.rb +42 -0
  84. data/lib/solargraph/pin/singleton.rb +1 -1
  85. data/lib/solargraph/pin/symbol.rb +3 -2
  86. data/lib/solargraph/pin/until.rb +18 -0
  87. data/lib/solargraph/pin/while.rb +18 -0
  88. data/lib/solargraph/pin.rb +4 -1
  89. data/lib/solargraph/pin_cache.rb +185 -0
  90. data/lib/solargraph/position.rb +9 -0
  91. data/lib/solargraph/range.rb +9 -0
  92. data/lib/solargraph/rbs_map/conversions.rb +221 -67
  93. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  94. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  95. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  96. data/lib/solargraph/rbs_map.rb +74 -17
  97. data/lib/solargraph/shell.rb +17 -18
  98. data/lib/solargraph/source/chain/array.rb +11 -7
  99. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  100. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  101. data/lib/solargraph/source/chain/call.rb +53 -23
  102. data/lib/solargraph/source/chain/constant.rb +1 -1
  103. data/lib/solargraph/source/chain/hash.rb +4 -3
  104. data/lib/solargraph/source/chain/head.rb +1 -1
  105. data/lib/solargraph/source/chain/if.rb +1 -1
  106. data/lib/solargraph/source/chain/link.rb +2 -0
  107. data/lib/solargraph/source/chain/literal.rb +22 -2
  108. data/lib/solargraph/source/chain/or.rb +1 -1
  109. data/lib/solargraph/source/chain/z_super.rb +1 -1
  110. data/lib/solargraph/source/chain.rb +78 -48
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source_map/clip.rb +3 -1
  113. data/lib/solargraph/source_map/mapper.rb +9 -5
  114. data/lib/solargraph/source_map.rb +0 -17
  115. data/lib/solargraph/type_checker/checks.rb +4 -0
  116. data/lib/solargraph/type_checker.rb +35 -8
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/views/_method.erb +10 -10
  119. data/lib/solargraph/views/_namespace.erb +3 -3
  120. data/lib/solargraph/views/document.erb +10 -10
  121. data/lib/solargraph/workspace/config.rb +1 -1
  122. data/lib/solargraph/workspace.rb +23 -5
  123. data/lib/solargraph/yard_map/helpers.rb +29 -1
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  127. data/lib/solargraph/yard_map/mapper.rb +4 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +7 -8
  130. data/lib/solargraph.rb +32 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +2 -1
  134. metadata +37 -9
  135. data/lib/solargraph/cache.rb +0 -77
@@ -66,7 +66,7 @@ module Solargraph
66
66
  @source_map_hash = {}
67
67
  implicit.clear
68
68
  cache.clear
69
- store.update! @@core_map.pins, pins
69
+ store.update @@core_map.pins, pins
70
70
  self
71
71
  end
72
72
 
@@ -85,24 +85,23 @@ module Solargraph
85
85
  # @param bench [Bench]
86
86
  # @return [self]
87
87
  def catalog bench
88
- old_api_hash = @source_map_hash&.values&.map(&:api_hash)
89
- need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
90
- # @todo Work around #to_h problem in current Ruby head (3.5)
91
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
92
- 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 || []
93
91
  implicit.clear
94
92
  source_map_hash.each_value do |map|
95
93
  implicit.merge map.environ
96
94
  end
97
95
  unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
98
- if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
99
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
100
- @unresolved_requires = unresolved_requires
101
- 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
102
103
  end
103
- store.update! @@core_map.pins + @doc_map.pins, implicit.pins + pins
104
- @cache.clear if need_to_uncache
105
-
104
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
106
105
  @missing_docs = [] # @todo Implement missing docs
107
106
  self
108
107
  end
@@ -111,7 +110,11 @@ module Solargraph
111
110
  # that this overload of 'protected' will typecheck @sg-ignore
112
111
  # @sg-ignore
113
112
  protected def equality_fields
114
- [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([], [])
115
118
  end
116
119
 
117
120
  # @return [::Array<Gem::Specification>]
@@ -119,6 +122,16 @@ module Solargraph
119
122
  @doc_map&.uncached_gemspecs || []
120
123
  end
121
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
+
122
135
  # @return [Array<Pin::Base>]
123
136
  def core_pins
124
137
  @@core_map.pins
@@ -173,6 +186,18 @@ module Solargraph
173
186
  api_map
174
187
  end
175
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
+
176
201
  # Create an ApiMap with a workspace in the specified directory and cache
177
202
  # any missing gems.
178
203
  #
@@ -183,15 +208,14 @@ module Solargraph
183
208
  # @param directory [String]
184
209
  # @param out [IO] The output stream for messages
185
210
  # @return [ApiMap]
186
- def self.load_with_cache directory, out = IO::NULL
211
+ def self.load_with_cache directory, out
187
212
  api_map = load(directory)
188
- return api_map if api_map.uncached_gemspecs.empty?
189
-
190
- api_map.uncached_gemspecs.each do |gemspec|
191
- out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
192
- pins = GemPins.build(gemspec)
193
- 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
194
216
  end
217
+
218
+ api_map.cache_all!(out)
195
219
  load(directory)
196
220
  end
197
221
 
@@ -269,15 +293,19 @@ module Solargraph
269
293
  # Should not be prefixed with '::'.
270
294
  # @return [String, nil] fully qualified tag
271
295
  def qualify tag, context_tag = ''
272
- return tag if ['self', nil].include?(tag)
296
+ return tag if ['Boolean', 'self', nil].include?(tag)
273
297
 
274
- context_type = ComplexType.parse(context_tag).force_rooted
298
+ context_type = ComplexType.try_parse(context_tag).force_rooted
275
299
  return unless context_type
276
300
 
277
301
  type = ComplexType.try_parse(tag)
278
302
  return unless type
303
+ return tag if type.literal?
279
304
 
280
- fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
305
+ context_type = ComplexType.try_parse(context_tag)
306
+ return unless context_type
307
+
308
+ fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
281
309
  return unless fqns
282
310
 
283
311
  fqns + type.substring
@@ -324,6 +352,11 @@ module Solargraph
324
352
  result
325
353
  end
326
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
+
327
360
  # Get an array of class variable pins for a namespace.
328
361
  #
329
362
  # @param namespace [String] A fully qualified namespace
@@ -355,6 +388,11 @@ module Solargraph
355
388
  # @param deep [Boolean] True to include superclasses, mixins, etc.
356
389
  # @return [Array<Solargraph::Pin::Method>]
357
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
358
396
  rooted_type = ComplexType.try_parse(rooted_tag)
359
397
  fqns = rooted_type.namespace
360
398
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -381,18 +419,36 @@ module Solargraph
381
419
  init_pin = get_method_stack(rooted_tag, 'initialize').first
382
420
  next pin unless init_pin
383
421
 
384
- type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
385
- Pin::Method.new(
422
+ type = ComplexType::SELF
423
+ new_pin = Pin::Method.new(
386
424
  name: 'new',
387
425
  scope: :class,
388
426
  location: init_pin.location,
389
- parameters: init_pin.parameters,
390
- signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
391
427
  return_type: type,
392
428
  comments: init_pin.comments,
393
- closure: init_pin.closure
394
- # @todo Hack to force TypeChecker#internal_or_core?
395
- ).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
396
452
  end
397
453
  end
398
454
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
@@ -431,7 +487,7 @@ module Solargraph
431
487
  result = Set.new
432
488
  complex_type.each do |type|
433
489
  if type.duck_type?
434
- 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)
435
491
  result.merge get_methods('Object')
436
492
  else
437
493
  unless type.nil? || type.name == 'void'
@@ -500,13 +556,8 @@ module Solargraph
500
556
  .select { |path| path.downcase.include?(query.downcase) }
501
557
  end
502
558
 
503
- # Get YARD documentation for the specified path.
504
- #
505
- # @example
506
- # api_map.document('String#split')
507
- #
508
- # @todo This method is likely superfluous. Calling get_path_pins directly
509
- # should be sufficient.
559
+ # @deprecated This method is likely superfluous. Calling #get_path_pins
560
+ # directly should be sufficient.
510
561
  #
511
562
  # @param path [String] The path to find
512
563
  # @return [Enumerable<Pin::Base>]
@@ -600,6 +651,19 @@ module Solargraph
600
651
  store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
601
652
  end
602
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
+
603
667
  private
604
668
 
605
669
  # A hash of source maps with filename keys.
@@ -648,29 +712,20 @@ module Solargraph
648
712
  if scope == :instance
649
713
  store.get_includes(fqns).reverse.each do |include_tag|
650
714
  rooted_include_tag = qualify(include_tag, rooted_tag)
651
- # Ensure the types returned by the included methods are
652
- # relative to the generics passed to the include. e.g.,
653
- # Foo<String> might include Enumerable<String>
654
- #
655
- # @todo perform the same translation in the other areas
656
- # here after adding a spec and handling things correctly
657
- # in ApiMap::Store and RbsMap::Conversions
658
- resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
659
- methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
660
- result.concat methods
715
+ result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
661
716
  end
662
- fqsc = qualify_superclass(fqns)
663
- unless fqsc.nil?
664
- 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)
665
720
  end
666
721
  else
667
722
  store.get_extends(fqns).reverse.each do |em|
668
723
  fqem = qualify(em, fqns)
669
724
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
670
725
  end
671
- fqsc = qualify_superclass(fqns)
672
- unless fqsc.nil?
673
- 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)
674
729
  end
675
730
  unless no_core || fqns.empty?
676
731
  type = get_namespace_type(fqns)
@@ -686,6 +741,41 @@ module Solargraph
686
741
  result
687
742
  end
688
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
+
689
779
  # @param fqns [String]
690
780
  # @param visibility [Array<Symbol>]
691
781
  # @param skip [Set<String>]
@@ -721,15 +811,19 @@ module Solargraph
721
811
  qualify namespace, context.split('::')[0..-2].join('::')
722
812
  end
723
813
 
724
- # @param fqsub [String]
814
+ # @param fq_tag [String]
725
815
  # @return [String, nil]
726
- def qualify_superclass fqsub
727
- sup = store.get_superclass(fqsub)
728
- return nil if sup.nil?
729
- 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('::')
730
824
  last = parts.pop
731
- parts.pop if last == sup
732
- qualify(sup, parts.join('::'))
825
+ parts.pop if last == sup_ns
826
+ qualify(sup_tag, parts.join('::'))
733
827
  end
734
828
 
735
829
  # @param name [String] Namespace to fully qualify
@@ -802,40 +896,44 @@ module Solargraph
802
896
  result + nil_pins
803
897
  end
804
898
 
805
- # @param pins [Enumerable<Pin::Base>]
806
- # @param visibility [Enumerable<Symbol>]
807
- # @return [Array<Pin::Base>]
808
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
809
- pins.map do |pin|
810
- resolved = resolve_method_alias(pin)
811
- next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
812
- resolved
813
- end.compact
814
- end
815
-
816
899
  # @param pin [Pin::MethodAlias, Pin::Base]
817
900
  # @return [Pin::Method]
818
901
  def resolve_method_alias pin
819
902
  return pin unless pin.is_a?(Pin::MethodAlias)
820
903
  return nil if @method_alias_stack.include?(pin.path)
821
904
  @method_alias_stack.push pin.path
822
- 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
823
906
  @method_alias_stack.pop
824
907
  return nil if origin.nil?
825
908
  args = {
826
909
  location: pin.location,
910
+ type_location: origin.type_location,
827
911
  closure: pin.closure,
828
912
  name: pin.name,
829
913
  comments: origin.comments,
830
914
  scope: origin.scope,
831
915
  # context: pin.context,
832
916
  visibility: origin.visibility,
833
- signatures: origin.signatures,
917
+ signatures: origin.signatures.map(&:clone).freeze,
834
918
  attribute: origin.attribute?,
835
- generics: origin.generics,
919
+ generics: origin.generics.clone,
836
920
  return_type: origin.return_type,
921
+ source: :resolve_method_alias
837
922
  }
838
- 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
839
937
  end
840
938
 
841
939
  include Logging
@@ -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,
@@ -171,7 +172,11 @@ module Solargraph
171
172
  elsif fixed_parameters?
172
173
  "(#{subtypes_str})"
173
174
  else
174
- "<#{subtypes_str}>"
175
+ if name == 'Hash'
176
+ "<#{key_types_str}, #{subtypes_str}>"
177
+ else
178
+ "<#{key_types_str}#{subtypes_str}>"
179
+ end
175
180
  end
176
181
  end
177
182
 
@@ -27,7 +27,7 @@ module Solargraph
27
27
  # @return [UniqueType]
28
28
  def self.parse name, substring = '', make_rooted: nil
29
29
  if name.start_with?(':::')
30
- raise "Illegal prefix: #{name}"
30
+ raise ComplexTypeError, "Illegal prefix: #{name}"
31
31
  end
32
32
  if name.start_with?('::')
33
33
  name = name[2..-1]
@@ -48,13 +48,20 @@ module Solargraph
48
48
  subs = ComplexType.parse(substring[1..-2], partial: true)
49
49
  parameters_type = PARAMETERS_TYPE_BY_STARTING_TAG.fetch(substring[0])
50
50
  if parameters_type == :hash
51
- 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)
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
52
  # @todo should be able to resolve map; both types have it
53
53
  # with same return type
54
54
  # @sg-ignore
55
55
  key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
56
56
  # @sg-ignore
57
57
  subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
58
+ elsif parameters_type == :list && name == 'Hash'
59
+ # Treat Hash<A, B> as Hash{A => B}
60
+ if subs.length != 2
61
+ raise ComplexTypeError, "Bad hash type: name=#{name}, substring=#{substring} - must have exactly two parameters"
62
+ end
63
+ key_types.concat(subs[0].map { |u| ComplexType.new([u]) })
64
+ subtypes.concat(subs[1].map { |u| ComplexType.new([u]) })
58
65
  else
59
66
  subtypes.concat subs
60
67
  end
@@ -73,19 +80,63 @@ module Solargraph
73
80
  end
74
81
  raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
75
82
  @name = name
76
- @key_types = key_types
77
- @subtypes = subtypes
83
+ @parameters_type = parameters_type
84
+ if implicit_union?
85
+ @key_types = key_types.uniq
86
+ @subtypes = subtypes.uniq
87
+ else
88
+ @key_types = key_types
89
+ @subtypes = subtypes
90
+ end
78
91
  @rooted = rooted
79
92
  @all_params = []
80
- @all_params.concat key_types
81
- @all_params.concat subtypes
82
- @parameters_type = parameters_type
93
+ @all_params.concat @key_types
94
+ @all_params.concat @subtypes
95
+ end
96
+
97
+ def implicit_union?
98
+ # @todo use api_map to establish number of generics in type;
99
+ # if only one is allowed but multiple are passed in, treat
100
+ # those as implicit unions
101
+ ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
83
102
  end
84
103
 
85
104
  def to_s
86
105
  tag
87
106
  end
88
107
 
108
+ def simplify_literals
109
+ transform do |t|
110
+ next t unless t.literal?
111
+ t.recreate(new_name: t.non_literal_name)
112
+ end
113
+ end
114
+
115
+ def literal?
116
+ non_literal_name != name
117
+ end
118
+
119
+ def non_literal_name
120
+ @non_literal_name ||= determine_non_literal_name
121
+ end
122
+
123
+ def determine_non_literal_name
124
+ # https://github.com/ruby/rbs/blob/master/docs/syntax.md
125
+ #
126
+ # _literal_ ::= _string-literal_
127
+ # | _symbol-literal_
128
+ # | _integer-literal_
129
+ # | `true`
130
+ # | `false`
131
+ return name if name.empty?
132
+ return 'NilClass' if name == 'nil'
133
+ return 'Boolean' if ['true', 'false'].include?(name)
134
+ return 'Symbol' if name[0] == ':'
135
+ return 'String' if ['"', "'"].include?(name[0])
136
+ return 'Integer' if name.match?(/^-?\d+$/)
137
+ name
138
+ end
139
+
89
140
  def eql?(other)
90
141
  self.class == other.class &&
91
142
  @name == other.name &&
@@ -113,6 +164,8 @@ module Solargraph
113
164
  def rbs_name
114
165
  if name == 'undefined'
115
166
  'untyped'
167
+ elsif literal?
168
+ name
116
169
  else
117
170
  rooted_name
118
171
  end
@@ -183,6 +236,23 @@ module Solargraph
183
236
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
184
237
  end
185
238
 
239
+ # @param api_map [ApiMap] The ApiMap that performs qualification
240
+ # @param atype [ComplexType] type which may be assigned to this type
241
+ def can_assign?(api_map, atype)
242
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
243
+ downcasted_atype = atype.downcast_to_literal_if_possible
244
+ out = downcasted_atype.all? do |autype|
245
+ autype.name == name || api_map.super_and_sub?(name, autype.name)
246
+ end
247
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
248
+ out
249
+ end
250
+
251
+ # @return [UniqueType]
252
+ def downcast_to_literal_if_possible
253
+ SINGLE_SUBTYPE.fetch(rooted_tag, self)
254
+ end
255
+
186
256
  # @param generics_to_resolve [Enumerable<String>]
187
257
  # @param context_type [UniqueType, nil]
188
258
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
@@ -243,7 +313,8 @@ module Solargraph
243
313
 
244
314
  transform(name) do |t|
245
315
  if t.name == GENERIC_TAG_NAME
246
- idx = definitions.generics.index(t.subtypes.first&.name)
316
+ generic_name = t.subtypes.first&.name
317
+ idx = definitions.generics.index(generic_name)
247
318
  next t if idx.nil?
248
319
  if context_type.parameters_type == :hash
249
320
  if idx == 0
@@ -253,8 +324,14 @@ module Solargraph
253
324
  else
254
325
  next ComplexType::UNDEFINED
255
326
  end
327
+ elsif context_type.all?(&:implicit_union?)
328
+ if idx == 0 && !context_type.all_params.empty?
329
+ ComplexType.new(context_type.all_params)
330
+ else
331
+ ComplexType::UNDEFINED
332
+ end
256
333
  else
257
- context_type.all_params[idx] || ComplexType::UNDEFINED
334
+ context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
258
335
  end
259
336
  else
260
337
  t
@@ -381,6 +458,18 @@ module Solargraph
381
458
 
382
459
  UNDEFINED = UniqueType.new('undefined', rooted: false)
383
460
  BOOLEAN = UniqueType.new('Boolean', rooted: true)
461
+ TRUE = UniqueType.new('true', rooted: true)
462
+ FALSE = UniqueType.new('false', rooted: true)
463
+ NIL = UniqueType.new('nil', rooted: true)
464
+ # @type [Hash{String => UniqueType}]
465
+ SINGLE_SUBTYPE = {
466
+ '::TrueClass' => UniqueType::TRUE,
467
+ '::FalseClass' => UniqueType::FALSE,
468
+ '::NilClass' => UniqueType::NIL
469
+ }.freeze
470
+
471
+
472
+ include Logging
384
473
  end
385
474
  end
386
475
  end