solargraph 0.54.4 → 0.57.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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +125 -0
  3. data/.github/workflows/plugins.yml +149 -5
  4. data/.github/workflows/rspec.yml +39 -4
  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 +2627 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +104 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/lib/solargraph/api_map/cache.rb +3 -2
  16. data/lib/solargraph/api_map/constants.rb +218 -0
  17. data/lib/solargraph/api_map/index.rb +44 -42
  18. data/lib/solargraph/api_map/source_to_yard.rb +10 -4
  19. data/lib/solargraph/api_map/store.rb +165 -32
  20. data/lib/solargraph/api_map.rb +319 -243
  21. data/lib/solargraph/bench.rb +18 -1
  22. data/lib/solargraph/complex_type/type_methods.rb +7 -1
  23. data/lib/solargraph/complex_type/unique_type.rb +105 -16
  24. data/lib/solargraph/complex_type.rb +40 -7
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  26. data/lib/solargraph/convention/base.rb +20 -3
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  29. data/lib/solargraph/convention/data_definition.rb +105 -0
  30. data/lib/solargraph/convention/gemspec.rb +3 -2
  31. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  32. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  33. data/lib/solargraph/convention/struct_definition.rb +164 -0
  34. data/lib/solargraph/convention.rb +35 -4
  35. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  36. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  37. data/lib/solargraph/doc_map.rb +313 -65
  38. data/lib/solargraph/environ.rb +9 -2
  39. data/lib/solargraph/gem_pins.rb +60 -38
  40. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  41. data/lib/solargraph/language_server/host/message_worker.rb +13 -7
  42. data/lib/solargraph/language_server/host.rb +14 -3
  43. data/lib/solargraph/language_server/message/base.rb +2 -1
  44. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  45. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  46. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -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 +1 -0
  53. data/lib/solargraph/library.rb +53 -32
  54. data/lib/solargraph/location.rb +23 -0
  55. data/lib/solargraph/logging.rb +12 -2
  56. data/lib/solargraph/page.rb +4 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  59. data/lib/solargraph/parser/node_methods.rb +16 -2
  60. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  61. data/lib/solargraph/parser/node_processor.rb +26 -9
  62. data/lib/solargraph/parser/parser_gem/class_methods.rb +17 -15
  63. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  64. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +8 -4
  66. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  70. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  73. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  74. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  75. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  76. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  80. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  81. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  83. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  84. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +63 -30
  85. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  86. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  89. data/lib/solargraph/parser/region.rb +4 -1
  90. data/lib/solargraph/parser/snippet.rb +2 -0
  91. data/lib/solargraph/parser.rb +1 -0
  92. data/lib/solargraph/pin/base.rb +360 -30
  93. data/lib/solargraph/pin/base_variable.rb +16 -10
  94. data/lib/solargraph/pin/block.rb +2 -0
  95. data/lib/solargraph/pin/breakable.rb +9 -0
  96. data/lib/solargraph/pin/callable.rb +83 -3
  97. data/lib/solargraph/pin/closure.rb +20 -1
  98. data/lib/solargraph/pin/common.rb +10 -1
  99. data/lib/solargraph/pin/constant.rb +2 -0
  100. data/lib/solargraph/pin/delegated_method.rb +21 -1
  101. data/lib/solargraph/pin/documenting.rb +16 -0
  102. data/lib/solargraph/pin/keyword.rb +7 -2
  103. data/lib/solargraph/pin/local_variable.rb +18 -6
  104. data/lib/solargraph/pin/method.rb +175 -46
  105. data/lib/solargraph/pin/method_alias.rb +3 -0
  106. data/lib/solargraph/pin/namespace.rb +17 -9
  107. data/lib/solargraph/pin/parameter.rb +78 -19
  108. data/lib/solargraph/pin/proxy_type.rb +13 -6
  109. data/lib/solargraph/pin/reference/override.rb +24 -6
  110. data/lib/solargraph/pin/reference/require.rb +2 -2
  111. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  112. data/lib/solargraph/pin/reference.rb +26 -0
  113. data/lib/solargraph/pin/search.rb +3 -1
  114. data/lib/solargraph/pin/signature.rb +44 -0
  115. data/lib/solargraph/pin/singleton.rb +1 -1
  116. data/lib/solargraph/pin/symbol.rb +8 -2
  117. data/lib/solargraph/pin/until.rb +18 -0
  118. data/lib/solargraph/pin/while.rb +18 -0
  119. data/lib/solargraph/pin.rb +4 -1
  120. data/lib/solargraph/pin_cache.rb +245 -0
  121. data/lib/solargraph/position.rb +11 -0
  122. data/lib/solargraph/range.rb +10 -0
  123. data/lib/solargraph/rbs_map/conversions.rb +226 -70
  124. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  125. data/lib/solargraph/rbs_map/core_map.rb +37 -11
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  127. data/lib/solargraph/rbs_map.rb +88 -18
  128. data/lib/solargraph/shell.rb +20 -18
  129. data/lib/solargraph/source/chain/array.rb +11 -7
  130. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  131. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  132. data/lib/solargraph/source/chain/call.rb +53 -23
  133. data/lib/solargraph/source/chain/constant.rb +1 -1
  134. data/lib/solargraph/source/chain/hash.rb +4 -3
  135. data/lib/solargraph/source/chain/head.rb +1 -1
  136. data/lib/solargraph/source/chain/if.rb +1 -1
  137. data/lib/solargraph/source/chain/link.rb +12 -1
  138. data/lib/solargraph/source/chain/literal.rb +22 -2
  139. data/lib/solargraph/source/chain/or.rb +1 -1
  140. data/lib/solargraph/source/chain/z_super.rb +1 -1
  141. data/lib/solargraph/source/chain.rb +84 -47
  142. data/lib/solargraph/source/change.rb +2 -2
  143. data/lib/solargraph/source/cursor.rb +2 -3
  144. data/lib/solargraph/source/source_chainer.rb +3 -3
  145. data/lib/solargraph/source.rb +5 -2
  146. data/lib/solargraph/source_map/clip.rb +4 -2
  147. data/lib/solargraph/source_map/data.rb +4 -0
  148. data/lib/solargraph/source_map/mapper.rb +13 -7
  149. data/lib/solargraph/source_map.rb +21 -31
  150. data/lib/solargraph/type_checker/checks.rb +4 -0
  151. data/lib/solargraph/type_checker/param_def.rb +2 -0
  152. data/lib/solargraph/type_checker/rules.rb +8 -0
  153. data/lib/solargraph/type_checker.rb +208 -128
  154. data/lib/solargraph/version.rb +1 -1
  155. data/lib/solargraph/views/_method.erb +10 -10
  156. data/lib/solargraph/views/_namespace.erb +3 -3
  157. data/lib/solargraph/views/document.erb +10 -10
  158. data/lib/solargraph/workspace/config.rb +1 -3
  159. data/lib/solargraph/workspace/require_paths.rb +98 -0
  160. data/lib/solargraph/workspace.rb +38 -52
  161. data/lib/solargraph/yard_map/helpers.rb +29 -1
  162. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  163. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  164. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  165. data/lib/solargraph/yard_map/mapper.rb +4 -3
  166. data/lib/solargraph/yard_map/to_method.rb +4 -2
  167. data/lib/solargraph/yardoc.rb +22 -10
  168. data/lib/solargraph.rb +34 -1
  169. data/rbs/fills/tuple.rbs +149 -0
  170. data/rbs_collection.yaml +19 -0
  171. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  172. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  173. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  174. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  175. data/solargraph.gemspec +15 -4
  176. metadata +157 -15
  177. data/lib/.rubocop.yml +0 -22
  178. data/lib/solargraph/cache.rb +0 -77
