solargraph 0.54.5 → 0.55.4

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/typecheck.yml +1 -1
  3. data/CHANGELOG.md +27 -0
  4. data/lib/solargraph/api_map/store.rb +9 -4
  5. data/lib/solargraph/api_map.rb +116 -39
  6. data/lib/solargraph/complex_type/type_methods.rb +1 -0
  7. data/lib/solargraph/complex_type/unique_type.rb +91 -9
  8. data/lib/solargraph/complex_type.rb +35 -6
  9. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  10. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  11. data/lib/solargraph/convention/struct_definition.rb +101 -0
  12. data/lib/solargraph/convention.rb +1 -0
  13. data/lib/solargraph/doc_map.rb +83 -23
  14. data/lib/solargraph/gem_pins.rb +2 -1
  15. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  16. data/lib/solargraph/language_server/host.rb +3 -1
  17. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -0
  18. data/lib/solargraph/location.rb +8 -0
  19. data/lib/solargraph/logging.rb +1 -0
  20. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  21. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  22. data/lib/solargraph/parser/node_methods.rb +14 -0
  23. data/lib/solargraph/parser/node_processor.rb +3 -2
  24. data/lib/solargraph/parser/parser_gem/class_methods.rb +9 -0
  25. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  26. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  27. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  28. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  29. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  30. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  31. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
  32. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  33. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  34. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  35. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  36. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  38. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  39. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
  40. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  41. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  43. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  44. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
  45. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  46. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  47. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  48. data/lib/solargraph/parser/region.rb +1 -1
  49. data/lib/solargraph/parser.rb +1 -0
  50. data/lib/solargraph/pin/base.rb +34 -5
  51. data/lib/solargraph/pin/base_variable.rb +7 -1
  52. data/lib/solargraph/pin/block.rb +2 -0
  53. data/lib/solargraph/pin/breakable.rb +9 -0
  54. data/lib/solargraph/pin/callable.rb +5 -3
  55. data/lib/solargraph/pin/closure.rb +6 -1
  56. data/lib/solargraph/pin/common.rb +5 -0
  57. data/lib/solargraph/pin/delegated_method.rb +20 -1
  58. data/lib/solargraph/pin/documenting.rb +16 -0
  59. data/lib/solargraph/pin/keyword.rb +7 -2
  60. data/lib/solargraph/pin/local_variable.rb +7 -1
  61. data/lib/solargraph/pin/method.rb +34 -27
  62. data/lib/solargraph/pin/namespace.rb +17 -9
  63. data/lib/solargraph/pin/parameter.rb +17 -5
  64. data/lib/solargraph/pin/proxy_type.rb +12 -6
  65. data/lib/solargraph/pin/reference/override.rb +10 -6
  66. data/lib/solargraph/pin/reference/require.rb +2 -2
  67. data/lib/solargraph/pin/signature.rb +4 -0
  68. data/lib/solargraph/pin/singleton.rb +1 -1
  69. data/lib/solargraph/pin/symbol.rb +3 -2
  70. data/lib/solargraph/pin/until.rb +18 -0
  71. data/lib/solargraph/pin/while.rb +18 -0
  72. data/lib/solargraph/pin.rb +4 -1
  73. data/lib/solargraph/rbs_map/conversions.rb +172 -56
  74. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  75. data/lib/solargraph/rbs_map/core_map.rb +3 -2
  76. data/lib/solargraph/shell.rb +1 -0
  77. data/lib/solargraph/source/chain/array.rb +11 -7
  78. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  79. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  80. data/lib/solargraph/source/chain/call.rb +53 -23
  81. data/lib/solargraph/source/chain/constant.rb +1 -1
  82. data/lib/solargraph/source/chain/hash.rb +4 -3
  83. data/lib/solargraph/source/chain/head.rb +1 -1
  84. data/lib/solargraph/source/chain/if.rb +1 -1
  85. data/lib/solargraph/source/chain/link.rb +2 -0
  86. data/lib/solargraph/source/chain/literal.rb +22 -2
  87. data/lib/solargraph/source/chain/or.rb +1 -1
  88. data/lib/solargraph/source/chain/z_super.rb +1 -1
  89. data/lib/solargraph/source/chain.rb +78 -48
  90. data/lib/solargraph/source/source_chainer.rb +2 -2
  91. data/lib/solargraph/source_map/clip.rb +3 -1
  92. data/lib/solargraph/source_map/mapper.rb +9 -5
  93. data/lib/solargraph/type_checker/checks.rb +4 -0
  94. data/lib/solargraph/type_checker.rb +35 -8
  95. data/lib/solargraph/version.rb +1 -1
  96. data/lib/solargraph/yard_map/mapper/to_constant.rb +4 -2
  97. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
  98. data/lib/solargraph/yard_map/mapper/to_namespace.rb +4 -2
  99. data/lib/solargraph/yard_map/mapper.rb +4 -3
  100. data/lib/solargraph/yard_map/to_method.rb +4 -2
  101. data/lib/solargraph.rb +20 -0
  102. data/rbs/fills/tuple.rbs +150 -0
  103. metadata +15 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 155e6f0e4fc44166c0d4b3c1ba6691ff1d1629d229adf8d37f6d3fe4b5fc0c77
