solargraph 0.56.2 → 0.58.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +127 -0
  3. data/.github/workflows/plugins.yml +183 -7
  4. data/.github/workflows/rspec.yml +55 -5
  5. data/.github/workflows/typecheck.yml +6 -3
  6. data/.gitignore +5 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +1279 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +69 -0
  13. data/README.md +8 -4
  14. data/Rakefile +125 -13
  15. data/bin/solargraph +8 -5
  16. data/lib/solargraph/api_map/cache.rb +3 -2
  17. data/lib/solargraph/api_map/constants.rb +279 -0
  18. data/lib/solargraph/api_map/index.rb +49 -31
  19. data/lib/solargraph/api_map/source_to_yard.rb +13 -4
  20. data/lib/solargraph/api_map/store.rb +144 -26
  21. data/lib/solargraph/api_map.rb +217 -245
  22. data/lib/solargraph/bench.rb +1 -0
  23. data/lib/solargraph/complex_type/type_methods.rb +6 -0
  24. data/lib/solargraph/complex_type/unique_type.rb +19 -12
  25. data/lib/solargraph/complex_type.rb +24 -3
  26. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  27. data/lib/solargraph/convention/base.rb +17 -0
  28. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +1 -0
  29. data/lib/solargraph/convention/data_definition/data_definition_node.rb +4 -2
  30. data/lib/solargraph/convention/data_definition.rb +2 -1
  31. data/lib/solargraph/convention/gemspec.rb +1 -1
  32. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
  33. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +3 -1
  34. data/lib/solargraph/convention/struct_definition.rb +36 -13
  35. data/lib/solargraph/convention.rb +31 -2
  36. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  38. data/lib/solargraph/doc_map.rb +44 -13
  39. data/lib/solargraph/environ.rb +9 -2
  40. data/lib/solargraph/equality.rb +1 -0
  41. data/lib/solargraph/gem_pins.rb +21 -11
  42. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  43. data/lib/solargraph/language_server/host/message_worker.rb +3 -0
  44. data/lib/solargraph/language_server/host.rb +12 -5
  45. data/lib/solargraph/language_server/message/base.rb +2 -1
  46. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +19 -2
  49. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  50. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  51. data/lib/solargraph/language_server/progress.rb +8 -0
  52. data/lib/solargraph/language_server/request.rb +4 -1
  53. data/lib/solargraph/library.rb +11 -18
  54. data/lib/solargraph/location.rb +3 -0
  55. data/lib/solargraph/logging.rb +11 -2
  56. data/lib/solargraph/page.rb +3 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +8 -1
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +33 -5
  59. data/lib/solargraph/parser/node_processor/base.rb +1 -1
  60. data/lib/solargraph/parser/node_processor.rb +6 -2
  61. data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -13
  62. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  63. data/lib/solargraph/parser/parser_gem/node_chainer.rb +3 -1
  64. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -16
  65. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +1 -0
  66. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +3 -2
  67. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +2 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -0
  69. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +64 -8
  70. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +12 -3
  71. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +36 -16
  72. data/lib/solargraph/parser/region.rb +3 -0
  73. data/lib/solargraph/parser/snippet.rb +2 -0
  74. data/lib/solargraph/pin/base.rb +77 -14
  75. data/lib/solargraph/pin/base_variable.rb +6 -5
  76. data/lib/solargraph/pin/block.rb +3 -2
  77. data/lib/solargraph/pin/callable.rb +14 -1
  78. data/lib/solargraph/pin/closure.rb +5 -7
  79. data/lib/solargraph/pin/common.rb +6 -2
  80. data/lib/solargraph/pin/constant.rb +2 -0
  81. data/lib/solargraph/pin/local_variable.rb +1 -2
  82. data/lib/solargraph/pin/method.rb +28 -9
  83. data/lib/solargraph/pin/method_alias.rb +3 -0
  84. data/lib/solargraph/pin/parameter.rb +24 -10
  85. data/lib/solargraph/pin/proxy_type.rb +5 -1
  86. data/lib/solargraph/pin/reference/override.rb +15 -1
  87. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  88. data/lib/solargraph/pin/reference.rb +17 -0
  89. data/lib/solargraph/pin/search.rb +6 -1
  90. data/lib/solargraph/pin/signature.rb +2 -0
  91. data/lib/solargraph/pin/symbol.rb +5 -0
  92. data/lib/solargraph/pin_cache.rb +64 -4
  93. data/lib/solargraph/position.rb +3 -0
  94. data/lib/solargraph/range.rb +5 -0
  95. data/lib/solargraph/rbs_map/conversions.rb +29 -6
  96. data/lib/solargraph/rbs_map/core_fills.rb +18 -0
  97. data/lib/solargraph/rbs_map/core_map.rb +14 -7
  98. data/lib/solargraph/rbs_map.rb +14 -1
  99. data/lib/solargraph/shell.rb +85 -1
  100. data/lib/solargraph/source/chain/call.rb +7 -3
  101. data/lib/solargraph/source/chain/constant.rb +3 -66
  102. data/lib/solargraph/source/chain/if.rb +1 -1
  103. data/lib/solargraph/source/chain/link.rb +11 -2
  104. data/lib/solargraph/source/chain/or.rb +1 -1
  105. data/lib/solargraph/source/chain.rb +11 -2
  106. data/lib/solargraph/source/change.rb +2 -2
  107. data/lib/solargraph/source/cursor.rb +2 -3
  108. data/lib/solargraph/source/source_chainer.rb +1 -1
  109. data/lib/solargraph/source.rb +6 -3
  110. data/lib/solargraph/source_map/clip.rb +18 -26
  111. data/lib/solargraph/source_map/data.rb +4 -0
  112. data/lib/solargraph/source_map/mapper.rb +2 -2
  113. data/lib/solargraph/source_map.rb +28 -16
  114. data/lib/solargraph/type_checker/param_def.rb +2 -0
  115. data/lib/solargraph/type_checker/rules.rb +30 -8
  116. data/lib/solargraph/type_checker.rb +301 -186
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/workspace/config.rb +21 -5
  119. data/lib/solargraph/workspace/require_paths.rb +97 -0
  120. data/lib/solargraph/workspace.rb +30 -67
  121. data/lib/solargraph/yard_map/mapper/to_method.rb +4 -3
  122. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  123. data/lib/solargraph/yard_map/to_method.rb +2 -1
  124. data/lib/solargraph/yardoc.rb +39 -3
  125. data/lib/solargraph.rb +2 -0
  126. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  127. data/rbs/fills/open3/0/open3.rbs +172 -0
  128. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  129. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  130. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  131. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  132. data/rbs/fills/{tuple.rbs → tuple/tuple.rbs} +2 -3
  133. data/rbs_collection.yaml +4 -4
  134. data/sig/shims/ast/0/node.rbs +5 -0
  135. data/sig/shims/ast/2.4/.rbs_meta.yaml +9 -0
  136. data/sig/shims/ast/2.4/ast.rbs +73 -0
  137. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  138. data/sig/shims/parser/3.2.0.1/manifest.yaml +7 -0
  139. data/sig/shims/parser/3.2.0.1/parser.rbs +201 -0
  140. data/sig/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  141. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  142. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  143. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  144. data/solargraph.gemspec +26 -5
  145. metadata +181 -13
  146. data/lib/.rubocop.yml +0 -22
  147. data/lib/solargraph/parser/node_methods.rb +0 -97