@@ -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,13 @@ 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 &&
41
42
  equality_fields == other.equality_fields
42
43
  end
43
44
 
45
+ # @param other [Object]
44
46
  def ==(other)
45
47
  self.eql?(other)
46
48
  end
@@ -64,19 +66,20 @@ module Solargraph
64
66
  # @todo This implementation is incomplete. It should probably create a
65
67
  # Bench.
66
68
  @source_map_hash = {}
67
- implicit.clear
69
+ conventions_environ.clear
68
70
  cache.clear
69
- store.update! @@core_map.pins, pins
71
+ store.update @@core_map.pins, pins
70
72
  self
71
73
  end
72
74
 
73
75
  # Map a single source.
74
76
  #
75
77
  # @param source [Source]
78
+ # @param live [Boolean] True for live source map (active editor file)
76
79
  # @return [self]
77
- def map source
80
+ def map source, live: false
78
81
  map = Solargraph::SourceMap.map(source)
79
- catalog Bench.new(source_maps: [map])
82
+ catalog Bench.new(source_maps: [map], live_map: live ? map : nil)
80
83
  self
81
84
  end
82
85
 
@@ -85,24 +88,23 @@ module Solargraph
85
88
  # @param bench [Bench]
86
89
  # @return [self]
87
90
  def catalog bench
88
- old_api_hash = @source_map_hash&.values&.map(&:api_hash)
89
- need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
90
- # @todo Work around #to_h problem in current Ruby head (3.5)
91
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
92
- pins = bench.source_maps.flat_map(&:pins).flatten
93
- implicit.clear
91
+ @source_map_hash = bench.source_map_hash
92
+ iced_pins = bench.icebox.flat_map(&:pins)
93
+ live_pins = bench.live_map&.all_pins || []
94
+ conventions_environ.clear
94
95
  source_map_hash.each_value do |map|
95
- implicit.merge map.environ
96
+ conventions_environ.merge map.conventions_environ
96
97
  end