4
- data.tar.gz: c33c4dab114c6a70b3c0dfbee6a5ccdc379cf284af3d34dae8a30473dbcdc517
3
+ metadata.gz: e00ddc5e4b665c4527099b496d2caf414a0fe5c1cdb44e71b4af16a7c5521d34
4
+ data.tar.gz: 494b003c5260150a4c66952c0e4c512374197a52b528249027100b48a3deb4c5
5
5
  SHA512:
6
- metadata.gz: 267350435fa9592fc49dc2c5b05f9f9157b576025ea4ed9abe3ebc58e4344d7412805ace58920585105fbb81eaa2bad246416c5456ce66656f22c8639054002d
7
- data.tar.gz: 7a3860337657dc869e7a96f8b95509867ece49db13e89523e09c1567490b427ed41f5fc57c87e5d8477bfe3857cbe97512bd811598adbb53f4a2f617e724c44c
6
+ metadata.gz: b56ff99aecf4a656722a8a55cdcf521b9dba1066c53e22dd344840f173dfb302fb560daba2b828af01ad0789f7defab7976511509484613df13687854b90bf77
7
+ data.tar.gz: 2cc74269454a5290c0c98e89f55942bdc36f916b622bfc37a42d4aeaf30d023f497a6be585f725b4397991a5083fdc43dedf90c05526a3739bb2c4a78266daf0
@@ -31,4 +31,4 @@ jobs:
31
31
  - name: Install gems
32
32
  run: bundle install
33
33
  - name: Typecheck self