@@ -13,6 +13,7 @@ module Solargraph
13
13
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
14
14
  autoload :Store, 'solargraph/api_map/store'
15
15
  autoload :Index, 'solargraph/api_map/index'
16
+ autoload :Constants, 'solargraph/api_map/constants'
16
17
 
17
18
  # @return [Array<String>]
18
19
  attr_reader :unresolved_requires
@@ -26,7 +27,6 @@ module Solargraph
26
27
  def initialize pins: []
27
28
  @source_map_hash = {}
28
29
  @cache = Cache.new
29
- @method_alias_stack = []
30
30
  index pins
31
31
  end
32
32
 
@@ -36,11 +36,14 @@ module Solargraph
36
36
  # just caches), please also change `equality_fields` below.
37
37
  #
38
38
 
39
+ # @param other [Object]
39
40
  def eql?(other)
40
41
  self.class == other.class &&
42
+ # @sg-ignore Flow sensitive typing needs to handle self.class == other.class
41
43
  equality_fields == other.equality_fields
42
44
  end
43
45
 
46
+ # @param other [Object]
44
47
  def ==(other)
45
48
  self.eql?(other)
46
49
  end
@@ -64,7 +67,7 @@ module Solargraph
64
67
  # @todo This implementation is incomplete. It should probably create a
