solargraph 0.54.0 → 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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +127 -0
  3. data/.github/workflows/plugins.yml +184 -6
  4. data/.github/workflows/rspec.yml +55 -5
  5. data/.github/workflows/typecheck.yml +8 -3
  6. data/.gitignore +7 -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 +171 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/bin/solargraph +8 -5
  16. data/lib/solargraph/api_map/cache.rb +13 -3
  17. data/lib/solargraph/api_map/constants.rb +279 -0
  18. data/lib/solargraph/api_map/index.rb +193 -0
  19. data/lib/solargraph/api_map/source_to_yard.rb +13 -4
  20. data/lib/solargraph/api_map/store.rb +207 -132
  21. data/lib/solargraph/api_map.rb +394 -261
  22. data/lib/solargraph/bench.rb +18 -1
  23. data/lib/solargraph/complex_type/type_methods.rb +29 -12
  24. data/lib/solargraph/complex_type/unique_type.rb +205 -26
  25. data/lib/solargraph/complex_type.rb +126 -26
  26. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  27. data/lib/solargraph/convention/base.rb +20 -3
  28. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  29. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  30. data/lib/solargraph/convention/data_definition.rb +105 -0
  31. data/lib/solargraph/convention/gemspec.rb +3 -2
  32. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  33. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  34. data/lib/solargraph/convention/struct_definition.rb +164 -0
  35. data/lib/solargraph/convention.rb +36 -4
  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 +316 -64
  39. data/lib/solargraph/environ.rb +9 -2
  40. data/lib/solargraph/equality.rb +34 -0
  41. data/lib/solargraph/gem_pins.rb +64 -38
  42. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  43. data/lib/solargraph/language_server/host/message_worker.rb +54 -5
  44. data/lib/solargraph/language_server/host.rb +36 -18
  45. data/lib/solargraph/language_server/message/base.rb +20 -12
  46. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  47. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  48. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  49. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  50. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  51. data/lib/solargraph/language_server/message/text_document/definition.rb +5 -3
  52. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  53. data/lib/solargraph/language_server/message/text_document/formatting.rb +23 -2
  54. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  55. data/lib/solargraph/language_server/message/text_document/type_definition.rb +4 -3
  56. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  57. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  58. data/lib/solargraph/language_server/progress.rb +27 -2
  59. data/lib/solargraph/language_server/request.rb +4 -1
  60. data/lib/solargraph/library.rb +83 -73
  61. data/lib/solargraph/location.rb +45 -1
  62. data/lib/solargraph/logging.rb +12 -2
  63. data/lib/solargraph/page.rb +3 -0
  64. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  65. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  66. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  67. data/lib/solargraph/parser/node_processor.rb +26 -8
  68. data/lib/solargraph/parser/parser_gem/class_methods.rb +10 -18
  69. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  70. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  71. data/lib/solargraph/parser/parser_gem/node_methods.rb +10 -19
  72. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  73. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -0
  74. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +26 -20
  75. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  76. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  77. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  79. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  80. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  81. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  83. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  84. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +14 -2
  85. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  86. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  89. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +16 -6
  90. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +64 -32
  91. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  92. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  93. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  94. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  95. data/lib/solargraph/parser/region.rb +4 -1
  96. data/lib/solargraph/parser/snippet.rb +2 -0
  97. data/lib/solargraph/parser.rb +3 -5
  98. data/lib/solargraph/pin/base.rb +417 -42
  99. data/lib/solargraph/pin/base_variable.rb +21 -12
  100. data/lib/solargraph/pin/block.rb +9 -26
  101. data/lib/solargraph/pin/breakable.rb +9 -0
  102. data/lib/solargraph/pin/callable.rb +231 -0
  103. data/lib/solargraph/pin/closure.rb +30 -10
  104. data/lib/solargraph/pin/common.rb +12 -7
  105. data/lib/solargraph/pin/constant.rb +2 -0
  106. data/lib/solargraph/pin/conversions.rb +3 -2
  107. data/lib/solargraph/pin/delegated_method.rb +20 -1
  108. data/lib/solargraph/pin/documenting.rb +16 -0
  109. data/lib/solargraph/pin/instance_variable.rb +2 -2
  110. data/lib/solargraph/pin/keyword.rb +7 -2
  111. data/lib/solargraph/pin/local_variable.rb +15 -7
  112. data/lib/solargraph/pin/method.rb +241 -70
  113. data/lib/solargraph/pin/method_alias.rb +3 -0
  114. data/lib/solargraph/pin/namespace.rb +21 -13
  115. data/lib/solargraph/pin/parameter.rb +94 -32
  116. data/lib/solargraph/pin/proxy_type.rb +17 -7
  117. data/lib/solargraph/pin/reference/override.rb +24 -6
  118. data/lib/solargraph/pin/reference/require.rb +2 -2
  119. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  120. data/lib/solargraph/pin/reference.rb +17 -0
  121. data/lib/solargraph/pin/search.rb +6 -1
  122. data/lib/solargraph/pin/signature.rb +39 -121
  123. data/lib/solargraph/pin/singleton.rb +1 -1
  124. data/lib/solargraph/pin/symbol.rb +8 -2
  125. data/lib/solargraph/pin/until.rb +18 -0
  126. data/lib/solargraph/pin/while.rb +18 -0
  127. data/lib/solargraph/pin.rb +8 -2
  128. data/lib/solargraph/pin_cache.rb +245 -0
  129. data/lib/solargraph/position.rb +19 -0
  130. data/lib/solargraph/range.rb +23 -4
  131. data/lib/solargraph/rbs_map/conversions.rb +315 -99
  132. data/lib/solargraph/rbs_map/core_fills.rb +50 -16
  133. data/lib/solargraph/rbs_map/core_map.rb +41 -11
  134. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  135. data/lib/solargraph/rbs_map.rb +87 -16
  136. data/lib/solargraph/shell.rb +117 -17
  137. data/lib/solargraph/source/chain/array.rb +13 -8
  138. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  139. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  140. data/lib/solargraph/source/chain/call.rb +135 -66
  141. data/lib/solargraph/source/chain/constant.rb +3 -66
  142. data/lib/solargraph/source/chain/hash.rb +9 -3
  143. data/lib/solargraph/source/chain/head.rb +1 -1
  144. data/lib/solargraph/source/chain/if.rb +7 -2
  145. data/lib/solargraph/source/chain/link.rb +38 -6
  146. data/lib/solargraph/source/chain/literal.rb +27 -2
  147. data/lib/solargraph/source/chain/or.rb +2 -2
  148. data/lib/solargraph/source/chain/z_super.rb +1 -1
  149. data/lib/solargraph/source/chain.rb +140 -63
  150. data/lib/solargraph/source/change.rb +2 -2
  151. data/lib/solargraph/source/cursor.rb +4 -4
  152. data/lib/solargraph/source/source_chainer.rb +3 -3
  153. data/lib/solargraph/source.rb +110 -89
  154. data/lib/solargraph/source_map/clip.rb +22 -28
  155. data/lib/solargraph/source_map/data.rb +34 -0
  156. data/lib/solargraph/source_map/mapper.rb +11 -7
  157. data/lib/solargraph/source_map.rb +50 -43
  158. data/lib/solargraph/type_checker/checks.rb +4 -0
  159. data/lib/solargraph/type_checker/param_def.rb +2 -0
  160. data/lib/solargraph/type_checker/rules.rb +35 -8
  161. data/lib/solargraph/type_checker.rb +331 -189
  162. data/lib/solargraph/version.rb +1 -1
  163. data/lib/solargraph/views/_method.erb +10 -10
  164. data/lib/solargraph/views/_namespace.erb +3 -3
  165. data/lib/solargraph/views/document.erb +10 -10
  166. data/lib/solargraph/views/environment.erb +3 -5
  167. data/lib/solargraph/workspace/config.rb +25 -5
  168. data/lib/solargraph/workspace/require_paths.rb +97 -0
  169. data/lib/solargraph/workspace.rb +53 -72
  170. data/lib/solargraph/yard_map/helpers.rb +29 -1
  171. data/lib/solargraph/yard_map/mapper/to_constant.rb +8 -5
  172. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -19
  173. data/lib/solargraph/yard_map/mapper/to_namespace.rb +11 -7
  174. data/lib/solargraph/yard_map/mapper.rb +5 -3
  175. data/lib/solargraph/yard_map/to_method.rb +6 -3
  176. data/lib/solargraph/yardoc.rb +45 -10
  177. data/lib/solargraph.rb +35 -1
  178. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  179. data/rbs/fills/open3/0/open3.rbs +172 -0
  180. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  181. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  182. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  183. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  184. data/rbs/fills/tuple/tuple.rbs +149 -0
  185. data/rbs_collection.yaml +19 -0
  186. data/sig/shims/ast/0/node.rbs +5 -0
  187. data/sig/shims/ast/2.4/.rbs_meta.yaml +9 -0
  188. data/sig/shims/ast/2.4/ast.rbs +73 -0
  189. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  190. data/sig/shims/parser/3.2.0.1/manifest.yaml +7 -0
  191. data/sig/shims/parser/3.2.0.1/parser.rbs +201 -0
  192. data/sig/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  193. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  194. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  195. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  196. data/solargraph.gemspec +32 -10
  197. metadata +237 -37
  198. data/lib/.rubocop.yml +0 -22
  199. data/lib/solargraph/cache.rb +0 -77
  200. data/lib/solargraph/parser/node_methods.rb +0 -83