97
- unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
98
- if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
99
- @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
100
- @unresolved_requires = unresolved_requires
101
- need_to_uncache = true
98
+ unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
99
+ recreate_docmap = @unresolved_requires != unresolved_requires ||
100
+ @doc_map&.uncached_yard_gemspecs&.any? ||
101
+ @doc_map&.uncached_rbs_collection_gemspecs&.any? ||
102
+ @doc_map&.rbs_collection_path != bench.workspace.rbs_collection_path
103
+ if recreate_docmap
104
+ @doc_map = DocMap.new(unresolved_requires, [], bench.workspace) # @todo Implement gem preferences
105
+ @unresolved_requires = @doc_map.unresolved_requires
102
106
  end
103
- store.update! @@core_map.pins + @doc_map.pins, implicit.pins + pins
104
- @cache.clear if need_to_uncache
105
-
107
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
106
108
  @missing_docs = [] # @todo Implement missing docs
107
109
  self
108
110
  end
@@ -111,7 +113,12 @@ module Solargraph
111
113
  # that this overload of 'protected' will typecheck @sg-ignore
112
114
  # @sg-ignore
113
115
  protected def equality_fields
114
- [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
116
+ [self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
117
+ end
118
+
119
+ # @return [DocMap]
120
+ def doc_map
121
+ @doc_map ||= DocMap.new([], [])
115
122
  end
116
123
 
117
124
  # @return [::Array<Gem::Specification>]
@@ -119,7 +126,17 @@ module Solargraph
119
126
  @doc_map&.uncached_gemspecs || []
120
127
  end
121
128
 
122
- # @return [Array<Pin::Base>]
129
+ # @return [::Array<Gem::Specification>]
130
+ def uncached_rbs_collection_gemspecs
131
+ @doc_map.uncached_rbs_collection_gemspecs
132
+ end
133
+
134
+ # @return [::Array<Gem::Specification>]
135
+ def uncached_yard_gemspecs
136
+ @doc_map.uncached_yard_gemspecs
137
+ end
138
+
139
+ # @return [Enumerable<Pin::Base>]
123
140
  def core_pins
124
141
  @@core_map.pins
125
142
  end
@@ -136,8 +153,8 @@ module Solargraph
136
153
  end
137
154
 
138
155
  # @return [Environ]
139
- def implicit
140
- @implicit ||= Environ.new
156
+ def conventions_environ
157
+ @conventions_environ ||= Environ.new
141
158
  end
142
159
 
143
160
  # @param filename [String]
@@ -173,25 +190,39 @@ module Solargraph
173
190
  api_map
174
191
  end
175
192
 
193
+ # @param out [IO, nil]
194
+ # @return [void]
195
+ def cache_all!(out)
196
+ @doc_map.cache_all!(out)
197
+ end
198
+
199
+ # @param gemspec [Gem::Specification]
200
+ # @param rebuild [Boolean]
201
+ # @param out [IO, nil]
202
+ # @return [void]
203
+ def cache_gem(gemspec, rebuild: false, out: nil)
204
+ @doc_map.cache(gemspec, rebuild: rebuild, out: out)
205
+ end
206
+
207
+ class << self
208
+ include Logging
209
+ end
210
+
176
211
  # Create an ApiMap with a workspace in the specified directory and cache
177
212
  # any missing gems.
178
213
  #
179
214
  #
180
- # @todo IO::NULL is incorrectly inferred to be a String.
181
- # @sg-ignore
182
- #
183
215
  # @param directory [String]
184
216
  # @param out [IO] The output stream for messages
185
217
  # @return [ApiMap]
186
- def self.load_with_cache directory, out = IO::NULL
218
+ def self.load_with_cache directory, out
187
219
  api_map = load(directory)
188
- return api_map if api_map.uncached_gemspecs.empty?
189
-
190
- api_map.uncached_gemspecs.each do |gemspec|
191
- out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
192
- pins = GemPins.build(gemspec)
193
- Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
220
+ if api_map.uncached_gemspecs.empty?
221
+ logger.info { "All gems cached for #{directory}" }
222
+ return api_map
194
223
  end
224
+
225
+ api_map.cache_all!(out)
195
226
  load(directory)
196
227
  end
197
228
 
@@ -231,19 +262,13 @@ module Solargraph
231
262
  # @return [Array<Solargraph::Pin::Base>]
232
263
  def get_constants namespace, *contexts
233
264
  namespace ||= ''
234
- contexts.push '' if contexts.empty?
235
- cached = cache.get_constants(namespace, contexts)
236
- return cached.clone unless cached.nil?
237
- skip = Set.new
238
- result = []
239
- contexts.each do |context|
240
- fqns = qualify(namespace, context)
241
- visibility = [:public]
242
- visibility.push :private if fqns == context
243
- result.concat inner_get_constants(fqns, visibility, skip)
244
- end
245
- cache.set_constants(namespace, contexts, result)
246
- result
265
+ gates = contexts.clone
266
+ gates.push '' if contexts.empty? && namespace.empty?
267
+ gates.push namespace unless namespace.empty?
268
+ store.constants
269
+ .collect(gates)
270
+ .select { |pin| namespace.empty? || contexts.empty? || pin.namespace == namespace }
271
+ .select { |pin| pin.visibility == :public || pin.namespace == namespace }
247
272
  end
248
273
 
249
274
  # @param namespace [String]
@@ -269,40 +294,27 @@ module Solargraph
269
294
  # Should not be prefixed with '::'.
270
295
  # @return [String, nil] fully qualified tag
271
296
  def qualify tag, context_tag = ''
272
- return tag if ['self', nil].include?(tag)
273
-
274
- context_type = ComplexType.parse(context_tag).force_rooted
275
- return unless context_type
276
-
277
- type = ComplexType.try_parse(tag)
278
- return unless type
297
+ store.constants.qualify(tag, context_tag)
298
+ end
279
299
 
280
- fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
281
- return unless fqns
300
+ # Get a fully qualified namespace from a reference pin.
301
+ #
302
+ # @param pin [Pin::Reference]
303
+ # @return [String, nil]
304
+ def dereference(pin)
305
+ store.constants.dereference(pin)
306
+ end
282
307
 
283
- fqns + type.substring
308
+ # @param fqns [String]
309
+ # @return [Array<String>]
310
+ def get_extends(fqns)
311
+ store.get_extends(fqns)
284
312
  end
285
313
 
286
- # Determine fully qualified namespace for a given namespace used
287
- # inside the definition of another tag ("context"). This method
288
- # will start the search in the specified context until it finds a
289
- # match for the namespace.
290
- #
291
- # @param namespace [String, nil] The namespace to
292
- # match
293
- # @param context_namespace [String] The context namespace in which the
294
- # tag was referenced; start from here to resolve the name
295
- # @return [String, nil] fully qualified namespace
296
- def qualify_namespace(namespace, context_namespace = '')
297
- cached = cache.get_qualified_namespace(namespace, context_namespace)
298
- return cached.clone unless cached.nil?
299
- result = if namespace.start_with?('::')
300
- inner_qualify(namespace[2..-1], '', Set.new)
301
- else
302
- inner_qualify(namespace, context_namespace, Set.new)
303
- end
304
- cache.set_qualified_namespace(namespace, context_namespace, result)
305
- result
314
+ # @param fqns [String]
315
+ # @return [Array<String>]
316
+ def get_includes(fqns)
317
+ store.get_includes(fqns)
306
318
  end
307
319
 
308
320
  # Get an array of instance variable pins defined in specified namespace
@@ -315,15 +327,20 @@ module Solargraph
315
327
  result = []
316
328
  used = [namespace]
317
329
  result.concat store.get_instance_variables(namespace, scope)
318
- sc = qualify_lower(store.get_superclass(namespace), namespace)
319
- until sc.nil? || used.include?(sc)
320
- used.push sc
321
- result.concat store.get_instance_variables(sc, scope)
322
- sc = qualify_lower(store.get_superclass(sc), sc)
330
+ sc_fqns = namespace
331
+ while (sc = store.get_superclass(sc_fqns))
332
+ sc_fqns = store.constants.dereference(sc)
333
+ result.concat store.get_instance_variables(sc_fqns, scope)
323
334
  end
324
335
  result
325
336
  end
326
337
 
338
+ # @sg-ignore Missing @return tag for Solargraph::ApiMap#visible_pins
339
+ # @see Solargraph::Parser::FlowSensitiveTyping#visible_pins
340
+ def visible_pins(*args, **kwargs, &blk)
341
+ Solargraph::Parser::FlowSensitiveTyping.visible_pins(*args, **kwargs, &blk)
342
+ end
343
+
327
344
  # Get an array of class variable pins for a namespace.
328
345
  #
329
346
  # @param namespace [String] A fully qualified namespace
@@ -355,6 +372,11 @@ module Solargraph
355
372
  # @param deep [Boolean] True to include superclasses, mixins, etc.
356
373
  # @return [Array<Solargraph::Pin::Method>]
357
374
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
375
+ if rooted_tag.start_with? 'Array('
376
+ # Array() are really tuples - use our fill, as the RBS repo
377
+ # does not give us definitions for it
378
+ rooted_tag = "Solargraph::Fills::Tuple(#{rooted_tag[6..-2]})"
379
+ end
358
380
  rooted_type = ComplexType.try_parse(rooted_tag)
359
381
  fqns = rooted_type.namespace
360
382
  namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
@@ -365,7 +387,7 @@ module Solargraph
365
387
  skip = Set.new
366
388
  if rooted_tag == ''
367
389
  # @todo Implement domains
368
- implicit.domains.each do |domain|
390
+ conventions_environ.domains.each do |domain|
369
391
  type = ComplexType.try_parse(domain)
370
392
  next if type.undefined?
371
393
  result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
@@ -381,18 +403,36 @@ module Solargraph
381
403
  init_pin = get_method_stack(rooted_tag, 'initialize').first
382
404
  next pin unless init_pin
383
405
 
384
- type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
385
- Pin::Method.new(
406
+ type = ComplexType::SELF
407
+ new_pin = Pin::Method.new(
386
408
  name: 'new',
387
409
  scope: :class,
388
410
  location: init_pin.location,
389
- parameters: init_pin.parameters,
390
- signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
391
411
  return_type: type,
392
412
  comments: init_pin.comments,
393
- closure: init_pin.closure
394
- # @todo Hack to force TypeChecker#internal_or_core?
395
- ).tap { |pin| pin.source = :rbs }
413
+ closure: init_pin.closure,
414
+ source: init_pin.source,
415
+ type_location: init_pin.type_location,
416
+ )
417
+ new_pin.parameters = init_pin.parameters.map do |init_param|
418
+ param = init_param.clone
419
+ param.closure = new_pin
420
+ param.reset_generated!
421
+ param
422
+ end.freeze
423
+ new_pin.signatures = init_pin.signatures.map do |init_sig|
424
+ sig = init_sig.proxy(type)
425
+ sig.parameters = init_sig.parameters.map do |param|
426
+ param = param.clone
427
+ param.closure = new_pin
428
+ param.reset_generated!
429
+ param
430
+ end.freeze
431
+ sig.closure = new_pin
432
+ sig.reset_generated!
433
+ sig
434
+ end.freeze
435
+ new_pin
396
436
  end
397
437
  end
398
438
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
@@ -431,7 +471,7 @@ module Solargraph
431
471
  result = Set.new
432
472
  complex_type.each do |type|
433
473
  if type.duck_type?
434
- result.add Pin::DuckMethod.new(name: type.to_s[1..-1])
474
+ result.add Pin::DuckMethod.new(name: type.to_s[1..-1], source: :api_map)
435
475
  result.merge get_methods('Object')
436
476
  else
437
477
  unless type.nil? || type.name == 'void'
@@ -458,12 +498,24 @@ module Solargraph
458
498
  # @param rooted_tag [String] Parameterized namespace, fully qualified
459
499
  # @param name [String] Method name to look up
460
500
  # @param scope [Symbol] :instance or :class
501
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
502
+ # @param preserve_generics [Boolean]
461
503
  # @return [Array<Solargraph::Pin::Method>]
462
504
  def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
463
505
  rooted_type = ComplexType.parse(rooted_tag)
464
506
  fqns = rooted_type.namespace
465
- namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
466
- methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
507
+ namespace_pin = store.get_path_pins(fqns).first
508
+ methods = if namespace_pin.is_a?(Pin::Constant)
509
+ type = namespace_pin.infer(self)
510
+ if type.defined?
511
+ namespace_pin = store.get_path_pins(type.namespace).first
512
+ get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
513
+ else
514
+ []
515
+ end
516
+ else
517
+ get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
518
+ end
467
519
  methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
468
520
  methods
469
521
  end
@@ -500,13 +552,8 @@ module Solargraph
500
552
  .select { |path| path.downcase.include?(query.downcase) }
501
553
  end
502
554
 
503
- # Get YARD documentation for the specified path.
504
- #
505
- # @example
506
- # api_map.document('String#split')
507
- #
508
- # @todo This method is likely superfluous. Calling get_path_pins directly
509
- # should be sufficient.
555
+ # @deprecated This method is likely superfluous. Calling #get_path_pins
556
+ # directly should be sufficient.
510
557
  #
511
558
  # @param path [String] The path to find
512
559
  # @return [Enumerable<Pin::Base>]
@@ -578,13 +625,22 @@ module Solargraph
578
625
  # @param sub [String] The subclass
579
626
  # @return [Boolean]
580
627
  def super_and_sub?(sup, sub)
581
- fqsup = qualify(sup)
582
- cls = qualify(sub)
583
- tested = []
584
- until fqsup.nil? || cls.nil? || tested.include?(cls)
585
- return true if cls == fqsup
586
- tested.push cls
587
- cls = qualify_superclass(cls)
628
+ sup = ComplexType.try_parse(sup)
629
+ sub = ComplexType.try_parse(sub)
630
+ # @todo If two literals are different values of the same type, it would
631
+ # make more sense for super_and_sub? to return true, but there are a
632
+ # few callers that currently expect this to be false.
633
+ return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
634
+ sup = sup.simplify_literals.to_s
635
+ sub = sub.simplify_literals.to_s
636
+ return true if sup == sub
637
+ sc_fqns = sub
638
+ while (sc = store.get_superclass(sc_fqns))
639
+ sc_new = store.constants.dereference(sc)
640
+ # Cyclical inheritance is invalid
641
+ return false if sc_new == sc_fqns
642
+ sc_fqns = sc_new
643
+ return true if sc_fqns == sup
588
644
  end
589
645
  false
590
646
  end
@@ -597,7 +653,56 @@ module Solargraph
597
653
  #
598
654
  # @return [Boolean]
599
655
  def type_include?(host_ns, module_ns)
600
- store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
656
+ store.get_includes(host_ns).map { |inc_tag| inc_tag.parametrized_tag.name }.include?(module_ns)
657
+ end
658
+
659
+ # @param pins [Enumerable<Pin::Base>]
660
+ # @param visibility [Enumerable<Symbol>]
661
+ # @return [Array<Pin::Base>]
662
+ def resolve_method_aliases pins, visibility = [:public, :private, :protected]
663
+ with_resolved_aliases = pins.map do |pin|
664
+ next pin unless pin.is_a?(Pin::MethodAlias)
665
+ resolved = resolve_method_alias(pin)
666
+ next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
667
+ resolved
668
+ end.compact
669
+ logger.debug { "ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}" }
670
+ GemPins.combine_method_pins_by_path(with_resolved_aliases)
671
+ end
672
+
673
+ # @param fq_reference_tag [String] A fully qualified whose method should be pulled in
674
+ # @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
675
+ # parameter - used to pull generics information
676
+ # @param type [ComplexType] The type which is having its
677
+ # methods supplemented from fq_reference_tag
678
+ # @param scope [Symbol] :class or :instance
679
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
680
+ # @param deep [Boolean]
681
+ # @param skip [Set<String>]
682
+ # @param no_core [Boolean] Skip core classes if true
683
+ # @return [Array<Pin::Base>]
684
+ def inner_get_methods_from_reference(fq_reference_tag, namespace_pin, type, scope, visibility, deep, skip, no_core)
685
+ logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) starting" }
686
+
687
+ # Ensure the types returned by the methods in the referenced
688
+ # type are relative to the generic values passed in the
689
+ # reference. e.g., Foo<String> might include Enumerable<String>
690
+ #
691
+ # @todo perform the same translation in the other areas
692
+ # here after adding a spec and handling things correctly
693
+ # in ApiMap::Store and RbsMap::Conversions for each
694
+ resolved_reference_type = ComplexType.parse(fq_reference_tag).force_rooted.resolve_generics(namespace_pin, type)
695
+ # @todo Can inner_get_methods be cached? Lots of lookups of base types going on.
696
+ methods = inner_get_methods(resolved_reference_type.tag, scope, visibility, deep, skip, no_core)
697
+ if namespace_pin && !resolved_reference_type.all_params.empty?
698
+ reference_pin = store.get_path_pins(resolved_reference_type.name).select { |p| p.is_a?(Pin::Namespace) }.first
699
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolving generics with #{reference_pin.generics}, #{resolved_reference_type.rooted_tags}" }
700
+ methods = methods.map do |method_pin|
701
+ method_pin.resolve_generics(reference_pin, resolved_reference_type)
702
+ end
703
+ end
704
+ # logger.debug { "ApiMap#add_methods_from_reference(type=#{type}) - resolved_reference_type: #{resolved_reference_type} for type=#{type}: #{methods.map(&:name)}" }
705
+ methods
601
706
  end
602
707
 
603
708
  private
@@ -623,6 +728,7 @@ module Solargraph
623
728
  # @param skip [Set<String>]
624
729
  # @param no_core [Boolean] Skip core classes if true
625
730
  # @return [Array<Pin::Base>]
731
+ # rubocop:disable Metrics/CyclomaticComplexity
626
732
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
627
733
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
628
734
  fqns = rooted_type.namespace
@@ -633,9 +739,16 @@ module Solargraph
633
739
  return [] if skip.include?(reqstr)
634
740
  skip.add reqstr
635
741
  result = []
742
+ environ = Convention.for_object(self, rooted_tag, scope, visibility, deep, skip, no_core)
743
+ # ensure we start out with any immediate methods in this
744
+ # namespace so we roughly match the same ordering of get_methods
745
+ # and obey the 'deep' instruction
746
+ direct_convention_methods, convention_methods_by_reference = environ.pins.partition { |p| p.namespace == rooted_tag }
747
+ result.concat direct_convention_methods
748
+
636
749
  if deep && scope == :instance
637
750
  store.get_prepends(fqns).reverse.each do |im|
638
- fqim = qualify(im, fqns)
751
+ fqim = store.constants.dereference(im)
639
752
  result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
640
753
  end
641
754
  end
@@ -643,34 +756,38 @@ module Solargraph
643
756
  # namespaces; resolving the generics in the method pins is this
644
757
  # class' responsibility
645
758
  methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
759
+ 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}" }
646
760
  result.concat methods