65
68
  # Bench.
66
69
  @source_map_hash = {}
67
- implicit.clear
70
+ conventions_environ.clear
68
71
  cache.clear
69
72
  store.update @@core_map.pins, pins
70
73
  self
@@ -73,10 +76,11 @@ module Solargraph
73
76
  # Map a single source.
74
77
  #
75
78
  # @param source [Source]
79
+ # @param live [Boolean] True for live source map (active editor file)
76
80
  # @return [self]
77
- def map source
81
+ def map source, live: false
78
82
  map = Solargraph::SourceMap.map(source)
79
- catalog Bench.new(source_maps: [map])
83
+ catalog Bench.new(source_maps: [map], live_map: live ? map : nil)
80
84
  self
81
85
  end
82
86
 
@@ -87,12 +91,12 @@ module Solargraph
87
91
  def catalog bench
88
92
  @source_map_hash = bench.source_map_hash
89
93
  iced_pins = bench.icebox.flat_map(&:pins)
90
- live_pins = bench.live_map&.pins || []
91
- implicit.clear
94
+ live_pins = bench.live_map&.all_pins || []
95
+ conventions_environ.clear
92
96
  source_map_hash.each_value do |map|
93
- implicit.merge map.environ
97
+ conventions_environ.merge map.conventions_environ
94
98
  end
95
- unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
99
+ unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
96
100
  recreate_docmap = @unresolved_requires != unresolved_requires ||
97
101
  @doc_map&.uncached_yard_gemspecs&.any? ||
98
102
  @doc_map&.uncached_rbs_collection_gemspecs&.any? ||
@@ -101,7 +105,7 @@ module Solargraph
101
105
  @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
102
106
  @unresolved_requires = @doc_map.unresolved_requires
103
107
  end
104
- @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
108
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
105
109
  @missing_docs = [] # @todo Implement missing docs
106
110
  self
107
111
  end
@@ -110,9 +114,10 @@ module Solargraph
110
114
  # that this overload of 'protected' will typecheck @sg-ignore
111
115
  # @sg-ignore
112
116
  protected def equality_fields