@@ -5,13 +5,15 @@ require 'yard'
5
5
  require 'solargraph/yard_tags'
6
6
 
7
7
  module Solargraph
8
- # An aggregate provider for information about workspaces, sources, gems, and
8
+ # An aggregate provider for information about Workspaces, Sources, gems, and
9
9
  # the Ruby core.
10
10
  #
11
11
  class ApiMap
12
12
  autoload :Cache, 'solargraph/api_map/cache'
13
13
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
14
14
  autoload :Store, 'solargraph/api_map/store'
15
+ autoload :Index, 'solargraph/api_map/index'
16
+ autoload :Constants, 'solargraph/api_map/constants'
15
17
 
16
18
  # @return [Array<String>]
17
19
  attr_reader :unresolved_requires
@@ -25,7 +27,6 @@ module Solargraph
25
27
  def initialize pins: []
26
28
  @source_map_hash = {}
27
29
  @cache = Cache.new
28
- @method_alias_stack = []
29
30
  index pins
30
31
  end
31
32
 
@@ -35,11 +36,14 @@ module Solargraph
35
36
  # just caches), please also change `equality_fields` below.
36
37
  #
37
38
 
39
+ # @param other [Object]
38
40
  def eql?(other)
39
41
  self.class == other.class &&
42
+ # @sg-ignore Flow sensitive typing needs to handle self.class == other.class
40
43
  equality_fields == other.equality_fields