647
761
  if deep
762
+ result.concat convention_methods_by_reference
763
+
648
764
  if scope == :instance
649
- store.get_includes(fqns).reverse.each do |include_tag|
650
- rooted_include_tag = qualify(include_tag, rooted_tag)
651
- # Ensure the types returned by the included methods are
652
- # relative to the generics passed to the include. e.g.,
653
- # Foo<String> might include Enumerable<String>
654
- #
655
- # @todo perform the same translation in the other areas
656
- # here after adding a spec and handling things correctly
657
- # in ApiMap::Store and RbsMap::Conversions
658
- resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
659
- methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
660
- result.concat methods
765
+ store.get_includes(fqns).reverse.each do |ref|
766
+ const = get_constants('', *ref.closure.gates).find { |pin| pin.path.end_with? ref.name }
767
+ if const.is_a?(Pin::Namespace)
768
+ result.concat inner_get_methods(const.path, scope, visibility, deep, skip, true)
769
+ elsif const.is_a?(Pin::Constant)
770
+ type = const.infer(self)
771
+ result.concat inner_get_methods(type.namespace, scope, visibility, deep, skip, true) if type.defined?
772
+ else
773
+ referenced_tag = ref.parametrized_tag
774
+ next unless referenced_tag.defined?
775
+ result.concat inner_get_methods_from_reference(referenced_tag.to_s, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
776
+ end
661
777
  end
662
- fqsc = qualify_superclass(fqns)
663
- unless fqsc.nil?
664
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
778
+ rooted_sc_tag = qualify_superclass(rooted_tag)
779
+ unless rooted_sc_tag.nil?
780
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, no_core)
665
781
  end