113
- [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
117
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
114
118
  end
115
119
 
120
+ # @return [DocMap]
116
121
  def doc_map
117
122
  @doc_map ||= DocMap.new([], [])
118
123
  end
@@ -132,7 +137,7 @@ module Solargraph
132
137
  @doc_map.uncached_yard_gemspecs
133
138
  end
134
139
 
135
- # @return [Array<Pin::Base>]
140
+ # @return [Enumerable<Pin::Base>]
136
141
  def core_pins
137
142
  @@core_map.pins
138
143
  end
@@ -149,8 +154,8 @@ module Solargraph
149
154
  end
150
155
 
151
156
  # @return [Environ]
152
- def implicit
153
- @implicit ||= Environ.new
157
+ def conventions_environ
158
+ @conventions_environ ||= Environ.new
154
159
  end
155
160
 
156
161
  # @param filename [String]
@@ -175,6 +180,7 @@ module Solargraph
175
180
  # Create an ApiMap with a workspace in the specified directory.
176
181
  #
177
182
  # @param directory [String]
183
+ #
178
184
  # @return [ApiMap]
179
185
  def self.load directory
180
186
  api_map = new
@@ -186,10 +192,16 @@ module Solargraph
186
192
  api_map
187
193
  end
188
194
 
195
+ # @param out [IO, nil]
196
+ # @return [void]
189
197
  def cache_all!(out)
190
198
  @doc_map.cache_all!(out)
191
199
  end
192
200
 
201
+ # @param gemspec [Gem::Specification]
202
+ # @param rebuild [Boolean]
203
+ # @param out [IO, nil]
204
+ # @return [void]
193
205
  def cache_gem(gemspec, rebuild: false, out: nil)
194
206
  @doc_map.cache(gemspec, rebuild: rebuild, out: out)
195
207
  end
@@ -202,11 +214,9 @@ module Solargraph
202
214
  # any missing gems.
203
215
  #
204
216
  #
205
- # @todo IO::NULL is incorrectly inferred to be a String.
206
- # @sg-ignore
207
- #
208
217
  # @param directory [String]
209
218
  # @param out [IO] The output stream for messages
219
+ #
210
220
  # @return [ApiMap]
211
221
  def self.load_with_cache directory, out
212
222
  api_map = load(directory)
@@ -231,13 +241,6 @@ module Solargraph
231
241
  store.pins_by_class(Pin::Keyword)
232
242
  end
233
243
 
234
- # An array of namespace names defined in the ApiMap.
235
- #
236
- # @return [Set<String>]
237
- def namespaces
238
- store.namespaces
239
- end
240
-
241
244
  # True if the namespace exists.
242
245
  #
243
246
  # @param name [String] The namespace to match
@@ -252,22 +255,16 @@ module Solargraph
252
255
  #
253
256
  # @param namespace [String] The namespace
254
257
  # @param contexts [Array<String>] The contexts
255
- # @return [Array<Solargraph::Pin::Base>]
258
+ # @return [Array<Solargraph::Pin::Constant, Solargraph::Pin::Namespace>]
256
259
  def get_constants namespace, *contexts
257
260
  namespace ||= ''
258
- contexts.push '' if contexts.empty?
259
- cached = cache.get_constants(namespace, contexts)
260
- return cached.clone unless cached.nil?
261
- skip = Set.new
262
- result = []
263
- contexts.each do |context|
264
- fqns = qualify(namespace, context)
265
- visibility = [:public]
266
- visibility.push :private if fqns == context
267
- result.concat inner_get_constants(fqns, visibility, skip)
268
- end
269
- cache.set_constants(namespace, contexts, result)
270
- result
261
+ gates = contexts.clone
262
+ gates.push '' if contexts.empty? && namespace.empty?
263
+ gates.push namespace unless namespace.empty?
264
+ store.constants
265
+ .collect(gates)
266
+ .select { |pin| namespace.empty? || contexts.empty? || pin.namespace == namespace }
267
+ .select { |pin| pin.visibility == :public || pin.namespace == namespace }
271
268
  end
272
269
 
273
270
  # @param namespace [String]
@@ -288,49 +285,41 @@ module Solargraph
288
285
  # @param tag [String, nil] The namespace to
289
286
  # match, complete with generic parameters set to appropriate
290
287
  # values if available
291
- # @param context_tag [String] The fully qualified context in which
288
+ # @param gates [Array<String>] The fully qualified context in which
292
289
  # the tag was referenced; start from here to resolve the name.
293
290
  # Should not be prefixed with '::'.
294
291
  # @return [String, nil] fully qualified tag
295
- def qualify tag, context_tag = ''
296
- return tag if ['Boolean', 'self', nil].include?(tag)
297
-
298
- context_type = ComplexType.try_parse(context_tag).force_rooted
299
- return unless context_type
300
-
301
- type = ComplexType.try_parse(tag)
302
- return unless type
303
- return tag if type.literal?
292
+ def qualify tag, *gates
293
+ store.constants.qualify(tag, *gates)
294
+ end
304
295
 
305
- context_type = ComplexType.try_parse(context_tag)
306
- return unless context_type
296
+ # @see Store::Constants#resolve
297
+ #
298
+ # @param name [String]
299
+ # @param gates [Array<String, Array<String>>]
300
+ # @return [String, nil]
301
+ def resolve name, *gates
302
+ store.constants.resolve(name, *gates)
303
+ end
307
304
 
308
- fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
309
- return unless fqns
305
+ # Get a fully qualified namespace from a reference pin.
306
+ #
307
+ # @param pin [Pin::Reference]
308
+ # @return [String, nil]
309
+ def dereference(pin)
310
+ store.constants.dereference(pin)
311
+ end
310
312
 
311
- fqns + type.substring
313
+ # @param fqns [String]
314
+ # @return [Array<Pin::Reference::Extend>]
315
+ def get_extends(fqns)
316
+ store.get_extends(fqns)
312
317
  end
313
318
 
314
- # Determine fully qualified namespace for a given namespace used
315
- # inside the definition of another tag ("context"). This method
316
- # will start the search in the specified context until it finds a
317
- # match for the namespace.
318
- #
319
- # @param namespace [String, nil] The namespace to
320
- # match
321
- # @param context_namespace [String] The context namespace in which the
322
- # tag was referenced; start from here to resolve the name
323
- # @return [String, nil] fully qualified namespace
324
- def qualify_namespace(namespace, context_namespace = '')
325
- cached = cache.get_qualified_namespace(namespace, context_namespace)
326
- return cached.clone unless cached.nil?
327
- result = if namespace.start_with?('::')
328
- inner_qualify(namespace[2..-1], '', Set.new)
329
- else
330
- inner_qualify(namespace, context_namespace, Set.new)
331
- end
332
- cache.set_qualified_namespace(namespace, context_namespace, result)
333
- result
319
+ # @param fqns [String]
320
+ # @return [Array<Pin::Reference::Include>]
321
+ def get_includes(fqns)
322
+ store.get_includes(fqns)
334
323
  end
335
324
 
336
325
  # Get an array of instance variable pins defined in specified namespace
@@ -343,15 +332,15 @@ module Solargraph
343
332
  result = []
344
333
  used = [namespace]
345
334
  result.concat store.get_instance_variables(namespace, scope)
346
- sc = qualify_lower(store.get_superclass(namespace), namespace)
347
- until sc.nil? || used.include?(sc)
348
- used.push sc
349
- result.concat store.get_instance_variables(sc, scope)
350
- sc = qualify_lower(store.get_superclass(sc), sc)
335
+ sc_fqns = namespace
336
+ while (sc = store.get_superclass(sc_fqns))
337
+ sc_fqns = store.constants.dereference(sc)
338
+ result.concat store.get_instance_variables(sc_fqns, scope)
351
339
  end
352
340
  result
353
341
  end
354
342
 
343
+ # @sg-ignore Missing @return tag for Solargraph::ApiMap#visible_pins
355
344
  # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
356
345
  def visible_pins(*args, **kwargs, &blk)
357
346
  Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
@@ -403,7 +392,7 @@ module Solargraph
403
392
  skip = Set.new
404
393
  if rooted_tag == ''
405
394
  # @todo Implement domains
406
- implicit.domains.each do |domain|
395
+ conventions_environ.domains.each do |domain|
407
396
  type = ComplexType.try_parse(domain)
408
397
  next if type.undefined?
409
398
  result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
@@ -514,12 +503,25 @@ module Solargraph
514
503
  # @param rooted_tag [String] Parameterized namespace, fully qualified
515
504
  # @param name [String] Method name to look up
516
505
  # @param scope [Symbol] :instance or :class
506
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
507
+ # @param preserve_generics [Boolean] True to preserve any
508
+ # unresolved generic parameters, false to erase them
517
509
  # @return [Array<Solargraph::Pin::Method>]
518
510
  def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
519
511
  rooted_type = ComplexType.parse(rooted_tag)
520
512
  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 }