41
44
  end
42
45
 
46
+ # @param other [Object]
43
47
  def ==(other)
44
48
  self.eql?(other)
45
49
  end
@@ -48,25 +52,35 @@ module Solargraph
48
52
  equality_fields.hash
49
53
  end
50
54
 
55
+ def to_s
56
+ self.class.to_s
57
+ end
58
+
59
+ # avoid enormous dump
60
+ def inspect
61
+ to_s
62
+ end
63
+
51
64
  # @param pins [Array<Pin::Base>]
52
65
  # @return [self]
53
66
  def index pins
54
67
  # @todo This implementation is incomplete. It should probably create a
55
68
  # Bench.
56
69
  @source_map_hash = {}
57
- implicit.clear
70
+ conventions_environ.clear
58
71
  cache.clear
59
- @store = Store.new(@@core_map.pins + pins)
72
+ store.update @@core_map.pins, pins
60
73
  self
61
74
  end
62
75
 
63
76
  # Map a single source.
64
77
  #
65
78
  # @param source [Source]
79
+ # @param live [Boolean] True for live source map (active editor file)
66
80
  # @return [self]
67
- def map source
81
+ def map source, live: false
68
82
  map = Solargraph::SourceMap.map(source)
69
- catalog Bench.new(source_maps: [map])
83
+ catalog Bench.new(source_maps: [map], live_map: live ? map : nil)
70
84
  self
71
85
  end
72
86
 
@@ -75,29 +89,37 @@ module Solargraph
75
89
  # @param bench [Bench]
76
90
  # @return [self]
77
91
  def catalog bench
78
- old_api_hash = @source_map_hash&.values&.map(&:api_hash)
79
- need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
80
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
81
- pins = bench.source_maps.flat_map(&:pins).flatten
82
- implicit.clear
92
+ @source_map_hash = bench.source_map_hash
93
+ iced_pins = bench.icebox.flat_map(&:pins)
94
+ live_pins = bench.live_map&.all_pins || []
95
+ conventions_environ.clear
83
96
  source_map_hash.each_value do |map|
84
- implicit.merge map.environ
97
+ conventions_environ.merge map.conventions_environ
85
98
  end
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
99
+ unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
100
+ recreate_docmap = @unresolved_requires != unresolved_requires ||
101
+ @doc_map&.uncached_yard_gemspecs&.any? ||
102
+ @doc_map&.uncached_rbs_collection_gemspecs&.any? ||
103
+ @doc_map&.rbs_collection_path != bench.workspace.rbs_collection_path
104
+ if recreate_docmap
105
+ @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
106
+ @unresolved_requires = @doc_map.unresolved_requires
91
107
  end
92
- @store = Store.new(@@core_map.pins + @doc_map.pins + implicit.pins + pins)
93
- @cache.clear if need_to_uncache
94
-
108
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
95
109
  @missing_docs = [] # @todo Implement missing docs
96
110
  self
97
111
  end
98
112
 
113
+ # @todo need to model type def statement in chains as a symbol so
114
+ # that this overload of 'protected' will typecheck @sg-ignore
115
+ # @sg-ignore
99
116
  protected def equality_fields
