solargraph 0.53.2 → 0.54.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/lib/solargraph/api_map/cache.rb +2 -12
  4. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  5. data/lib/solargraph/api_map/store.rb +11 -6
  6. data/lib/solargraph/api_map.rb +80 -38
  7. data/lib/solargraph/complex_type/type_methods.rb +62 -30
  8. data/lib/solargraph/complex_type/unique_type.rb +149 -75
  9. data/lib/solargraph/complex_type.rb +41 -25
  10. data/lib/solargraph/doc_map.rb +19 -3
  11. data/lib/solargraph/gem_pins.rb +10 -2
  12. data/lib/solargraph/language_server/host/dispatch.rb +8 -1
  13. data/lib/solargraph/language_server/host/sources.rb +1 -61
  14. data/lib/solargraph/language_server/host.rb +39 -68
  15. data/lib/solargraph/language_server/message/base.rb +1 -1
  16. data/lib/solargraph/language_server/message/initialize.rb +14 -0
  17. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
  18. data/lib/solargraph/language_server/progress.rb +118 -0
  19. data/lib/solargraph/language_server.rb +1 -0
  20. data/lib/solargraph/library.rb +149 -97
  21. data/lib/solargraph/page.rb +6 -0
  22. data/lib/solargraph/parser/node_processor/base.rb +3 -2
  23. data/lib/solargraph/parser/node_processor.rb +1 -0
  24. data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
  25. data/lib/solargraph/parser/parser_gem/node_methods.rb +0 -4
  26. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -19
  27. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
  28. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +5 -3
  29. data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
  30. data/lib/solargraph/pin/base_variable.rb +35 -6
  31. data/lib/solargraph/pin/block.rb +70 -32
  32. data/lib/solargraph/pin/delegated_method.rb +5 -1
  33. data/lib/solargraph/pin/documenting.rb +2 -0
  34. data/lib/solargraph/pin/method.rb +4 -2
  35. data/lib/solargraph/pin/parameter.rb +5 -28
  36. data/lib/solargraph/rbs_map/conversions.rb +13 -44
  37. data/lib/solargraph/rbs_map/core_fills.rb +12 -6
  38. data/lib/solargraph/rbs_map/core_map.rb +2 -13
  39. data/lib/solargraph/rbs_map.rb +17 -7
  40. data/lib/solargraph/shell.rb +18 -13
  41. data/lib/solargraph/source/chain.rb +20 -0
  42. data/lib/solargraph/source/updater.rb +1 -0
  43. data/lib/solargraph/source.rb +0 -44
  44. data/lib/solargraph/source_map/clip.rb +3 -2
  45. data/lib/solargraph/source_map/mapper.rb +3 -2
  46. data/lib/solargraph/source_map.rb +8 -6
  47. data/lib/solargraph/type_checker.rb +57 -13
  48. data/lib/solargraph/version.rb +1 -1
  49. data/lib/solargraph/workspace/config.rb +2 -1
  50. data/lib/solargraph/workspace.rb +13 -0
  51. data/lib/solargraph/yard_map/mapper/to_method.rb +5 -2
  52. metadata +4 -4
  53. data/lib/solargraph/language_server/host/cataloger.rb +0 -57
  54. data/lib/solargraph/rbs_map/core_signs.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd82572e0c8ca2d5e7477c4bbcd58fa66b318550a23b6944d8749f67913560e3
4
- data.tar.gz: 79ba0becff2e00fbcc7139ec540e91db5dec195cf5ae557cdcf599026b11b964
3
+ metadata.gz: 39ad27afe3afb59299f8701f9a7ea3f25a9f95379ef68e53ac0e22eb60f50e21
4
+ data.tar.gz: 1f01a9d5cdf4aec5b50c245a25b1f7d83e5d6762e5b8f65bc9ce3555654e21ed
5
5
  SHA512:
6
- metadata.gz: 743c84f70a8d49f253f4fdf3e8b43a3061ad7cd6eec68fbbf592505d63d56666ae5654ca2664750a7963d43f5b131ff1c7bc52660266786d345c9a19b86856fa
7
- data.tar.gz: 8accd71eec8ce6f5ccf3aa5efd692a3ff71ea35eabf125446fe533049df80bd84fb33bf73d0cdadd7914598d485ed1f013c7cc898575b1a34238c90e92d8318e
6
+ metadata.gz: 788f17e92a54e782ea1dcf03cb44245df823ba33b4fe866d1afa5d06c7171b40a4e1f93c97c8b26c13b58be7478b9b07cd75d6735da65ecef21ab6a4a61dbb0e
7
+ data.tar.gz: 742f43fcdce4a592a80ebe8e3acbd020f39f569cf323621a527e626ae8c6154f7dedb32739b8b62f38e98997ed414ec30471160332ecfb4a2fe5d5052e871333
data/CHANGELOG.md CHANGED
@@ -1,3 +1,39 @@
1
+ ## 0.54.0 - April 14, 2025
2
+ - Add support for simple block argument destructuring (#821)
3
+ - Benchmark the typecheck command (#852)
4
+ - Send Gem Caching Progress Notifications to LSP Clients (#855)
5
+ - [breaking] Fix more complex_type_spec.rb cases (#813)
6
+ - Mass assignment support - e.g., a, b = ['1', '2'] (#843)
7
+ - Memoize result of Chain#infer (#857)
8
+ - Ignore malformed mixins and overloads (#862)
9
+ - Drop Parser::ParserGem::ClassMethods#returns_from_node (#866)
10
+ - Refactor TypeChecker#argument_problems_for for type safety (#867)
11
+ - Specify more type behavior for variable reassignment (#863)
12
+ - One-step source synchronization (#871)
13
+ - Show cache progress in shell commands (#874)
14
+ - Fix miscellaneous scan errors (#875)
15
+ - Synchronous libraries (#876)
16
+ - Fix parsing of Set#classify method signature from RBS (#878)
17
+ - Sync Library#diagnose (#882)
18
+ - Doesn't false-alarm over splatted non-final args in typechecking (#883)
19
+ - Remove accidental inclusion of Module's methods in objects (#884)
20
+ - Remove another splat-related false alarm in strict typechecking (#889)
21
+ - Change require path `warn` to `debug` (#897)
22
+
23
+ ## 0.53.4 - March 30, 2025
24
+ - [regression] Restore 'Unresolved call' typecheck for stdlib objects (#849)
25
+ - Lazy dynamic rebinding (#851)
26
+ - Restore fill for Class#allocate (#848)
27
+ - [regression] Ensure YardMap gems have return type for Class<T>.new (#850)
28
+ - Create implicit .new pins in namespace method queries (#853)
29
+
30
+ ## 0.53.3 - March 29, 2025
31
+ - Remove redundant core fills (#824, #841)
32
+ - Resolve self type in variable assignments (#839)
33
+ - Eliminate splat-related false-alarms in strict typechecking (#840)
34
+ - Dynamic block binding with yieldreceiver (#842)
35
+ - Resolve generics by descending through context type (#847)
36
+
1
37
  ## 0.53.2 - March 27, 2025
2
38
  - Fix a self-type-related false-positive in strict typechecking (#834)
3
39
  - DocMap fetches gem dependencies (#835)
@@ -78,20 +78,10 @@ module Solargraph
78
78
  @receiver_definitions[path] = pin
79
79
  end
80
80
 
81
- # @param cursor [Source::Cursor]
82
- # @return [SourceMap::Clip, nil]
83
- def get_clip(cursor)
84
- @clips["#{cursor.filename}|#{cursor.range.inspect}|#{cursor.node&.to_sexp}"]
85
- end
86
-
87
- # @param cursor [Source::Cursor]
88
- # @param clip [SourceMap::Clip]
89
- def set_clip(cursor, clip)
90
- @clips["#{cursor.filename}|#{cursor.range.inspect}|#{cursor.node&.to_sexp}"] = clip
91
- end
92
-
93
81
  # @return [void]
94
82
  def clear
83
+ return if empty?
84
+
95
85
  all_caches.each(&:clear)
96
86
  end
97
87
 
@@ -6,10 +6,13 @@ module Solargraph
6
6
 
7
7
  # Get the YARD CodeObject at the specified path.
8
8
  #
9
+ # @generic T
9
10
  # @param path [String]
10
- # @return [YARD::CodeObjects::Base]
11
- def code_object_at path
12
- code_object_map[path]
11
+ # @param klass [Class<generic<T>>]
12
+ # @return [generic<T>, nil]
13
+ def code_object_at path, klass = YARD::CodeObjects::Base
14
+ obj = code_object_map[path]
15
+ obj if obj&.is_a?(klass)
13
16
  end
14
17
 
15
18
  # @return [Array<String>]
@@ -41,11 +44,13 @@ module Solargraph
41
44
  end
42
45
  code_object_map[pin.path].docstring = pin.docstring
43
46
  store.get_includes(pin.path).each do |ref|
44
- code_object_map[pin.path].instance_mixins.push code_object_map[ref] unless code_object_map[ref].nil? or code_object_map[pin.path].nil?
47
+ include_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject)
48
+ include_object.instance_mixins.push code_object_map[ref] unless include_object.nil? or include_object.nil?
45
49
  end
46
50
  store.get_extends(pin.path).each do |ref|
47
- code_object_map[pin.path].instance_mixins.push code_object_map[ref] unless code_object_map[ref].nil? or code_object_map[pin.path].nil?
48
- code_object_map[pin.path].class_mixins.push code_object_map[ref] unless code_object_map[ref].nil? or code_object_map[pin.path].nil?
51
+ extend_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject)
52
+ extend_object.instance_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil?
53
+ extend_object.class_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil?
49
54
  end
50
55
  end
51
56
  store.method_pins.each do |pin|
@@ -53,13 +58,15 @@ module Solargraph
53
58
  code_object_map[pin.path] ||= pin.code_object
54
59
  next
55
60
  end
56
- code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new(code_object_at(pin.namespace), pin.name, pin.scope) { |obj|
61
+
62
+ code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new(code_object_at(pin.namespace, YARD::CodeObjects::NamespaceObject), pin.name, pin.scope) { |obj|
57
63
  next if pin.location.nil? || pin.location.filename.nil?
58
64
  obj.add_file pin.location.filename, pin.location.range.start.line
59
65
  }
60
- code_object_map[pin.path].docstring = pin.docstring
61
- code_object_map[pin.path].visibility = pin.visibility || :public
62
- code_object_map[pin.path].parameters = pin.parameters.map do |p|
66
+ method_object = code_object_at(pin.path, YARD::CodeObjects::MethodObject)
67
+ method_object.docstring = pin.docstring
68
+ method_object.visibility = pin.visibility || :public
69
+ method_object.parameters = pin.parameters.map do |p|
63
70
  [p.name, p.asgn_code]
64
71
  end
65
72
  end
@@ -65,6 +65,10 @@ module Solargraph
65
65
  path_pin_hash[path] || []
66
66
  end
67
67
 
68
+ def cacheable_pins
69
+ @cacheable_pins ||= pins_by_class(Pin::Namespace) + pins_by_class(Pin::Constant) + pins_by_class(Pin::Method) + pins_by_class(Pin::Reference)
70
+ end
71
+
68
72
  # @param fqns [String]
69
73
  # @param scope [Symbol] :class or :instance
70
74
  # @return [Enumerable<Solargraph::Pin::Base>]
@@ -96,7 +100,7 @@ module Solargraph
96
100
  @namespaces ||= Set.new
97
101
  end
98
102
 
99
- # @return [Enumerable<Solargraph::Pin::Base>]
103
+ # @return [Array<Solargraph::Pin::Base>]
100
104
  def namespace_pins
101
105
  pins_by_class(Solargraph::Pin::Namespace)
102
106
  end
@@ -140,8 +144,9 @@ module Solargraph
140
144
  to_s
141
145
  end
142
146
 
143
- # @param klass [Class]
144
- # @return [Enumerable<Solargraph::Pin::Base>]
147
+ # @generic T
148
+ # @param klass [Class<T>]
149
+ # @return [Array<T>]
145
150
  def pins_by_class klass
146
151
  # @type [Set<Solargraph::Pin::Base>]
147
152
  s = Set.new
@@ -165,7 +170,7 @@ module Solargraph
165
170
 
166
171
  private
167
172
 
168
- # @return [Hash{Array(String, String) => Array<Pin::Namespace>}]
173
+ # @return [Hash{::Array(String, String) => ::Array<Pin::Namespace>}]
169
174
  def fqns_pins_map
170
175
  @fqns_pins_map ||= Hash.new do |h, (base, name)|
171
176
  value = namespace_children(base).select { |pin| pin.name == name && pin.is_a?(Pin::Namespace) }
@@ -178,7 +183,7 @@ module Solargraph
178
183
  pins_by_class(Pin::Symbol)
179
184
  end
180
185
 
181
- # @return [Hash{String => Enumerable<String>}]
186
+ # @return [Hash{String => Array<String>}]
182
187
  def superclass_references
183
188
  @superclass_references ||= {}
184
189
  end
@@ -243,7 +248,7 @@ module Solargraph
243
248
  def index
244
249
  set = pins.to_set
245
250
  @pin_class_hash = set.classify(&:class).transform_values(&:to_a)
246
- # @type [Hash{Class => Enumerable<Solargraph::Pin::Base>}]
251
+ # @type [Hash{Class => ::Array<Solargraph::Pin::Base>}]
247
252
  @pin_select_cache = {}
248
253
  @namespace_map = set.classify(&:namespace)
249
254
  @path_pin_hash = set.classify(&:path)
@@ -29,6 +29,25 @@ module Solargraph
29
29
  index pins
30
30
  end
31
31
 
32
+ #
33
+ # This is a mutable object, which is cached in the Chain class -
34
+ # if you add any fields which change the results of calls (not
35
+ # just caches), please also change `equality_fields` below.
36
+ #
37
+
38
+ def eql?(other)
39
+ self.class == other.class &&
40
+ equality_fields == other.equality_fields
41
+ end
42
+
43
+ def ==(other)
44
+ self.eql?(other)
45
+ end
46
+
47
+ def hash
48
+ equality_fields.hash
49
+ end
50
+
32
51
  # @param pins [Array<Pin::Base>]
33
52
  # @return [self]
34
53
  def index pins
@@ -56,23 +75,31 @@ module Solargraph
56
75
  # @param bench [Bench]
57
76
  # @return [self]
58
77
  def catalog bench
59
- implicit.clear
60
- @cache.clear
78
+ old_api_hash = @source_map_hash&.values&.map(&:api_hash)
79
+ need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
61
80
  @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
62
- pins = bench.source_maps.map(&:pins).flatten
81
+ pins = bench.source_maps.flat_map(&:pins).flatten
82
+ implicit.clear
63
83
  source_map_hash.each_value do |map|
64
84
  implicit.merge map.environ
65
85
  end
66
- unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).uniq
67
- @doc_map = DocMap.new(unresolved_requires, []) # @todo Implement gem preferences
86
+ 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
91
+ end
68
92
  @store = Store.new(@@core_map.pins + @doc_map.pins + implicit.pins + pins)
69
- @unresolved_requires = @doc_map.unresolved_requires
93
+ @cache.clear if need_to_uncache
94
+
70
95
  @missing_docs = [] # @todo Implement missing docs
71
- @rebindable_method_names = nil
72
- store.block_pins.each { |blk| blk.rebind(self) }
73
96
  self
74
97
  end
75
98
 
99
+ protected def equality_fields
100
+ [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
101
+ end
102
+
76
103
  # @return [::Array<Gem::Specification>]
77
104
  def uncached_gemspecs
78
105
  @doc_map&.uncached_gemspecs || []
@@ -135,14 +162,19 @@ module Solargraph
135
162
  # Create an ApiMap with a workspace in the specified directory and cache
136
163
  # any missing gems.
137
164
  #
165
+ #
166
+ # @todo IO::NULL is incorrectly inferred to be a String.
167
+ # @sg-ignore
168
+ #
138
169
  # @param directory [String]
170
+ # @param out [IO] The output stream for messages
139
171
  # @return [ApiMap]
140
- def self.load_with_cache directory
172
+ def self.load_with_cache directory, out = IO::NULL
141
173
  api_map = load(directory)
142
174
  return api_map if api_map.uncached_gemspecs.empty?
143
175
 
144
176
  api_map.uncached_gemspecs.each do |gemspec|
145
- Solargraph.logger.info "Caching #{gemspec.name} #{gemspec.version}..."
177
+ out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
146
178
  pins = GemPins.build(gemspec)
147
179
  Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
148
180
  end
@@ -154,17 +186,6 @@ module Solargraph
154
186
  store.pins
155
187
  end
156
188
 
157
- # @return [Set<String>]
158
- def rebindable_method_names
159
- @rebindable_method_names ||= begin
160
- result = ['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec', 'define_method'].to_set
161
- source_maps.each do |map|
162
- result.merge map.rebindable_method_names
163
- end
164
- result
165
- end
166
- end
167
-
168
189
  # An array of pins based on Ruby keywords (`if`, `end`, etc.).
169
190
  #
170
191
  # @return [Enumerable<Solargraph::Pin::Keyword>]
@@ -234,10 +255,15 @@ module Solargraph
234
255
  # @return [String, nil] fully qualified tag
235
256
  def qualify tag, context_tag = ''
236
257
  return tag if ['self', nil].include?(tag)
237
- context_type = ComplexType.parse(context_tag)
238
- type = ComplexType.parse(tag)
258
+ context_type = ComplexType.try_parse(context_tag)
259
+ return unless context_type
260
+
261
+ type = ComplexType.try_parse(tag)
262
+ return unless type
263
+
239
264
  fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
240
- return nil if fqns.nil?
265
+ return unless fqns
266
+
241
267
  fqns + type.substring
242
268
  end
243
269
 
@@ -324,8 +350,28 @@ module Solargraph
324
350
  result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
325
351
  else
326
352
  result.concat inner_get_methods(rooted_tag, scope, visibility, deep, skip)
353
+ unless %w[Class Class<Class>].include?(rooted_tag)
354
+ result.map! do |pin|
355
+ next pin unless pin.path == 'Class#new'
356
+ init_pin = get_method_stack(rooted_tag, 'initialize').first
357
+ next pin unless init_pin
358
+
359
+ type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
360
+ Pin::Method.new(
361
+ name: 'new',
362
+ scope: :class,
363
+ location: init_pin.location,
364
+ parameters: init_pin.parameters,
365
+ signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
366
+ return_type: type,
367
+ comments: init_pin.comments,
368
+ closure: init_pin.closure
369
+ # @todo Hack to force TypeChecker#internal_or_core?
370
+ ).tap { |pin| pin.source = :rbs }
371
+ end
372
+ end
327
373
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
328
- result.concat inner_get_methods('Module', scope, visibility, deep, skip)
374
+ result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
329
375
  end
330
376
  resolved = resolve_method_aliases(result, visibility)
331
377
  cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
@@ -459,9 +505,6 @@ module Solargraph
459
505
  def clip cursor
460
506
  raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
461
507
 
462
- # @todo Clip caches are disabled pending resolution of a stale cache bug
463
- # cache.get_clip(cursor) ||
464
- # SourceMap::Clip.new(self, cursor).tap { |clip| cache.set_clip(cursor, clip) }
465
508
  SourceMap::Clip.new(self, cursor)
466
509
  end
467
510
 
@@ -566,8 +609,8 @@ module Solargraph
566
609
  # namespaces; resolving the generics in the method pins is this
567
610
  # class' responsibility
568
611
  raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
569
- namespace_pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
570
- methods = if rooted_tag != fqns
612
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
613
+ methods = if namespace_pin && rooted_tag != fqns
571
614
  methods = raw_methods.map do |method_pin|
572
615
  method_pin.resolve_generics(namespace_pin, rooted_type)
573
616
  end
@@ -737,23 +780,22 @@ module Solargraph
737
780
  # @param visibility [Enumerable<Symbol>]
738
781
  # @return [Array<Pin::Base>]
739
782
  def resolve_method_aliases pins, visibility = [:public, :private, :protected]
740
- result = []
741
- pins.each do |pin|
783
+ pins.map do |pin|
742
784
  resolved = resolve_method_alias(pin)
743
- next if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
744
- result.push resolved
745
- end
746
- result
785
+ next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
786
+ resolved
787
+ end.compact
747
788
  end
748
789
 
749
790
  # @param pin [Pin::MethodAlias, Pin::Base]
750
791
  # @return [Pin::Method]
751
792
  def resolve_method_alias pin
752
- return pin if !pin.is_a?(Pin::MethodAlias) || @method_alias_stack.include?(pin.path)
793
+ return pin unless pin.is_a?(Pin::MethodAlias)
794
+ return nil if @method_alias_stack.include?(pin.path)
753
795
  @method_alias_stack.push pin.path
754
796
  origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
755
797
  @method_alias_stack.pop
756
- return pin if origin.nil?
798
+ return nil if origin.nil?
757
799
  args = {
758
800
  location: pin.location,
759
801
  closure: pin.closure,
@@ -22,14 +22,18 @@ module Solargraph
22
22
  # @return [String]
23
23
  attr_reader :name
24
24
 
25
- # @return [String]
26
- attr_reader :substring
25
+ # @return [Array<ComplexType>]
26
+ attr_reader :subtypes
27
27
 
28
28
  # @return [String]
29
- attr_reader :tag
29
+ def tag
30
+ @tag ||= "#{name}#{substring}"
31
+ end
30
32
 
31
- # @return [Array<ComplexType>]
32
- attr_reader :subtypes
33
+ # @return [String]
34
+ def rooted_tag
35
+ @rooted_tag ||= rooted_name + rooted_substring
36
+ end
33
37
 
34
38
  # @return [Boolean]
35
39
  def duck_type?
@@ -46,6 +50,10 @@ module Solargraph
46
50
  !substring.empty?
47
51
  end
48
52
 
53
+ def tuple?
54
+ @tuple_type ||= (name == 'Tuple') || (name == 'Array' && subtypes.length >= 1 && fixed_parameters?)
55
+ end
56
+
49
57
  def void?
50
58
  name == 'void'
51
59
  end
@@ -74,19 +82,28 @@ module Solargraph
74
82
  end
75
83
  end
76
84
 
85
+ # @return [Symbol, nil]
86
+ attr_reader :parameters_type
87
+
88
+ PARAMETERS_TYPE_BY_STARTING_TAG = {
89
+ '{' => :hash,
90
+ '(' => :fixed,
91
+ '<' => :list
92
+ }.freeze
93
+
77
94
  # @return [Boolean]
78
95
  def list_parameters?
79
- substring.start_with?('<')
96
+ parameters_type == :list
80
97
  end
81
98
 
82
99
  # @return [Boolean]
83
100
  def fixed_parameters?
84
- substring.start_with?('(')
101
+ parameters_type == :fixed
85
102
  end
86
103
 
87
104
  # @return [Boolean]
88
105
  def hash_parameters?
89
- substring.start_with?('{')
106
+ parameters_type == :hash
90
107
  end
91
108
 
92
109
  # @return [Array<ComplexType>]
@@ -121,6 +138,33 @@ module Solargraph
121
138
  "::#{name}"
122
139
  end
123
140
 
141
+ # @return [String]
142
+ def substring
143
+ @substring ||= generate_substring_from(&:tags)
144
+ end
145
+
146
+ # @return [String]
147
+ def rooted_substring
148
+ @rooted_substring = generate_substring_from(&:rooted_tags)
149
+ end
150
+
151
+ # @return [String]
152
+ def generate_substring_from(&to_str)
153
+ key_types_str = key_types.map(&to_str).join(', ')
154
+ subtypes_str = subtypes.map(&to_str).join(', ')
155
+ if key_types.none?(&:defined?) && subtypes.none?(&:defined?)
156
+ ''
157
+ elsif key_types.empty? && subtypes.empty?
158
+ ''
159
+ elsif hash_parameters?
160
+ "{#{key_types_str} => #{subtypes_str}}"
161
+ elsif fixed_parameters?
162
+ "(#{subtypes_str})"
163
+ else
164
+ "<#{subtypes_str}>"
165
+ end
166
+ end
167
+
124
168
  # @return [::Symbol] :class or :instance
125
169
  def scope
126
170
  @scope ||= :instance if duck_type? || nil_type?
@@ -143,28 +187,16 @@ module Solargraph
143
187
  # @param context [String] The namespace from which to resolve names
144
188
  # @return [self, ComplexType, UniqueType] The generated ComplexType
145
189
  def qualify api_map, context = ''
146
- return self if name == GENERIC_TAG_NAME
147
- return ComplexType.new([self]) if duck_type? || void? || undefined?
148
- recon = (rooted? ? '' : context)
149
- fqns = api_map.qualify(name, recon)
150
- if fqns.nil?
151
- return UniqueType::BOOLEAN if tag == 'Boolean'
152
- return UniqueType::UNDEFINED
153
- end
154
- fqns = "::#{fqns}" # Ensure the resulting complex type is rooted
155
- all_ltypes = key_types.map { |t| t.qualify api_map, context }.uniq
156
- all_rtypes = value_types.map { |t| t.qualify api_map, context }
157
- if list_parameters?
158
- rtypes = all_rtypes.uniq
159
- Solargraph::ComplexType.parse("#{fqns}<#{rtypes.map(&:tag).join(', ')}>")
160
- elsif fixed_parameters?
161
- Solargraph::ComplexType.parse("#{fqns}(#{all_rtypes.map(&:tag).join(', ')})")
162
- elsif hash_parameters?
163
- ltypes = all_ltypes.uniq
164
- rtypes = all_rtypes.uniq
165
- Solargraph::ComplexType.parse("#{fqns}{#{ltypes.map(&:tag).join(', ')} => #{rtypes.map(&:tag).join(', ')}}")
166
- else
167
- Solargraph::ComplexType.parse(fqns)
190
+ transform do |t|
191
+ next t if t.name == GENERIC_TAG_NAME
192
+ next t if t.duck_type? || t.void? || t.undefined?
193
+ recon = (t.rooted? ? '' : context)
194
+ fqns = api_map.qualify(t.name, recon)
195
+ if fqns.nil?
196
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
197
+ next UniqueType::UNDEFINED
198
+ end
199
+ t.recreate(new_name: fqns, make_rooted: true)
168
200
  end
169
201
  end
170
202