513
+ namespace_pin = store.get_path_pins(fqns).first
514
+ methods = if namespace_pin.is_a?(Pin::Constant)
515
+ type = namespace_pin.infer(self)
516
+ if type.defined?
517
+ namespace_pin = store.get_path_pins(type.namespace).first
518
+ get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
519
+ else
520
+ []
521
+ end
522
+ else
523
+ get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
524
+ end
523
525
  methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
524
526
  methods
525
527
  end
@@ -529,7 +531,7 @@ module Solargraph
529
531
  # @deprecated Use #get_path_pins instead.
530
532
  #
531
533
  # @param path [String] The path to find
532
- # @return [Enumerable<Solargraph::Pin::Base>]
534
+ # @return [Array<Solargraph::Pin::Base>]
533
535
  def get_path_suggestions path
534
536
  return [] if path.nil?
535
537
  resolve_method_aliases store.get_path_pins(path)
@@ -538,7 +540,7 @@ module Solargraph
538
540
  # Get an array of pins that match the specified path.
539
541
  #
540
542
  # @param path [String]
541
- # @return [Enumerable<Pin::Base>]
543
+ # @return [Array<Pin::Base>]
542
544
  def get_path_pins path
543
545
  get_path_suggestions(path)
544
546
  end
@@ -629,13 +631,22 @@ module Solargraph
629
631
  # @param sub [String] The subclass
630
632
  # @return [Boolean]
631
633
  def super_and_sub?(sup, sub)