100
- [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
117
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
118
+ end
119
+
120
+ # @return [DocMap]
121
+ def doc_map
122
+ @doc_map ||= DocMap.new([], [])
101
123
  end
102
124
 
103
125
  # @return [::Array<Gem::Specification>]
@@ -105,7 +127,17 @@ module Solargraph
105
127
  @doc_map&.uncached_gemspecs || []
106
128
  end
107
129
 
108
- # @return [Array<Pin::Base>]
130
+ # @return [::Array<Gem::Specification>]
131
+ def uncached_rbs_collection_gemspecs
132
+ @doc_map.uncached_rbs_collection_gemspecs
133
+ end
134
+
135
+ # @return [::Array<Gem::Specification>]
136
+ def uncached_yard_gemspecs
137
+ @doc_map.uncached_yard_gemspecs
138
+ end
139
+
140
+ # @return [Enumerable<Pin::Base>]
109
141
  def core_pins
110
142
  @@core_map.pins
111
143
  end
@@ -122,8 +154,8 @@ module Solargraph
122
154
  end
123
155
 
124
156
  # @return [Environ]
125
- def implicit
126
- @implicit ||= Environ.new
157
+ def conventions_environ
158
+ @conventions_environ ||= Environ.new
127
159
  end
128
160
 
129
161
  # @param filename [String]
@@ -148,6 +180,7 @@ module Solargraph
148
180
  # Create an ApiMap with a workspace in the specified directory.
149
181
  #
150
182
  # @param directory [String]
183
+ #
151
184
  # @return [ApiMap]
152
185
  def self.load directory
153
186
  api_map = new
@@ -159,31 +192,46 @@ module Solargraph
159
192
  api_map
160
193
  end
161
194
 
195
+ # @param out [IO, nil]
196
+ # @return [void]
197
+ def cache_all!(out)
198
+ @doc_map.cache_all!(out)
199
+ end
200
+
201
+ # @param gemspec [Gem::Specification]
202
+ # @param rebuild [Boolean]
203
+ # @param out [IO, nil]
204
+ # @return [void]
205
+ def cache_gem(gemspec, rebuild: false, out: nil)
206
+ @doc_map.cache(gemspec, rebuild: rebuild, out: out)
207
+ end
208
+
209
+ class << self
210
+ include Logging
211
+ end
212
+
162
213
  # Create an ApiMap with a workspace in the specified directory and cache
163
214
  # any missing gems.
164
215
  #
165
216
  #
166
- # @todo IO::NULL is incorrectly inferred to be a String.
167
- # @sg-ignore
168
- #
169
217
  # @param directory [String]
170
218
  # @param out [IO] The output stream for messages
219
+ #
171
220
  # @return [ApiMap]
172
- def self.load_with_cache directory, out = IO::NULL
221
+ def self.load_with_cache directory, out
173
222
  api_map = load(directory)
174
- return api_map if api_map.uncached_gemspecs.empty?
175
-
176
- api_map.uncached_gemspecs.each do |gemspec|
177
- out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
178
- pins = GemPins.build(gemspec)
179
- Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
223
+ if api_map.uncached_gemspecs.empty?
224
+ logger.info { "All gems cached for #{directory}" }
225
+ return api_map
180
226
  end
227
+
228
+ api_map.cache_all!(out)
181
229
  load(directory)
182
230
  end
183
231
 
184
232
  # @return [Array<Solargraph::Pin::Base>]
185
233
  def pins
186
- store.pins
234
+ store.pins.clone.freeze
187
235
  end
188
236
 
189
237
  # An array of pins based on Ruby keywords (`if`, `end`, etc.).
@@ -193,13 +241,6 @@ module Solargraph
193
241
  store.pins_by_class(Pin::Keyword)
194
242
  end
195
243
 
196
- # An array of namespace names defined in the ApiMap.
197
- #
198
- # @return [Set<String>]
199
- def namespaces
200
- store.namespaces
201
- end
202
-
203
244
  # True if the namespace exists.
204
245
  #
205
246
  # @param name [String] The namespace to match
@@ -214,22 +255,16 @@ module Solargraph
214
255
  #
215
256
  # @param namespace [String] The namespace
216
257
  # @param contexts [Array<String>] The contexts
217
- # @return [Array<Solargraph::Pin::Base>]
258
+ # @return [Array<Solargraph::Pin::Constant, Solargraph::Pin::Namespace>]
218
259
  def get_constants namespace, *contexts
219
260
  namespace ||= ''
220
- contexts.push '' if contexts.empty?
221
- cached = cache.get_constants(namespace, contexts)
222
- return cached.clone unless cached.nil?
223
- skip = Set.new
224
- result = []
225
- contexts.each do |context|
226
- fqns = qualify(namespace, context)
227
- visibility = [:public]
228
- visibility.push :private if fqns == context
229
- result.concat inner_get_constants(fqns, visibility, skip)
230
- end
231
- cache.set_constants(namespace, contexts, result)
232
- 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 }
233
268
  end
234
269
 
235
270
  # @param namespace [String]
@@ -250,43 +285,41 @@ module Solargraph
250
285
  # @param tag [String, nil] The namespace to
251
286
  # match, complete with generic parameters set to appropriate
252
287
  # values if available
253
- # @param context_tag [String] The context in which the tag was
254
- # referenced; start from here to resolve the name
288
+ # @param gates [Array<String>] The fully qualified context in which
289
+ # the tag was referenced; start from here to resolve the name.
290
+ # Should not be prefixed with '::'.
255
291
  # @return [String, nil] fully qualified tag