666
782
  else
783
+ logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
667
784
  store.get_extends(fqns).reverse.each do |em|
668
- fqem = qualify(em, fqns)
785
+ fqem = store.constants.dereference(em)
669
786
  result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
670
787
  end
671
- fqsc = qualify_superclass(fqns)
672
- unless fqsc.nil?
673
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
788
+ rooted_sc_tag = qualify_superclass(rooted_tag)
789
+ unless rooted_sc_tag.nil?
790
+ result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope, visibility, true, skip, true)
674
791
  end
675
792
  unless no_core || fqns.empty?
676
793
  type = get_namespace_type(fqns)
@@ -685,92 +802,17 @@ module Solargraph
685
802
  end
686
803
  result
687
804
  end
688
-
689
- # @param fqns [String]
690
- # @param visibility [Array<Symbol>]
691
- # @param skip [Set<String>]
692
- # @return [Array<Pin::Base>]
693
- def inner_get_constants fqns, visibility, skip
694
- return [] if fqns.nil? || skip.include?(fqns)
695
- skip.add fqns
696
- result = []
697
- store.get_prepends(fqns).each do |is|
698
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
699
- end
700
- result.concat store.get_constants(fqns, visibility)
701
- .sort { |a, b| a.name <=> b.name }
702
- store.get_includes(fqns).each do |is|
703
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
704
- end
705
- fqsc = qualify_superclass(fqns)
706
- unless %w[Object BasicObject].include?(fqsc)
707
- result.concat inner_get_constants(fqsc, [:public], skip)
708
- end
709
- result
710
- end
805
+ # rubocop:enable Metrics/CyclomaticComplexity
711
806
 