632
- fqsup = qualify(sup)
633
- cls = qualify(sub)
634
- tested = []
635
- until fqsup.nil? || cls.nil? || tested.include?(cls)
636
- return true if cls == fqsup
637
- tested.push cls
638
- cls = qualify_superclass(cls)
634
+ sup = ComplexType.try_parse(sup)
635
+ sub = ComplexType.try_parse(sub)
636
+ # @todo If two literals are different values of the same type, it would
637
+ # make more sense for super_and_sub? to return true, but there are a
638
+ # few callers that currently expect this to be false.
639
+ return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
640
+ sup = sup.simplify_literals.to_s
641
+ sub = sub.simplify_literals.to_s
642
+ return true if sup == sub
643
+ sc_fqns = sub
644
+ while (sc = store.get_superclass(sc_fqns))
645
+ sc_new = store.constants.dereference(sc)
646
+ # Cyclical inheritance is invalid
647
+ return false if sc_new == sc_fqns
648
+ sc_fqns = sc_new
649
+ return true if sc_fqns == sup
639
650
  end
640
651
  false
641
652
  end
@@ -648,7 +659,7 @@ module Solargraph
648
659
  #
649
660
  # @return [Boolean]
650
661
  def type_include?(host_ns, module_ns)
651
- store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
662
+ store.get_includes(host_ns).map { |inc_tag| inc_tag.type.name }.include?(module_ns)
652
663
  end
653
664
 
654
665
  # @param pins [Enumerable<Pin::Base>]
@@ -656,6 +667,7 @@ module Solargraph
656
667
  # @return [Array<Pin::Base>]
657
668
  def resolve_method_aliases pins, visibility = [:public, :private, :protected]
658
669
  with_resolved_aliases = pins.map do |pin|
670
+ next pin unless pin.is_a?(Pin::MethodAlias)
659
671
  resolved = resolve_method_alias(pin)
660
672
  next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
661
673
  resolved
@@ -664,6 +676,41 @@ module Solargraph
664
676
  GemPins.combine_method_pins_by_path(with_resolved_aliases)
665
677
  end
666
678
 
679
+ # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
680
+ # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
681
+ # parameter - used to pull generics information
682
+ # @param type [ComplexType] The type which is having its
683
+ # methods supplemented from fq_reference_tag
684
+ # @param scope [Symbol] :class or :instance
685
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
686
+ # @param deep [Boolean]
687
+ # @param skip [Set<String>]
688
+ # @param no_core [Boolean] Skip core classes if true
689
+ # @return [Array<Pin::Base>]
690
+ def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
691
+ logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
692
+
693
+ # Ensure the types returned by the methods in the referenced
694
+ # type are relative to the generic values passed in the
695
+ # reference. e.g., Foo<String> might include Enumerable<String>
696
+ #
697
+ # @todo perform the same translation in the other areas
698
+ # here after adding a spec and handling things correctly
699
+ # in ApiMap::Store and RbsMap::Conversions for each
700
+ resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type)
701
+ # @todo Can inner_get_methods be cached? Lots of lookups of base types going on.
702
+ methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core)
703
+ if namespace_pin && !resolved_reference_type.all_params.empty?
704
+ reference_pin = store.get_path_pins(resolved_reference_type.name).select { |p| p.is_a?(Pin::Namespace) }.first
705
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" }
706
+ methods = methods.map do |method_pin|
707
+ method_pin.resolve_generics(reference_pin, resolved_reference_type)
708
+ end
709
+ end
710
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" }
711
+ methods
712
+ end
713
+
667
714
  private
668
715
 
669
716
  # A hash of source maps with filename keys.
@@ -697,9 +744,16 @@ module Solargraph
697
744
  return [] if skip.include?(reqstr)
698
745
  skip.add reqstr
699
746
  result = []
747
+ environ = Convention.for_object(self, rooted_tag, scope, visibility, deep, skip, no_core)
748
+ # ensure we start out with any immediate methods in this
749
+ # namespace so we roughly match the same ordering of get_methods
750
+ # and obey the 'deep' instruction
751
+ direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag }
752
+ result.concat direct_convention_methods
753
+
700
754
  if deep && scope == :instance
701
755
  store.get_prepends(fqns).reverse.each do |im|
702
- fqim = qualify(im, fqns)
756
+ fqim = store.constants.dereference(im)
703
757
  result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
704
758
  end
705
759
  end
@@ -707,20 +761,24 @@ module Solargraph
707
761
  # namespaces; resolving the generics in the method pins is this
708
762
  # class' responsibility
709
763
  methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
764
+ logger.info { "ApiMap#inner_get_methods(rooted_tag=#{rooted_tag.inspect}, scope=#{scope.inspect}, visibility=#{visibility.inspect}, deep=#{deep.inspect}, skip=#{skip.inspect}, fqns=#{fqns}) - added from store: #{methods}" }
710
765
  result.concat methods