256
- def qualify tag, context_tag = ''
257
- return tag if ['self', nil].include?(tag)
258
- context_type = ComplexType.try_parse(context_tag)
259
- return unless context_type
292
+ def qualify tag, *gates
293
+ store.constants.qualify(tag, *gates)
294
+ end
260
295
 
261
- type = ComplexType.try_parse(tag)
262
- return unless 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
263
304
 
264
- fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
265
- 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
266
312
 
267
- fqns + type.substring
313
+ # @param fqns [String]
314
+ # @return [Array<Pin::Reference::Extend>]
315
+ def get_extends(fqns)
316
+ store.get_extends(fqns)
268
317
  end
269
318
 
270
- # Determine fully qualified namespace for a given namespace used
271
- # inside the definition of another tag ("context"). This method
272
- # will start the search in the specified context until it finds a
273
- # match for the namespace.
274
- #
275
- # @param namespace [String, nil] The namespace to
276
- # match
277
- # @param context_namespace [String] The context namespace in which the
278
- # tag was referenced; start from here to resolve the name
279
- # @return [String, nil] fully qualified namespace
280
- def qualify_namespace(namespace, context_namespace = '')
281
- cached = cache.get_qualified_namespace(namespace, context_namespace)
282
- return cached.clone unless cached.nil?
283
- result = if namespace.start_with?('::')
284
- inner_qualify(namespace[2..-1], '', Set.new)
285
- else
286
- inner_qualify(namespace, context_namespace, Set.new)
287
- end
288
- cache.set_qualified_namespace(namespace, context_namespace, result)
289
- result
319
+ # @param fqns [String]
320
+ # @return [Array<Pin::Reference::Include>]
321
+ def get_includes(fqns)
322
+ store.get_includes(fqns)
290
323
  end
291
324
 
292
325
  # Get an array of instance variable pins defined in specified namespace
@@ -299,15 +332,20 @@ module Solargraph
299
332
  result = []
300
333
  used = [namespace]
301
334
  result.concat store.get_instance_variables(namespace, scope)
302
- sc = qualify_lower(store.get_superclass(namespace), namespace)
303
- until sc.nil? || used.include?(sc)
304
- used.push sc
305
- result.concat store.get_instance_variables(sc, scope)
306
- 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)
307
339
  end
308
340
  result
309
341
  end
310
342
 
343
+ # @sg-ignore Missing @return tag for Solargraph::ApiMap#visible_pins
344
+ # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
345
+ def visible_pins(*args, **kwargs, &blk)
346
+ Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
347
+ end
348
+
311
349
  # Get an array of class variable pins for a namespace.
312
350
  #
313
351
  # @param namespace [String] A fully qualified namespace
@@ -326,6 +364,11 @@ module Solargraph
326
364
  store.pins_by_class(Pin::GlobalVariable)
327
365
  end
328
366
 
367
+ # @return [Enumerable<Solargraph::Pin::Block>]
368
+ def get_block_pins
369
+ store.pins_by_class(Pin::Block)
370
+ end
371
+
329
372
  # Get an array of methods available in a particular context.
330
373
  #
331
374
  # @param rooted_tag [String] The fully qualified namespace to search for methods
@@ -334,13 +377,22 @@ module Solargraph
334
377
  # @param deep [Boolean] True to include superclasses, mixins, etc.
335
378
  # @return [Array<Solargraph::Pin::Method>]
336
379
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
380
+ if rooted_tag.start_with? 'Array('
381
+ # Array() are really tuples - use our fill, as the RBS repo
382
+ # does not give us definitions for it
383
+ rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})"
384
+ end
385
+ rooted_type = ComplexType.try_parse(rooted_tag)
386
+ fqns = rooted_type.namespace
387
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
337
388
  cached = cache.get_methods(rooted_tag, scope, visibility, deep)
338
389
  return cached.clone unless cached.nil?
390
+ # @type [Array<Solargraph::Pin::Method>]
339
391
  result = []
340
392
  skip = Set.new
341
393
  if rooted_tag == ''
342
394
  # @todo Implement domains
343
- implicit.domains.each do |domain|
395
+ conventions_environ.domains.each do |domain|
344
396
  type = ComplexType.try_parse(domain)
345
397
  next if type.undefined?
346
398
  result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
@@ -356,26 +408,47 @@ module Solargraph
356
408
  init_pin = get_method_stack(rooted_tag, 'initialize').first
357
409
  next pin unless init_pin
358
410
 