712
807
  # @return [Hash]
713
808
  def path_macros
714
809
  @path_macros ||= {}
715
810
  end
716
811
 
717
- # @param namespace [String]
718
- # @param context [String]
812
+ # @param fq_sub_tag [String]
719
813
  # @return [String, nil]
720
- def qualify_lower namespace, context
721
- qualify namespace, context.split('::')[0..-2].join('::')
722
- end
723
-
724
- # @param fqsub [String]
725
- # @return [String, nil]
726
- def qualify_superclass fqsub
727
- sup = store.get_superclass(fqsub)
728
- return nil if sup.nil?
729
- parts = fqsub.split('::')
730
- last = parts.pop
731
- parts.pop if last == sup
732
- qualify(sup, parts.join('::'))
733
- end
734
-
735
- # @param name [String] Namespace to fully qualify
736
- # @param root [String] The context to search
737
- # @param skip [Set<String>] Contexts already searched
738
- # @return [String, nil] Fully qualified ("rooted") namespace
739
- def inner_qualify name, root, skip
740
- return name if name == ComplexType::GENERIC_TAG_NAME
741
- return nil if name.nil?
742
- return nil if skip.include?(root)
743
- skip.add root
744
- possibles = []
745
- if name == ''
746
- if root == ''
747
- return ''
748
- else
749
- return inner_qualify(root, '', skip)
750
- end
751
- else
752
- return name if root == '' && store.namespace_exists?(name)
753
- roots = root.to_s.split('::')
754
- while roots.length > 0
755
- fqns = roots.join('::') + '::' + name
756
- return fqns if store.namespace_exists?(fqns)
757
- incs = store.get_includes(roots.join('::'))
758
- incs.each do |inc|
759
- foundinc = inner_qualify(name, inc, skip)
760
- possibles.push foundinc unless foundinc.nil?
761
- end
762
- roots.pop
763
- end
764
- if possibles.empty?
765
- incs = store.get_includes('')
766
- incs.each do |inc|
767
- foundinc = inner_qualify(name, inc, skip)
768
- possibles.push foundinc unless foundinc.nil?
769
- end
770
- end
771
- return name if store.namespace_exists?(name)
772
- return possibles.last
773
- end
814
+ def qualify_superclass fq_sub_tag
815
+ store.qualify_superclass fq_sub_tag
774
816
  end