711
766
  if deep
767
+ result.concat convention_methods_by_reference
768
+
712
769
  if scope == :instance
713
- store.get_includes(fqns).reverse.each do |include_tag|
714
- rooted_include_tag = qualify(include_tag, rooted_tag)
715
- result.concat inner_get_methods_from_reference(rooted_include_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
770
+ store.get_includes(fqns).reverse.each do |ref|
771
+ in_tag = dereference(ref)
772
+ result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
716
773
  end
717
774
  rooted_sc_tag = qualify_superclass(rooted_tag)
718
775
  unless rooted_sc_tag.nil?
719
776
  result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
720
777
  end
721
778
  else
779
+ logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
722
780
  store.get_extends(fqns).reverse.each do |em|
723
- fqem = qualify(em, fqns)
781
+ fqem = dereference(em)
724
782
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
725
783
  end
726
784
  rooted_sc_tag = qualify_superclass(rooted_tag)
@@ -741,130 +799,15 @@ module Solargraph
741
799
  result
742
800
  end
743
801
 
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
-
779
- # @param fqns [String]
780
- # @param visibility [Array<Symbol>]
781
- # @param skip [Set<String>]
782
- # @return [Array<Pin::Base>]
783
- def inner_get_constants fqns, visibility, skip
784
- return [] if fqns.nil? || skip.include?(fqns)
785
- skip.add fqns
786
- result = []
787
- store.get_prepends(fqns).each do |is|
788
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
789
- end
790
- result.concat store.get_constants(fqns, visibility)
791
- .sort { |a, b| a.name <=> b.name }
792
- store.get_includes(fqns).each do |is|
793
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
794
- end
795
- fqsc = qualify_superclass(fqns)
796
- unless %w[Object BasicObject].include?(fqsc)
797
- result.concat inner_get_constants(fqsc, [:public], skip)
798
- end
799
- result
800
- end
801
-
802
802
  # @return [Hash]
803
803
  def path_macros
804
804
  @path_macros ||= {}
805
805
  end
806
806
 
807
- # @param namespace [String]
808
- # @param context [String]
809
- # @return [String, nil]
810
- def qualify_lower namespace, context
811
- qualify namespace, context.split('::')[0..-2].join('::')
812
- end
813
-
814
- # @param fq_tag [String]
807
+ # @param fq_sub_tag [String]
815
808
  # @return [String, nil]
816
809
  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('::')
824
- last = parts.pop
825
- parts.pop if last == sup_ns
826
- qualify(sup_tag, parts.join('::'))
827
- end
828
-
829
- # @param name [String] Namespace to fully qualify
830
- # @param root [String] The context to search
831
- # @param skip [Set<String>] Contexts already searched
832
- # @return [String, nil] Fully qualified ("rooted") namespace
833
- def inner_qualify name, root, skip
834
- return name if name == ComplexType::GENERIC_TAG_NAME
835
- return nil if name.nil?
836
- return nil if skip.include?(root)
837
- skip.add root
838
- possibles = []
839
- if name == ''
840
- if root == ''
841
- return ''
842
- else
843
- return inner_qualify(root, '', skip)
844
- end
845
- else
846
- return name if root == '' && store.namespace_exists?(name)
847
- roots = root.to_s.split('::')
848
- while roots.length > 0
849
- fqns = roots.join('::') + '::' + name
850
- return fqns if store.namespace_exists?(fqns)
851
- incs = store.get_includes(roots.join('::'))
852
- incs.each do |inc|
853
- foundinc = inner_qualify(name, inc, skip)
854
- possibles.push foundinc unless foundinc.nil?
855
- end
856
- roots.pop
857
- end
858
- if possibles.empty?
859
- incs = store.get_includes('')
860
- incs.each do |inc|
861
- foundinc = inner_qualify(name, inc, skip)
862
- possibles.push foundinc unless foundinc.nil?
863
- end
864
- end
865
- return name if store.namespace_exists?(name)
866
- return possibles.last
867
- end
810
+ store.qualify_superclass fq_sub_tag
868
811
  end
869
812
 
870
813
  # Get the namespace's type (Class or Module).
@@ -896,49 +839,78 @@ module Solargraph
896
839
  result + nil_pins
897
840
  end
898
841
 
899
- # @param pin [Pin::MethodAlias, Pin::Base]
900
- # @return [Pin::Method]
901
- def resolve_method_alias pin
902
- return pin unless pin.is_a?(Pin::MethodAlias)
903
- return nil if @method_alias_stack.include?(pin.path)
904
- @method_alias_stack.push pin.path
905
- origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope, preserve_generics: true).first
906
- @method_alias_stack.pop
907
- return nil if origin.nil?
842
+ include Logging
843
+
844
+ private
845
+
846
+ # @param alias_pin [Pin::MethodAlias]
847
+ # @return [Pin::Method, nil]
848
+ def resolve_method_alias(alias_pin)
849
+ ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
850
+ original = nil
851
+
852
+ # Search each ancestor for the original method
853
+ ancestors.each do |ancestor_fqns|
854
+ next if ancestor_fqns.nil?
855
+ ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
856
+
857
+ # Search for the original method in the ancestor
858
+ original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
859
+ next if candidate_pin == alias_pin
860
+
861
+ if candidate_pin.is_a?(Pin::MethodAlias)
862
+ # recursively resolve method aliases
863
+ resolved = resolve_method_alias(candidate_pin)
864
+ break resolved if resolved
865
+ end
866
+
867
+ candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
868
+ end
869
+
870
+ break if original
871
+ end
872
+
873
+ # @sg-ignore ignore `received nil` for original
874
+ create_resolved_alias_pin(alias_pin, original) if original
875
+ end
876
+
877
+ # Fast path for creating resolved alias pins without individual method stack lookups
878
+ # @param alias_pin [Pin::MethodAlias] The alias pin to resolve
879
+ # @param original [Pin::Method] The original method pin that was already found
880
+ # @return [Pin::Method] The resolved method pin
881
+ def create_resolved_alias_pin(alias_pin, original)
882
+ # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup)
908
883
  args = {
909
- location: pin.location,
910
- type_location: origin.type_location,
911
- closure: pin.closure,
912
- name: pin.name,
913
- comments: origin.comments,
914
- scope: origin.scope,
915
- # context: pin.context,
916
- visibility: origin.visibility,
917
- signatures: origin.signatures.map(&:clone).freeze,
918
- attribute: origin.attribute?,
919
- generics: origin.generics.clone,
920
- return_type: origin.return_type,
884
+ location: alias_pin.location,
885
+ type_location: original.type_location,
886
+ closure: alias_pin.closure,
887
+ name: alias_pin.name,
888
+ comments: original.comments,
889
+ scope: original.scope,
890
+ visibility: original.visibility,
891
+ signatures: original.signatures.map(&:clone).freeze,
892
+ attribute: original.attribute?,
893
+ generics: original.generics.clone,
894
+ return_type: original.return_type,
921
895
  source: :resolve_method_alias
922
896
  }