359
- type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
360
- Pin::Method.new(
411
+ type = ComplexType::SELF
412
+ new_pin = Pin::Method.new(
361
413
  name: 'new',
362
414
  scope: :class,
363
415
  location: init_pin.location,
364
- parameters: init_pin.parameters,
365
- signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
366
416
  return_type: type,
367
417
  comments: init_pin.comments,
368
- closure: init_pin.closure
369
- # @todo Hack to force TypeChecker#internal_or_core?
370
- ).tap { |pin| pin.source = :rbs }
418
+ closure: init_pin.closure,
419
+ source: init_pin.source,
420
+ type_location: init_pin.type_location,
421
+ )
422
+ new_pin.parameters = init_pin.parameters.map do |init_param|
423
+ param = init_param.clone
424
+ param.closure = new_pin
425
+ param.reset_generated!
426
+ param
427
+ end.freeze
428
+ new_pin.signatures = init_pin.signatures.map do |init_sig|
429
+ sig = init_sig.proxy(type)
430
+ sig.parameters = init_sig.parameters.map do |param|
431
+ param = param.clone
432
+ param.closure = new_pin
433
+ param.reset_generated!
434
+ param
435
+ end.freeze
436
+ sig.closure = new_pin
437
+ sig.reset_generated!
438
+ sig
439
+ end.freeze
440
+ new_pin
371
441
  end
372
442
  end
373
443
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
374
444
  result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
375
445
  end
376
- resolved = resolve_method_aliases(result, visibility)
377
- cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
378
- resolved
446
+ result = resolve_method_aliases(result, visibility)
447
+ if namespace_pin && rooted_tag != rooted_type.name
448
+ result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
449
+ end
450
+ cache.set_methods(rooted_tag, scope, visibility, deep, result)
451
+ result
379
452
  end
380
453
 
381
454
  # Get an array of method pins for a complex type.
@@ -403,7 +476,7 @@ module Solargraph
403
476
  result = Set.new
404
477
  complex_type.each do |type|
405
478
  if type.duck_type?
406
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1])
479
+ result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
407
480
  result.merge get_methods('Object')
408
481
  else
409
482
  unless type.nil? || type.name == 'void'
@@ -430,9 +503,27 @@ module Solargraph
430
503
  # @param rooted_tag [String] Parameterized namespace, fully qualified
431
504
  # @param name [String] Method name to look up
432
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
433
509
  # @return [Array<Solargraph::Pin::Method>]
434
- def get_method_stack rooted_tag, name, scope: :instance
435
- get_methods(rooted_tag, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
510
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
511
+ rooted_type = ComplexType.parse(rooted_tag)
512
+ fqns = rooted_type.namespace
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
525
+ methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
526
+ methods
436
527
  end
437
528
 
438
529
  # Get an array of all suggestions that match the specified path.
@@ -440,7 +531,7 @@ module Solargraph
440
531
  # @deprecated Use #get_path_pins instead.
441
532
  #
442
533
  # @param path [String] The path to find
443
- # @return [Enumerable<Solargraph::Pin::Base>]
534
+ # @return [Array<Solargraph::Pin::Base>]
444
535
  def get_path_suggestions path
445
536
  return [] if path.nil?
446
537
  resolve_method_aliases store.get_path_pins(path)
@@ -449,7 +540,7 @@ module Solargraph
449
540
  # Get an array of pins that match the specified path.
450
541
  #
451
542
  # @param path [String]
452
- # @return [Enumerable<Pin::Base>]
543
+ # @return [Array<Pin::Base>]
453
544
  def get_path_pins path
454
545
  get_path_suggestions(path)
455
546
  end
@@ -467,13 +558,8 @@ module Solargraph
467
558
  .select { |path| path.downcase.include?(query.downcase) }
468
559
  end
469
560
 
470
- # Get YARD documentation for the specified path.
471
- #
472
- # @example
473
- # api_map.document('String#split')
474
- #
475
- # @todo This method is likely superfluous. Calling get_path_pins directly
476
- # should be sufficient.
561
+ # @deprecated This method is likely superfluous. Calling #get_path_pins
562
+ # directly should be sufficient.
477
563
  #
478
564
  # @param path [String] The path to find
479
565
  # @return [Enumerable<Pin::Base>]
@@ -545,13 +631,22 @@ module Solargraph
545
631
  # @param sub [String] The subclass
546
632
  # @return [Boolean]
547
633
  def super_and_sub?(sup, sub)
548
- fqsup = qualify(sup)
549
- cls = qualify(sub)
550
- tested = []
551
- until fqsup.nil? || cls.nil? || tested.include?(cls)
552
- return true if cls == fqsup
553
- tested.push cls
554
- 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
555
650
  end
556
651
  false
557
652
  end
@@ -564,7 +659,56 @@ module Solargraph
564
659
  #
565
660
  # @return [Boolean]
566
661
  def type_include?(host_ns, module_ns)
567
- 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)
663
+ end
664
+
665
+ # @param pins [Enumerable<Pin::Base>]
666
+ # @param visibility [Enumerable<Symbol>]
667
+ # @return [Array<Pin::Base>]
668
+ def resolve_method_aliases pins, visibility = [:public, :private, :protected]
669
+ with_resolved_aliases = pins.map do |pin|
670
+ next pin unless pin.is_a?(Pin::MethodAlias)
671
+ resolved = resolve_method_alias(pin)
672
+ next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
673
+ resolved
674
+ end.compact
675
+ logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" }
676
+ GemPins.combine_method_pins_by_path(with_resolved_aliases)
677
+ end
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
568
712
  end
569
713
 