775
817
 
776
818
  # Get the namespace's type (Class or Module).
@@ -802,45 +844,79 @@ module Solargraph
802
844
  result + nil_pins
803
845
  end
804
846
 
805
- # @param pins [Enumerable<Pin::Base>]
806
- # @param visibility [Enumerable<Symbol>]
807
- # @return [Array<Pin::Base>]
808
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
809
- pins.map do |pin|
810
- resolved = resolve_method_alias(pin)
811
- next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
812
- resolved
813
- end.compact
847
+ include Logging
848
+
849
+ private
850
+
851
+ # @param alias_pin [Pin::MethodAlias]
852
+ # @return [Pin::Method, nil]
853
+ def resolve_method_alias(alias_pin)
854
+ ancestors = store.get_ancestors(alias_pin.full_context.tag)
855
+ original = nil
856
+
857
+ # Search each ancestor for the original method
858
+ ancestors.each do |ancestor_fqns|
859
+ ancestor_fqns = ComplexType.try_parse(ancestor_fqns).force_rooted.namespace
860
+ next if ancestor_fqns.nil?
861
+ ancestor_method_path = "#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
862
+
863
+ # Search for the original method in the ancestor
864
+ original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
865
+ next if candidate_pin == alias_pin
866
+
867
+ if candidate_pin.is_a?(Pin::MethodAlias)
868
+ # recursively resolve method aliases
869
+ resolved = resolve_method_alias(candidate_pin)
870
+ break resolved if resolved
871
+ end
872
+
873
+ candidate_pin.is_a?(Pin::Method) && candidate_pin.scope == alias_pin.scope
874
+ end
875
+
876
+ break if original
877
+ end
878
+
879
+ # @sg-ignore ignore `received nil` for original
880
+ create_resolved_alias_pin(alias_pin, original) if original
814
881
  end