923
- out = Pin::Method.new **args
924
- out.signatures.each do |sig|
897
+ resolved_pin = Pin::Method.new **args
898
+
899
+ # Clone signatures and parameters
900
+ resolved_pin.signatures.each do |sig|
925
901
  sig.parameters = sig.parameters.map(&:clone).freeze
926
902
  sig.source = :resolve_method_alias
927
903
  sig.parameters.each do |param|
928
- param.closure = out
904
+ param.closure = resolved_pin
929
905
  param.source = :resolve_method_alias
930
906
  param.reset_generated!
931
907
  end
932
- sig.closure = out
908
+ sig.closure = resolved_pin
933
909
  sig.reset_generated!
934
910
  end
935
- logger.debug { "ApiMap#resolve_method_alias(pin=#{pin}) - returning #{out} from #{origin}" }
936
- out
937
- end
938
911
 
939
- include Logging
940
-
941
- private
912
+ resolved_pin
913
+ end
942
914
 
943
915
  # @param namespace_pin [Pin::Namespace]
944
916
  # @param rooted_type [ComplexType]
@@ -959,9 +931,9 @@ module Solargraph
959
931
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
960
932
  end
961
933
 
962
- # @param namespace_pin [Pin::Namespace]
934
+ # @param namespace_pin [Pin::Namespace, Pin::Constant]
963
935
  def has_generics?(namespace_pin)
964
- namespace_pin && !namespace_pin.generics.empty?
936
+ namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
965
937
  end
966
938
 
967
939
  # @param namespace_pin [Pin::Namespace]