34
- run: bundle exec solargraph typecheck --level typed
34
+ run: SOLARGRAPH_ASSERTS=on bundle exec solargraph typecheck --level typed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ ## 0.55.4 - June 27, 2025
2
+ - Flatten results of DocMap external bundle query (#981)
3
+
4
+ ## 0.55.3 - June 25, 2025
5
+ - Nil guards in flow-sensitive typing (patch release) (#980)
6
+
7
+ ## 0.55.2 - June 21, 2025
8
+ - Require external bundle (#972)
9
+
10
+ ## 0.55.1 - June 8, 2025
11
+ - Fix inline Struct definition (#962)
12
+ - Ensure DocMap requires bundler when loading gemspecs (#963)
13
+ - DelegatedMethod improvements (#953)
14
+
15
+ ## 0.55.0 - June 3, 2025
16
+ - Flow-sensitive typing - automatically downcast from is_a? calls (#856)
17
+ - Tuple enabler: infer literal types and use them for signature selection (#836)
18
+ - Signature selection improvements (#907)
19
+ - Add support for Ruby Structs (#939)
20
+ - [regression] Fix interface change breaking solargraph-rails (#940)
21
+ - [regression] Add back bundler/require support for solargraph-rails (#941)
22
+ - Add specs for initialize capabilities (#955)
23
+ - Create MethodAlias pins from YARD (#945)
24
+ - MessageWorker prioritizes synchronization (#956)
25
+ - initialize/new method pin cleanups (#949)
26
+ - Clip rebinds blocks when cursor is not part of receiver (#958)
27
+
1
28
  ## 0.54.5 - May 17, 2025
2
29
  - Repair unknown encoding errors (#936, #935)
3
30
  - Index arbitrary pinsets (#937)
@@ -66,13 +66,18 @@ module Solargraph
66
66
  end
67
67
  end
68
68
 
69
- # @param fqns [String]
69
+ # @param fq_tag [String]
70
70
  # @return [String, nil]
71
- def get_superclass fqns
72
- raise "Do not prefix fully qualified namespaces with '::' - #{fqns.inspect}" if fqns.start_with?('::')
71
+ def get_superclass fq_tag
72
+ raise "Do not prefix fully qualified tags with '::' - #{fq_tag.inspect}" if fq_tag.start_with?('::')
73
+ sub = ComplexType.parse(fq_tag)
74
+ fqns = sub.namespace
75
+ return superclass_references[fq_tag].first if superclass_references.key?(fq_tag)
73
76
  return superclass_references[fqns].first if superclass_references.key?(fqns)
74
77
  return 'Object' if fqns != 'BasicObject' && namespace_exists?(fqns)
75
78
  return 'Object' if fqns == 'Boolean'
79
+ simplified_literal_name = ComplexType.parse("#{fqns}").simplify_literals.name
80
+ return simplified_literal_name if simplified_literal_name != fqns
76
81
  nil
77
82
  end
78
83
 
@@ -112,7 +117,7 @@ module Solargraph
112
117
  # @param fqns [String]
113
118
  # @return [Enumerable<Solargraph::Pin::Base>]
114
119
  def get_class_variables(fqns)
115
- namespace_children(fqns).select{|pin| pin.is_a?(Pin::ClassVariable)}
120
+ namespace_children(fqns).select { |pin| pin.is_a?(Pin::ClassVariable)}
116
121
  end
117
122
 
118
123
  # @return [Enumerable<Solargraph::Pin::Base>]
@@ -94,7 +94,7 @@ module Solargraph
94
94
  end
95
95
  unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
96
96
  if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
97
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
97
+ @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
98
98
  @unresolved_requires = unresolved_requires
99
99
  end
100
100
  @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
@@ -264,15 +264,19 @@ module Solargraph
264
264
  # Should not be prefixed with '::'.
265
265
  # @return [String, nil] fully qualified tag
266
266
  def qualify tag, context_tag = ''
267
- return tag if ['self', nil].include?(tag)
267
+ return tag if ['Boolean', 'self', nil].include?(tag)
268
268
 
269
- context_type = ComplexType.parse(context_tag).force_rooted
269
+ context_type = ComplexType.try_parse(context_tag).force_rooted
270
270
  return unless context_type
271
271
 
272
272
  type = ComplexType.try_parse(tag)
273
273
  return unless type
274
+ return tag if type.literal?
274
275
 
275
- fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
276
+ context_type = ComplexType.try_parse(context_tag)
277
+ return unless context_type
278
+
279
+ fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
276
280
  return unless fqns
277
281
 
278
282
  fqns + type.substring
@@ -319,6 +323,11 @@ module Solargraph
319
323
  result
320
324
  end
321
325
 
326
+ # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
327
+ def visible_pins(*args, **kwargs, &blk)
328
+ Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
329
+ end
330
+
322
331
  # Get an array of class variable pins for a namespace.
323
332
  #
324
333
  # @param namespace [String] A fully qualified namespace
@@ -350,6 +359,11 @@ module Solargraph
350
359
  # @param deep [Boolean] True to include superclasses, mixins, etc.
351
360
  # @return [Array<Solargraph::Pin::Method>]
352
361
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
362
+ if rooted_tag.start_with? 'Array('
363
+ # Array() are really tuples - use our fill, as the RBS repo
364
+ # does not give us definitions for it
365
+ rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})"
366
+ end
353
367
  rooted_type = ComplexType.try_parse(rooted_tag)
354
368
  fqns = rooted_type.namespace
355
369
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -376,18 +390,36 @@ module Solargraph
376
390
  init_pin = get_method_stack(rooted_tag, 'initialize').first
377
391
  next pin unless init_pin
378
392
 
379
- type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
380
- Pin::Method.new(
393
+ type = ComplexType::SELF
394
+ new_pin = Pin::Method.new(
381
395
  name: 'new',
382
396
  scope: :class,
383
397
  location: init_pin.location,
384
- parameters: init_pin.parameters,
385
- signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
386
398
  return_type: type,
387
399
  comments: init_pin.comments,
388
- closure: init_pin.closure
389
- # @todo Hack to force TypeChecker#internal_or_core?
390
- ).tap { |pin| pin.source = :rbs }
400
+ closure: init_pin.closure,
401
+ source: init_pin.source,
402
+ type_location: init_pin.type_location,
403
+ )
404
+ new_pin.parameters = init_pin.parameters.map do |init_param|
405
+ param = init_param.clone
406
+ param.closure = new_pin
407
+ param.reset_generated!
408
+ param
409
+ end.freeze
410
+ new_pin.signatures = init_pin.signatures.map do |init_sig|
411
+ sig = init_sig.proxy(type)
412
+ sig.parameters = init_sig.parameters.map do |param|
413
+ param = param.clone
414
+ param.closure = new_pin
415
+ param.reset_generated!
416
+ param
417
+ end.freeze
418
+ sig.closure = new_pin
419
+ sig.reset_generated!
420
+ sig
421
+ end.freeze
422
+ new_pin
391
423
  end
392
424
  end
393
425
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
@@ -426,7 +458,7 @@ module Solargraph
426
458
  result = Set.new
427
459
  complex_type.each do |type|
428
460
  if type.duck_type?
429
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1])
461
+ result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
430
462
  result.merge get_methods('Object')
431
463
  else
432
464
  unless type.nil? || type.name == 'void'
@@ -643,29 +675,20 @@ module Solargraph
643
675
  if scope == :instance
644
676
  store.get_includes(fqns).reverse.each do |include_tag|
645
677
  rooted_include_tag = qualify(include_tag, rooted_tag)
646
- # Ensure the types returned by the included methods are
647
- # relative to the generics passed to the include. e.g.,
648
- # Foo<String> might include Enumerable<String>
649
- #
650
- # @todo perform the same translation in the other areas
651
- # here after adding a spec and handling things correctly
652
- # in ApiMap::Store and RbsMap::Conversions
653
- resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
654
- methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
655
- result.concat methods
678
+ result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
656
679
  end
657
- fqsc = qualify_superclass(fqns)
658
- unless fqsc.nil?
659
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
680
+ rooted_sc_tag = qualify_superclass(rooted_tag)
681
+ unless rooted_sc_tag.nil?
682
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
660
683
  end
661
684
  else
662
685
  store.get_extends(fqns).reverse.each do |em|
663
686
  fqem = qualify(em, fqns)
664
687
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
665
688
  end
666
- fqsc = qualify_superclass(fqns)
667
- unless fqsc.nil?
668
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
689
+ rooted_sc_tag = qualify_superclass(rooted_tag)
690
+ unless rooted_sc_tag.nil?
691
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true)
669
692
  end
670
693
  unless no_core || fqns.empty?
671
694
  type = get_namespace_type(fqns)
@@ -681,6 +704,41 @@ module Solargraph
681
704
  result
682
705
  end
683
706
 
707
+ # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
708
+ # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
709
+ # parameter - used to pull generics information
710
+ # @param type [ComplexType] The type which is having its
711
+ # methods supplemented from fq_reference_tag
712
+ # @param scope [Symbol] :class or :instance
713
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
714
+ # @param deep [Boolean]
715
+ # @param skip [Set<String>]
716
+ # @param no_core [Boolean] Skip core classes if true
717
+ # @return [Array<Pin::Base>]
718
+ def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
719
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
720
+
721
+ # Ensure the types returned by the methods in the referenced
722
+ # type are relative to the generic values passed in the
723
+ # reference. e.g., Foo<String> might include Enumerable<String>
724
+ #
725
+ # @todo perform the same translation in the other areas
726
+ # here after adding a spec and handling things correctly
727
+ # in ApiMap::Store and RbsMap::Conversions for each
728
+ resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type)
729
+ # @todo Can inner_get_methods be cached? Lots of lookups of base types going on.
730
+ methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core)
731
+ if namespace_pin && !resolved_reference_type.all_params.empty?
732
+ reference_pin = store.get_path_pins(resolved_reference_type.name).select { |p| p.is_a?(Pin::Namespace) }.first
733
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" }
734
+ methods = methods.map do |method_pin|
735
+ method_pin.resolve_generics(reference_pin, resolved_reference_type)
736
+ end
737
+ end
738
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" }
739
+ methods
740
+ end
741
+
684
742
  # @param fqns [String]
685
743
  # @param visibility [Array<Symbol>]
686
744
  # @param skip [Set<String>]
@@ -716,15 +774,19 @@ module Solargraph
716
774
  qualify namespace, context.split('::')[0..-2].join('::')
717
775
  end
718
776
 
719
- # @param fqsub [String]
777
+ # @param fq_tag [String]
720
778
  # @return [String, nil]
721
- def qualify_superclass fqsub
722
- sup = store.get_superclass(fqsub)
723
- return nil if sup.nil?
724
- parts = fqsub.split('::')
779
+ def qualify_superclass fq_sub_tag
780
+ fq_sub_type = ComplexType.try_parse(fq_sub_tag)
781
+ fq_sub_ns = fq_sub_type.name
782
+ sup_tag = store.get_superclass(fq_sub_tag)
783
+ sup_type = ComplexType.try_parse(sup_tag)
784
+ sup_ns = sup_type.name
785
+ return nil if sup_tag.nil?
786
+ parts = fq_sub_ns.split('::')
725
787
  last = parts.pop
726
- parts.pop if last == sup
727
- qualify(sup, parts.join('::'))
788
+ parts.pop if last == sup_ns
789
+ qualify(sup_tag, parts.join('::'))
728
790
  end
729
791
 
730
792
  # @param name [String] Namespace to fully qualify
@@ -814,23 +876,38 @@ module Solargraph
814
876
  return pin unless pin.is_a?(Pin::MethodAlias)
815
877
  return nil if @method_alias_stack.include?(pin.path)
816
878
  @method_alias_stack.push pin.path
817
- origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
879
+ origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope, preserve_generics: true).first
818
880
  @method_alias_stack.pop
819
881
  return nil if origin.nil?
820
882
  args = {
821
883
  location: pin.location,
884
+ type_location: origin.type_location,
822
885
  closure: pin.closure,
823
886
  name: pin.name,
824
887
  comments: origin.comments,
825
888
  scope: origin.scope,
826
889
  # context: pin.context,
827
890
  visibility: origin.visibility,
828
- signatures: origin.signatures,
891
+ signatures: origin.signatures.map(&:clone).freeze,
829
892
  attribute: origin.attribute?,
830
- generics: origin.generics,
893
+ generics: origin.generics.clone,
831
894
  return_type: origin.return_type,
895
+ source: :resolve_method_alias
832
896
  }
833
- Pin::Method.new **args
897
+ out = Pin::Method.new **args
898
+ out.signatures.each do |sig|
899
+ sig.parameters = sig.parameters.map(&:clone).freeze
900
+ sig.source = :resolve_method_alias
901
+ sig.parameters.each do |param|
902
+ param.closure = out
903
+ param.source = :resolve_method_alias
904
+ param.reset_generated!
905
+ end
906
+ sig.closure = out
907
+ sig.reset_generated!
908
+ end
909
+ logger.debug { "ApiMap#resolve_method_alias(pin=#{pin}) - returning #{out} from #{origin}" }
910
+ out
834
911
  end
835
912
 
836
913
  include Logging
@@ -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,
@@ -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,7 +48,7 @@ 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
@@ -73,19 +73,63 @@ module Solargraph
73
73
  end
74
74
  raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
75
75
  @name = name
76
- @key_types = key_types
77
- @subtypes = subtypes
76
+ @parameters_type = parameters_type
77
+ if implicit_union?
78
+ @key_types = key_types.uniq
79
+ @subtypes = subtypes.uniq
80
+ else
81
+ @key_types = key_types
82
+ @subtypes = subtypes
83
+ end
78
84
  @rooted = rooted
79
85
  @all_params = []
80
- @all_params.concat key_types
81
- @all_params.concat subtypes
82
- @parameters_type = parameters_type
86
+ @all_params.concat @key_types
87
+ @all_params.concat @subtypes
88
+ end
89
+
90
+ def implicit_union?
91
+ # @todo use api_map to establish number of generics in type;
92
+ # if only one is allowed but multiple are passed in, treat
93
+ # those as implicit unions
94
+ ['Hash', 'Array', 'Set', '_ToAry', 'Enumerable', '_Each'].include?(name) && parameters_type != :fixed
83
95
  end
84
96
 
85
97
  def to_s
86
98
  tag
87
99
  end
88
100
 
101
+ def simplify_literals
102
+ transform do |t|
103
+ next t unless t.literal?
104
+ t.recreate(new_name: t.non_literal_name)
105
+ end
106
+ end
107
+
108
+ def literal?
109
+ non_literal_name != name
110
+ end
111
+
112
+ def non_literal_name
113
+ @non_literal_name ||= determine_non_literal_name
114
+ end
115
+
116
+ def determine_non_literal_name
117
+ # https://github.com/ruby/rbs/blob/master/docs/syntax.md
118
+ #
119
+ # _literal_ ::= _string-literal_
120
+ # | _symbol-literal_
121
+ # | _integer-literal_
122
+ # | `true`
123
+ # | `false`
124
+ return name if name.empty?
125
+ return 'NilClass' if name == 'nil'
126
+ return 'Boolean' if ['true', 'false'].include?(name)
127
+ return 'Symbol' if name[0] == ':'
128
+ return 'String' if ['"', "'"].include?(name[0])
129
+ return 'Integer' if name.match?(/^-?\d+$/)
130
+ name
131
+ end
132
+
89
133
  def eql?(other)
90
134
  self.class == other.class &&
91
135
  @name == other.name &&
@@ -113,6 +157,8 @@ module Solargraph
113
157
  def rbs_name
114
158
  if name == 'undefined'
115
159
  'untyped'
160
+ elsif literal?
161
+ name
116
162
  else
117
163
  rooted_name
118
164
  end
@@ -183,6 +229,23 @@ module Solargraph
183
229
  name == GENERIC_TAG_NAME || all_params.any?(&:generic?)
184
230
  end
185
231
 
232
+ # @param api_map [ApiMap] The ApiMap that performs qualification
233
+ # @param atype [ComplexType] type which may be assigned to this type
234
+ def can_assign?(api_map, atype)
235
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect})" }
236
+ downcasted_atype = atype.downcast_to_literal_if_possible
237
+ out = downcasted_atype.all? do |autype|
238
+ autype.name == name || api_map.super_and_sub?(name, autype.name)
239
+ end
240
+ logger.debug { "UniqueType#can_assign?(self=#{rooted_tags.inspect}, atype=#{atype.rooted_tags.inspect}) => #{out}" }
241
+ out
242
+ end
243
+
244
+ # @return [UniqueType]
245
+ def downcast_to_literal_if_possible
246
+ SINGLE_SUBTYPE.fetch(rooted_tag, self)
247
+ end
248
+
186
249
  # @param generics_to_resolve [Enumerable<String>]
187
250
  # @param context_type [UniqueType, nil]
188
251
  # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
@@ -243,7 +306,8 @@ module Solargraph
243
306
 
244
307
  transform(name) do |t|
245
308
  if t.name == GENERIC_TAG_NAME
246
- idx = definitions.generics.index(t.subtypes.first&.name)
309
+ generic_name = t.subtypes.first&.name
310
+ idx = definitions.generics.index(generic_name)
247
311
  next t if idx.nil?
248
312
  if context_type.parameters_type == :hash
249
313
  if idx == 0
@@ -253,8 +317,14 @@ module Solargraph
253
317
  else
254
318
  next ComplexType::UNDEFINED
255
319
  end
320
+ elsif context_type.all?(&:implicit_union?)
321
+ if idx == 0 && !context_type.all_params.empty?
322
+ ComplexType.new(context_type.all_params)
323
+ else
324
+ ComplexType::UNDEFINED
325
+ end
256
326
  else
257
- context_type.all_params[idx] || ComplexType::UNDEFINED
327
+ context_type.all_params[idx] || definitions.generic_defaults[generic_name] || ComplexType::UNDEFINED
258
328
  end
259
329
  else
260
330
  t
@@ -381,6 +451,18 @@ module Solargraph
381
451
 
382
452
  UNDEFINED = UniqueType.new('undefined', rooted: false)
383
453
  BOOLEAN = UniqueType.new('Boolean', rooted: true)
454
+ TRUE = UniqueType.new('true', rooted: true)
455
+ FALSE = UniqueType.new('false', rooted: true)
456
+ NIL = UniqueType.new('nil', rooted: true)
457
+ # @type [Hash{String => UniqueType}]
458
+ SINGLE_SUBTYPE = {
459
+ '::TrueClass' => UniqueType::TRUE,
460
+ '::FalseClass' => UniqueType::FALSE,
461
+ '::NilClass' => UniqueType::NIL
462
+ }.freeze
463
+
464
+
465
+ include Logging
384
466
  end
385
467
  end
386
468
  end
@@ -16,7 +16,13 @@ module Solargraph
16
16
  def initialize types = [UniqueType::UNDEFINED]
17
17
  # @todo @items here should not need an annotation
18
18
  # @type [Array<UniqueType>]
19
- @items = types.flat_map(&:items).uniq(&:to_s)
19
+ items = types.flat_map(&:items).uniq(&:to_s)
20
+ if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
21
+ items.delete_if { |i| i.name == 'false' || i.name == 'true' }
22
+ items.unshift(ComplexType::BOOLEAN)
23
+ end
24
+ items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
25
+ @items = items
20
26
  end
21
27
 
22
28
  # @sg-ignore Fix "Not enough arguments to Module#protected"
@@ -70,7 +76,7 @@ module Solargraph
70
76
  end
71
77
 
72
78
  # @yieldparam [UniqueType]
73
- # @return [Array]
79
+ # @return [Array<UniqueType>]
74
80
  def map &block
75
81
  @items.map &block
76
82
  end
@@ -93,6 +99,12 @@ module Solargraph
93
99
  end
94
100
  end
95
101
 
102
+ # @param atype [ComplexType] type which may be assigned to this type
103
+ # @param api_map [ApiMap] The ApiMap that performs qualification
104
+ def can_assign?(api_map, atype)
105
+ any? { |ut| ut.can_assign?(api_map, atype) }
106
+ end
107
+
96
108
  # @return [Integer]
97
109
  def length
98
110
  @items.length
@@ -103,10 +115,6 @@ module Solargraph
103
115
  @items
104
116
  end
105
117
 
106
- def tags
107
- @items.map(&:tag).join(', ')
108
- end
109
-
110
118
  # @param index [Integer]
111
119
  # @return [UniqueType]
112
120
  def [](index)
@@ -147,6 +155,23 @@ module Solargraph
147
155
  map(&:tag).join(', ')
148
156
  end
149
157
 
158
+ def tags
159
+ map(&:tag).join(', ')
160
+ end
161
+
162
+ def simple_tags
163
+ simplify_literals.tags
164
+ end
165
+
166
+ def literal?
167
+ @items.any?(&:literal?)
168
+ end
169
+
170
+ # @return [ComplexType]
171
+ def downcast_to_literal_if_possible
172
+ ComplexType.new(items.map(&:downcast_to_literal_if_possible))
173
+ end
174
+
150
175
  def desc
151
176
  rooted_tags
152
177
  end
@@ -175,6 +200,10 @@ module Solargraph
175
200
  any?(&:generic?)
176
201
  end
177
202
 
203
+ def simplify_literals
204
+ ComplexType.new(map(&:simplify_literals))
205
+ end
206
+
178
207
  # @param new_name [String, nil]
179
208
  # @yieldparam t [UniqueType]
180
209
  # @yieldreturn [UniqueType]
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Convention
5
+ module StructDefinition
6
+ # A node wrapper for a Struct definition via const assignment.
7
+ # @example
8
+ # MyStruct = Struct.new(:bar, :baz) do
9
+ # def foo
10
+ # end
11
+ # end
12
+ class StructAssignmentNode < StructDefintionNode
13
+ class << self
14
+ # @example
15
+ # s(:casgn, nil, :Foo,
16
+ # s(:block,
17
+ # s(:send,
18
+ # s(:const, nil, :Struct), :new,
19
+ # s(:sym, :bar),
20
+ # s(:sym, :baz)),
21
+ # s(:args),
22
+ # s(:def, :foo,
23
+ # s(:args),
24
+ # s(:send, nil, :bar))))
25
+ def valid?(node)
26
+ return false unless node&.type == :casgn
27
+ return false if node.children[2].nil?
28
+
29
+ struct_node = if node.children[2].type == :block
30
+ node.children[2].children[0]
31
+ else
32
+ node.children[2]
33
+ end
34
+
35
+ struct_definition_node?(struct_node)
36
+ end
37
+ end
38
+
39
+ def class_name
40
+ if node.children[0]
41
+ Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
42
+ else
43
+ node.children[1].to_s
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # @return [Parser::AST::Node]
50
+ def struct_node
51
+ if node.children[2].type == :block
52
+ node.children[2].children[0]
53
+ else
54
+ node.children[2]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end