815
882
 
816
- # @param pin [Pin::MethodAlias, Pin::Base]
817
- # @return [Pin::Method]
818
- def resolve_method_alias pin
819
- return pin unless pin.is_a?(Pin::MethodAlias)
820
- return nil if @method_alias_stack.include?(pin.path)
821
- @method_alias_stack.push pin.path
822
- origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
823
- @method_alias_stack.pop
824
- return nil if origin.nil?
883
+ # Fast path for creating resolved alias pins without individual method stack lookups
884
+ # @param alias_pin [Pin::MethodAlias] The alias pin to resolve
885
+ # @param original [Pin::Method] The original method pin that was already found
886
+ # @return [Pin::Method] The resolved method pin
887
+ def create_resolved_alias_pin(alias_pin, original)
888
+ # Build the resolved method pin directly (same logic as resolve_method_alias but without lookup)
825
889
  args = {
826
- location: pin.location,
827
- closure: pin.closure,
828
- name: pin.name,
829
- comments: origin.comments,
830
- scope: origin.scope,
831
- # context: pin.context,
832
- visibility: origin.visibility,
833
- signatures: origin.signatures,
834
- attribute: origin.attribute?,
835
- generics: origin.generics,
836
- return_type: origin.return_type,
890
+ location: alias_pin.location,
891
+ type_location: original.type_location,
892
+ closure: alias_pin.closure,
893
+ name: alias_pin.name,
894
+ comments: original.comments,
895
+ scope: original.scope,
896
+ visibility: original.visibility,
897
+ signatures: original.signatures.map(&:clone).freeze,
898
+ attribute: original.attribute?,
899
+ generics: original.generics.clone,
900
+ return_type: original.return_type,
901
+ source: :resolve_method_alias
837
902
  }
838
- Pin::Method.new **args
839
- end
840
-
841
- include Logging
903
+ resolved_pin = Pin::Method.new **args
904
+
905
+ # Clone signatures and parameters
906
+ resolved_pin.signatures.each do |sig|
907
+ sig.parameters = sig.parameters.map(&:clone).freeze
908
+ sig.source = :resolve_method_alias
909
+ sig.parameters.each do |param|
910
+ param.closure = resolved_pin
911
+ param.source = :resolve_method_alias
912
+ param.reset_generated!
913
+ end
914
+ sig.closure = resolved_pin
915
+ sig.reset_generated!
916
+ end
842
917
 
843
- private
918
+ resolved_pin
919
+ end
844
920
 
845
921
  # @param namespace_pin [Pin::Namespace]
846
922
  # @param rooted_type [ComplexType]
@@ -861,9 +937,9 @@ module Solargraph
861
937
  has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
862
938
  end
863
939
 
864
- # @param namespace_pin [Pin::Namespace]
940
+ # @param namespace_pin [Pin::Namespace, Pin::Constant]
865
941
  def has_generics?(namespace_pin)
866
- namespace_pin && !namespace_pin.generics.empty?
942
+ namespace_pin.is_a?(Pin::Namespace) && !namespace_pin.generics.empty?
867
943
  end
868
944
 
869
945
  # @param namespace_pin [Pin::Namespace]