570
714
  private
@@ -591,60 +735,55 @@ module Solargraph
591
735
  # @param no_core [Boolean] Skip core classes if true
592
736
  # @return [Array<Pin::Base>]
593
737
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
594
- rooted_type = ComplexType.parse(rooted_tag)
738
+ rooted_type = ComplexType.parse(rooted_tag).force_rooted
595
739
  fqns = rooted_type.namespace
596
740
  fqns_generic_params = rooted_type.all_params
741
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
597
742
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
598
743
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
599
744
  return [] if skip.include?(reqstr)
600
745
  skip.add reqstr
601
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
+
602
754
  if deep && scope == :instance
603
755
  store.get_prepends(fqns).reverse.each do |im|
604
- fqim = qualify(im, fqns)
756
+ fqim = store.constants.dereference(im)
605
757
  result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
606
758
  end
607
759
  end
608
760
  # Store#get_methods doesn't know about full tags, just
609
761
  # namespaces; resolving the generics in the method pins is this
610
762
  # class' responsibility
611
- raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
612
- namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
613
- methods = if namespace_pin && rooted_tag != fqns
614
- methods = raw_methods.map do |method_pin|
615
- method_pin.resolve_generics(namespace_pin, rooted_type)
616
- end
617
- else
618
- raw_methods
619
- end
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}" }
620
765
  result.concat methods
621
766
  if deep
767
+ result.concat convention_methods_by_reference
768
+
622
769
  if scope == :instance
623
- store.get_includes(fqns).reverse.each do |include_tag|
624
- rooted_include_tag = qualify(include_tag, rooted_tag)
625
- # Ensure the types returned by the included methods are
626
- # relative to the generics passed to the include. e.g.,
627
- # Foo<String> might include Enumerable<String>
628
- #
629
- # @todo perform the same translation in the other areas
630
- # here after adding a spec and handling things correctly
631
- # in ApiMap::Store and RbsMap::Conversions
632
- resolved_include_type = ComplexType.parse(rooted_include_tag).resolve_generics(namespace_pin, rooted_type)
633
- methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
634
- result.concat methods
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)
635
773
  end
636
- fqsc = qualify_superclass(fqns)
637
- unless fqsc.nil?
638
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
774
+ rooted_sc_tag = qualify_superclass(rooted_tag)
775
+ unless rooted_sc_tag.nil?
776
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
639
777
  end
640
778
  else
779
+ logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
641
780
  store.get_extends(fqns).reverse.each do |em|
642
- fqem = qualify(em, fqns)
781
+ fqem = dereference(em)
643
782
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
644
783
  end
645
- fqsc = qualify_superclass(fqns)
646
- unless fqsc.nil?
647
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
784
+ rooted_sc_tag = qualify_superclass(rooted_tag)
785
+ unless rooted_sc_tag.nil?
786
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true)
648
787
  end
649
788
  unless no_core || fqns.empty?
650
789
  type = get_namespace_type(fqns)
@@ -660,91 +799,15 @@ module Solargraph
660
799
  result
661
800
  end
662
801
 
663
- # @param fqns [String]
664
- # @param visibility [Array<Symbol>]
665
- # @param skip [Set<String>]
666
- # @return [Array<Pin::Base>]
667
- def inner_get_constants fqns, visibility, skip
668
- return [] if fqns.nil? || skip.include?(fqns)
669
- skip.add fqns
670
- result = []
671
- store.get_prepends(fqns).each do |is|
672
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
673
- end
674
- result.concat store.get_constants(fqns, visibility)
675
- .sort { |a, b| a.name <=> b.name }
676
- store.get_includes(fqns).each do |is|
677
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
678
- end
679
- fqsc = qualify_superclass(fqns)
680
- unless %w[Object BasicObject].include?(fqsc)
681
- result.concat inner_get_constants(fqsc, [:public], skip)
682
- end
683
- result
684
- end
685
-
686
802
  # @return [Hash]
687
803
  def path_macros
688
804
  @path_macros ||= {}
689
805
  end
690
806
 
691
- # @param namespace [String]
692
- # @param context [String]
693
- # @return [String]
694
- def qualify_lower namespace, context
695
- qualify namespace, context.split('::')[0..-2].join('::')
696
- end
697
-
698
- # @param fqsub [String]
807
+ # @param fq_sub_tag [String]
699
808
  # @return [String, nil]
700
- def qualify_superclass fqsub
701
- sup = store.get_superclass(fqsub)
702
- return nil if sup.nil?
703
- parts = fqsub.split('::')
704
- last = parts.pop
705
- parts.pop if last == sup
706
- qualify(sup, parts.join('::'))
707
- end
708
-
709
- # @param name [String] Namespace to fully qualify
710
- # @param root [String] The context to search
711
- # @param skip [Set<String>] Contexts already searched
712
- # @return [String, nil] Fully qualified ("rooted") namespace
713
- def inner_qualify name, root, skip
714
- return name if name == ComplexType::GENERIC_TAG_NAME
715
- return nil if name.nil?
716
- return nil if skip.include?(root)
717
- skip.add root
718
- possibles = []
719
- if name == ''
720
- if root == ''
721
- return ''
722
- else
723
- return inner_qualify(root, '', skip)
724
- end
725
- else
726
- return name if root == '' && store.namespace_exists?(name)
727
- roots = root.to_s.split('::')
728
- while roots.length > 0
729
- fqns = roots.join('::') + '::' + name
730
- return fqns if store.namespace_exists?(fqns)
731
- incs = store.get_includes(roots.join('::'))
732
- incs.each do |inc|
733
- foundinc = inner_qualify(name, inc, skip)
734
- possibles.push foundinc unless foundinc.nil?
735
- end
736
- roots.pop
737
- end
738
- if possibles.empty?
739
- incs = store.get_includes('')
740
- incs.each do |inc|
741
- foundinc = inner_qualify(name, inc, skip)
742
- possibles.push foundinc unless foundinc.nil?
743
- end
744
- end
745
- return name if store.namespace_exists?(name)
746
- return possibles.last
747
- end
809
+ def qualify_superclass fq_sub_tag
810
+ store.qualify_superclass fq_sub_tag
748
811
  end
749
812
 
750
813
  # Get the namespace's type (Class or Module).
@@ -761,8 +824,8 @@ module Solargraph
761
824
 
762
825
  # Sort an array of pins to put nil or undefined variables last.
763
826
  #
764
- # @param pins [Enumerable<Solargraph::Pin::Base>]
765
- # @return [Enumerable<Solargraph::Pin::Base>]
827
+ # @param pins [Enumerable<Pin::BaseVariable>]
828
+ # @return [Enumerable<Pin::BaseVariable>]
766
829
  def prefer_non_nil_variables pins
767
830
  result = []
768
831
  nil_pins = []
@@ -776,37 +839,107 @@ module Solargraph
776
839
  result + nil_pins
777
840
  end
778
841
 
779
- # @param pins [Enumerable<Pin::Base>]
780
- # @param visibility [Enumerable<Symbol>]
781
- # @return [Array<Pin::Base>]
782
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
783
- pins.map do |pin|
784
- resolved = resolve_method_alias(pin)
785
- next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
786
- resolved
787
- end.compact
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
788
875
  end
789
876
 
790
- # @param pin [Pin::MethodAlias, Pin::Base]
791
- # @return [Pin::Method]
792
- def resolve_method_alias pin
793
- return pin unless pin.is_a?(Pin::MethodAlias)
794
- return nil if @method_alias_stack.include?(pin.path)
795
- @method_alias_stack.push pin.path
796
- origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
797
- @method_alias_stack.pop
798
- return nil if origin.nil?
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)
799
883
  args = {
800
- location: pin.location,
801
- closure: pin.closure,
802
- name: pin.name,
803
- comments: origin.comments,
804
- scope: origin.scope,
805
- visibility: origin.visibility,
806
- signatures: origin.signatures,
807
- attribute: origin.attribute?
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,
895
+ source: :resolve_method_alias
808
896
  }
809
- Pin::Method.new **args
897
+ resolved_pin = Pin::Method.new **args
898
+
899
+ # Clone signatures and parameters
900
+ resolved_pin.signatures.each do |sig|
901
+ sig.parameters = sig.parameters.map(&:clone).freeze
902
+ sig.source = :resolve_method_alias
903
+ sig.parameters.each do |param|
904
+ param.closure = resolved_pin
905
+ param.source = :resolve_method_alias
906
+ param.reset_generated!
907
+ end
908
+ sig.closure = resolved_pin
909
+ sig.reset_generated!
910
+ end
911
+
912
+ resolved_pin
913
+ end
914
+
915
+ # @param namespace_pin [Pin::Namespace]
916
+ # @param rooted_type [ComplexType]
917
+ # @param pins [Enumerable<Pin::Base>]
918
+ # @return [Array<Pin::Base>]
919
+ def erase_generics(namespace_pin, rooted_type, pins)
920
+ return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
921
+
922
+ logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
923
+ pins.map do |method_pin|
924
+ method_pin.erase_generics(namespace_pin.generics)
925
+ end
926
+ end
927
+
928
+ # @param namespace_pin [Pin::Namespace]
929
+ # @param rooted_type [ComplexType]
930
+ def should_erase_generics_when_done?(namespace_pin, rooted_type)
931
+ has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
932
+ end
933
+
934
+ # @param namespace_pin [Pin::Namespace, Pin::Constant]
935
+ def has_generics?(namespace_pin)
936
+ namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
937
+ end
938
+
939
+ # @param namespace_pin [Pin::Namespace]
940
+ # @param rooted_type [ComplexType]
941
+ def can_resolve_generics?(namespace_pin, rooted_type)
942
+ has_generics?(namespace_pin) && !rooted_type.all_params.empty?
810
943
  end
811
944
  end
812
945
  end