solargraph 0.57.0 → 0.58.3

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/linting.yml +4 -2
  4. data/.github/workflows/plugins.yml +63 -28
  5. data/.github/workflows/rspec.yml +19 -4
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.gitignore +1 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +90 -1438
  10. data/CHANGELOG.md +39 -0
  11. data/Rakefile +1 -1
  12. data/bin/solargraph +3 -0
  13. data/lib/solargraph/api_map/cache.rb +110 -110
  14. data/lib/solargraph/api_map/constants.rb +279 -218
  15. data/lib/solargraph/api_map/index.rb +193 -169
  16. data/lib/solargraph/api_map/source_to_yard.rb +97 -94
  17. data/lib/solargraph/api_map/store.rb +384 -374
  18. data/lib/solargraph/api_map.rb +945 -951
  19. data/lib/solargraph/bench.rb +45 -45
  20. data/lib/solargraph/complex_type/type_methods.rb +228 -223
  21. data/lib/solargraph/complex_type/unique_type.rb +482 -475
  22. data/lib/solargraph/complex_type.rb +444 -427
  23. data/lib/solargraph/convention/active_support_concern.rb +1 -1
  24. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  25. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
  26. data/lib/solargraph/convention/data_definition.rb +105 -105
  27. data/lib/solargraph/convention/gemfile.rb +15 -15
  28. data/lib/solargraph/convention/gemspec.rb +23 -23
  29. data/lib/solargraph/convention/rakefile.rb +17 -17
  30. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
  31. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
  32. data/lib/solargraph/convention/struct_definition.rb +164 -164
  33. data/lib/solargraph/convention.rb +78 -78
  34. data/lib/solargraph/converters/dd.rb +17 -17
  35. data/lib/solargraph/converters/dl.rb +15 -15
  36. data/lib/solargraph/converters/dt.rb +15 -15
  37. data/lib/solargraph/converters/misc.rb +1 -1
  38. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  39. data/lib/solargraph/diagnostics/rubocop.rb +118 -118
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -66
  41. data/lib/solargraph/diagnostics/type_check.rb +55 -55
  42. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  43. data/lib/solargraph/doc_map.rb +439 -436
  44. data/lib/solargraph/environ.rb +1 -1
  45. data/lib/solargraph/equality.rb +34 -33
  46. data/lib/solargraph/gem_pins.rb +98 -94
  47. data/lib/solargraph/language_server/error_codes.rb +20 -20
  48. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  49. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  50. data/lib/solargraph/language_server/host/message_worker.rb +112 -112
  51. data/lib/solargraph/language_server/host/sources.rb +99 -99
  52. data/lib/solargraph/language_server/host.rb +878 -872
  53. data/lib/solargraph/language_server/message/base.rb +97 -97
  54. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  55. data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -60
  56. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
  57. data/lib/solargraph/language_server/message/extended/document.rb +23 -23
  58. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  59. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -19
  60. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  61. data/lib/solargraph/language_server/message/initialize.rb +191 -191
  62. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
  63. data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
  64. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  65. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
  66. data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -145
  67. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  68. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  69. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  70. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  71. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
  72. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
  73. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -35
  74. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -40
  75. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +26 -26
  76. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  77. data/lib/solargraph/language_server/message.rb +94 -94
  78. data/lib/solargraph/language_server/progress.rb +1 -1
  79. data/lib/solargraph/language_server/request.rb +27 -25
  80. data/lib/solargraph/language_server/transport/data_reader.rb +74 -74
  81. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  82. data/lib/solargraph/library.rb +683 -683
  83. data/lib/solargraph/location.rb +82 -81
  84. data/lib/solargraph/logging.rb +37 -37
  85. data/lib/solargraph/page.rb +92 -93
  86. data/lib/solargraph/parser/comment_ripper.rb +69 -69
  87. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
  88. data/lib/solargraph/parser/node_processor/base.rb +92 -92
  89. data/lib/solargraph/parser/node_processor.rb +62 -62
  90. data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -159
  91. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  92. data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
  93. data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -499
  94. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -21
  95. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
  96. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
  97. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
  98. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
  99. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +37 -37
  100. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
  101. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
  102. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
  103. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
  104. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -42
  105. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  106. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
  107. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -43
  108. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -292
  109. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  110. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
  111. data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
  112. data/lib/solargraph/parser/parser_gem.rb +12 -12
  113. data/lib/solargraph/parser/region.rb +69 -69
  114. data/lib/solargraph/parser/snippet.rb +17 -17
  115. data/lib/solargraph/parser.rb +23 -23
  116. data/lib/solargraph/pin/base.rb +729 -708
  117. data/lib/solargraph/pin/base_variable.rb +126 -124
  118. data/lib/solargraph/pin/block.rb +104 -103
  119. data/lib/solargraph/pin/breakable.rb +9 -9
  120. data/lib/solargraph/pin/callable.rb +231 -227
  121. data/lib/solargraph/pin/closure.rb +72 -76
  122. data/lib/solargraph/pin/common.rb +79 -79
  123. data/lib/solargraph/pin/constant.rb +45 -45
  124. data/lib/solargraph/pin/conversions.rb +123 -123
  125. data/lib/solargraph/pin/delegated_method.rb +120 -121
  126. data/lib/solargraph/pin/documenting.rb +114 -114
  127. data/lib/solargraph/pin/instance_variable.rb +34 -34
  128. data/lib/solargraph/pin/keyword.rb +20 -20
  129. data/lib/solargraph/pin/local_variable.rb +75 -79
  130. data/lib/solargraph/pin/method.rb +672 -656
  131. data/lib/solargraph/pin/method_alias.rb +34 -34
  132. data/lib/solargraph/pin/namespace.rb +115 -115
  133. data/lib/solargraph/pin/parameter.rb +275 -271
  134. data/lib/solargraph/pin/proxy_type.rb +39 -36
  135. data/lib/solargraph/pin/reference/override.rb +47 -47
  136. data/lib/solargraph/pin/reference/superclass.rb +15 -15
  137. data/lib/solargraph/pin/reference.rb +39 -48
  138. data/lib/solargraph/pin/search.rb +61 -58
  139. data/lib/solargraph/pin/signature.rb +61 -61
  140. data/lib/solargraph/pin/symbol.rb +53 -53
  141. data/lib/solargraph/pin/until.rb +18 -18
  142. data/lib/solargraph/pin/while.rb +18 -18
  143. data/lib/solargraph/pin.rb +44 -44
  144. data/lib/solargraph/pin_cache.rb +245 -245
  145. data/lib/solargraph/position.rb +132 -118
  146. data/lib/solargraph/range.rb +112 -108
  147. data/lib/solargraph/rbs_map/conversions.rb +823 -802
  148. data/lib/solargraph/rbs_map/core_fills.rb +84 -66
  149. data/lib/solargraph/rbs_map/core_map.rb +58 -54
  150. data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
  151. data/lib/solargraph/rbs_map.rb +163 -163
  152. data/lib/solargraph/server_methods.rb +16 -16
  153. data/lib/solargraph/shell.rb +363 -271
  154. data/lib/solargraph/source/chain/array.rb +37 -37
  155. data/lib/solargraph/source/chain/call.rb +337 -333
  156. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  157. data/lib/solargraph/source/chain/constant.rb +26 -89
  158. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  159. data/lib/solargraph/source/chain/hash.rb +34 -34
  160. data/lib/solargraph/source/chain/if.rb +28 -28
  161. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/link.rb +109 -109
  163. data/lib/solargraph/source/chain/literal.rb +48 -48
  164. data/lib/solargraph/source/chain/or.rb +23 -23
  165. data/lib/solargraph/source/chain/q_call.rb +11 -11
  166. data/lib/solargraph/source/chain/variable.rb +13 -13
  167. data/lib/solargraph/source/chain/z_super.rb +30 -30
  168. data/lib/solargraph/source/chain.rb +291 -289
  169. data/lib/solargraph/source/change.rb +82 -82
  170. data/lib/solargraph/source/cursor.rb +166 -166
  171. data/lib/solargraph/source/encoding_fixes.rb +23 -23
  172. data/lib/solargraph/source/source_chainer.rb +194 -194
  173. data/lib/solargraph/source/updater.rb +55 -55
  174. data/lib/solargraph/source.rb +498 -498
  175. data/lib/solargraph/source_map/clip.rb +226 -234
  176. data/lib/solargraph/source_map/data.rb +34 -34
  177. data/lib/solargraph/source_map/mapper.rb +259 -261
  178. data/lib/solargraph/source_map.rb +212 -207
  179. data/lib/solargraph/type_checker/checks.rb +124 -124
  180. data/lib/solargraph/type_checker/param_def.rb +37 -37
  181. data/lib/solargraph/type_checker/problem.rb +32 -32
  182. data/lib/solargraph/type_checker/rules.rb +84 -70
  183. data/lib/solargraph/type_checker.rb +814 -752
  184. data/lib/solargraph/version.rb +5 -5
  185. data/lib/solargraph/workspace/config.rb +255 -237
  186. data/lib/solargraph/workspace/require_paths.rb +97 -98
  187. data/lib/solargraph/workspace.rb +220 -225
  188. data/lib/solargraph/yard_map/helpers.rb +44 -44
  189. data/lib/solargraph/yard_map/mapper/to_method.rb +130 -129
  190. data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -30
  191. data/lib/solargraph/yard_map/mapper.rb +79 -79
  192. data/lib/solargraph/yard_map/to_method.rb +89 -88
  193. data/lib/solargraph/yard_tags.rb +20 -20
  194. data/lib/solargraph/yardoc.rb +87 -64
  195. data/lib/solargraph.rb +105 -105
  196. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  197. data/rbs/fills/open3/0/open3.rbs +172 -0
  198. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  199. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  200. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  201. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/ast/2.4/.rbs_meta.yaml +9 -0
  204. data/rbs/shims/ast/2.4/ast.rbs +73 -0
  205. data/rbs/shims/parser/3.2.0.1/manifest.yaml +7 -0
  206. data/rbs/shims/parser/3.2.0.1/parser.rbs +201 -0
  207. data/rbs/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  208. data/rbs_collection.yaml +4 -4
  209. data/solargraph.gemspec +15 -4
  210. metadata +71 -16
  211. data/lib/solargraph/parser/node_methods.rb +0 -97
  212. /data/rbs/fills/{tuple.rbs → tuple/tuple.rbs} +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  215. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  216. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,656 +1,672 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Pin
5
- # The base class for method and attribute pins.
6
- #
7
- class Method < Callable
8
- include Solargraph::Parser::NodeMethods
9
-
10
- # @return [::Symbol] :public, :private, or :protected
11
- attr_reader :visibility
12
-
13
- attr_writer :signatures
14
-
15
- # @return [Parser::AST::Node]
16
- attr_reader :node
17
-
18
- # @param visibility [::Symbol] :public, :protected, or :private
19
- # @param explicit [Boolean]
20
- # @param block [Pin::Signature, nil, ::Symbol]
21
- # @param node [Parser::AST::Node, nil]
22
- # @param attribute [Boolean]
23
- # @param signatures [::Array<Signature>, nil]
24
- # @param anon_splat [Boolean]
25
- def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false,
26
- **splat
27
- super(**splat)
28
- @visibility = visibility
29
- @explicit = explicit
30
- @block = block
31
- @node = node
32
- @attribute = attribute
33
- @signatures = signatures
34
- @anon_splat = anon_splat
35
- end
36
-
37
- # @return [Array<Pin::Signature>]
38
- def combine_all_signature_pins(*signature_pins)
39
- by_arity = {}
40
- signature_pins.each do |signature_pin|
41
- by_arity[signature_pin.arity] ||= []
42
- by_arity[signature_pin.arity] << signature_pin
43
- end
44
- by_arity.transform_values! do |same_arity_pins|
45
- same_arity_pins.reduce(nil) do |memo, signature|
46
- next signature if memo.nil?
47
- memo.combine_with(signature)
48
- end
49
- end
50
- by_arity.values.flatten
51
- end
52
-
53
- # @param other [Pin::Method]
54
- # @return [::Symbol]
55
- def combine_visibility(other)
56
- if dodgy_visibility_source? && !other.dodgy_visibility_source?
57
- other.visibility
58
- elsif other.dodgy_visibility_source? && !dodgy_visibility_source?
59
- visibility
60
- else
61
- assert_same(other, :visibility)
62
- end
63
- end
64
-
65
- # @param other [Pin::Method]
66
- # @return [Array<Pin::Signature>]
67
- def combine_signatures(other)
68
- all_undefined = signatures.all? { |sig| sig.return_type.undefined? }
69
- other_all_undefined = other.signatures.all? { |sig| sig.return_type.undefined? }
70
- if all_undefined && !other_all_undefined
71
- other.signatures
72
- elsif other_all_undefined && !all_undefined
73
- signatures
74
- else
75
- combine_all_signature_pins(*signatures, *other.signatures)
76
- end
77
- end
78
-
79
- def combine_with(other, attrs = {})
80
- priority_choice = choose_priority(other)
81
- return priority_choice unless priority_choice.nil?
82
-
83
- sigs = combine_signatures(other)
84
- parameters = if sigs.length > 0
85
- [].freeze
86
- else
87
- choose(other, :parameters).clone.freeze
88
- end
89
- new_attrs = {
90
- visibility: combine_visibility(other),
91
- explicit: explicit? || other.explicit?,
92
- block: combine_blocks(other),
93
- node: choose_node(other, :node),
94
- attribute: prefer_rbs_location(other, :attribute?),
95
- parameters: parameters,
96
- signatures: sigs,
97
- anon_splat: assert_same(other, :anon_splat?),
98
- return_type: nil # pulled from signatures on first call
99
- }.merge(attrs)
100
- super(other, new_attrs)
101
- end
102
-
103
- # @param other [Pin::Method]
104
- def == other
105
- super && other.node == node
106
- end
107
-
108
- def transform_types(&transform)
109
- # @todo 'super' alone should work here I think, but doesn't typecheck at level typed
110
- m = super(&transform)
111
- m.signatures = m.signatures.map do |sig|
112
- sig.transform_types(&transform)
113
- end
114
- m.block = block&.transform_types(&transform)
115
- m.reset_generated!
116
- m
117
- end
118
-
119
- # @return [void]
120
- def reset_generated!
121
- super
122
- unless signatures.empty?
123
- return_type = nil
124
- @block = :undefined
125
- parameters = []
126
- end
127
- block&.reset_generated!
128
- @signatures&.each(&:reset_generated!)
129
- signature_help = nil
130
- documentation = nil
131
- end
132
-
133
- def all_rooted?
134
- super && parameters.all?(&:all_rooted?) && (!block || block&.all_rooted?) && signatures.all?(&:all_rooted?)
135
- end
136
-
137
- # @param signature [Pin::Signature]
138
- # @return [Pin::Method]
139
- def with_single_signature(signature)
140
- m = proxy signature.return_type
141
- m.reset_generated!
142
- # @todo populating the single parameters/return_type/block
143
- # arguments here seems to be needed for some specs to pass,
144
- # even though we have a signature with the same information.
145
- # Is this a problem for RBS-populated methods, which don't
146
- # populate these three?
147
- m.parameters = signature.parameters
148
- m.return_type = signature.return_type
149
- m.block = signature.block
150
- m.signatures = [signature]
151
- m
152
- end
153
-
154
- def block?
155
- !block.nil?
156
- end
157
-
158
- # @return [Pin::Signature, nil]
159
- def block
160
- return @block unless @block == :undefined
161
- @block = signatures.first&.block
162
- end
163
-
164
- def completion_item_kind
165
- attribute? ? Solargraph::LanguageServer::CompletionItemKinds::PROPERTY : Solargraph::LanguageServer::CompletionItemKinds::METHOD
166
- end
167
-
168
- def symbol_kind
169
- attribute? ? Solargraph::LanguageServer::SymbolKinds::PROPERTY : LanguageServer::SymbolKinds::METHOD
170
- end
171
-
172
- def return_type
173
- @return_type ||= ComplexType.new(signatures.map(&:return_type).flat_map(&:items))
174
- end
175
-
176
- # @param parameters [::Array<Parameter>]
177
- # @param return_type [ComplexType]
178
- # @return [Signature]
179
- def generate_signature(parameters, return_type)
180
- block = nil
181
- yieldparam_tags = docstring.tags(:yieldparam)
182
- yieldreturn_tags = docstring.tags(:yieldreturn)
183
- generics = docstring.tags(:generic).map(&:name)
184
- needs_block_param_signature =
185
- parameters.last&.block? || !yieldreturn_tags.empty? || !yieldparam_tags.empty?
186
- if needs_block_param_signature
187
- yield_parameters = yieldparam_tags.map do |p|
188
- name = p.name
189
- decl = :arg
190
- if name
191
- decl = select_decl(name, false)
192
- name = clean_param(name)
193
- end
194
- Pin::Parameter.new(
195
- location: location,
196
- closure: self,
197
- comments: p.text,
198
- name: name,
199
- decl: decl,
200
- presence: location ? location.range : nil,
201
- return_type: ComplexType.try_parse(*p.types),
202
- source: source
203
- )
204
- end
205
- yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types))
206
- block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source,
207
- closure: self, location: location, type_location: type_location)
208
- end
209
- signature = Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, closure: self, source: source,
210
- location: location, type_location: type_location)
211
- block.closure = signature if block
212
- signature
213
- end
214
-
215
- # @return [::Array<Signature>]
216
- def signatures
217
- @signatures ||= begin
218
- top_type = generate_complex_type
219
- result = []
220
- result.push generate_signature(parameters, top_type) if top_type.defined?
221
- result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty?
222
- result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
223
- result
224
- end
225
- end
226
-
227
- # @param return_type [ComplexType]
228
- # @return [self]
229
- def proxy_with_signatures return_type
230
- out = proxy return_type
231
- out.signatures = out.signatures.map { |sig| sig.proxy return_type }
232
- out
233
- end
234
-
235
- # @return [String, nil]
236
- def detail
237
- # This property is not cached in an instance variable because it can
238
- # change when pins get proxied.
239
- detail = String.new
240
- detail += if signatures.length > 1
241
- "(*) "
242
- else
243
- "(#{signatures.first.parameters.map(&:full).join(', ')}) " unless signatures.first.parameters.empty?
244
- end.to_s
245
- detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined?
246
- detail.strip!
247
- return nil if detail.empty?
248
- detail
249
- end
250
-
251
- # @return [::Array<Hash>]
252
- def signature_help
253
- @signature_help ||= signatures.map do |sig|
254
- {
255
- label: name + '(' + sig.parameters.map(&:full).join(', ') + ')',
256
- documentation: documentation
257
- }
258
- end
259
- end
260
-
261
- def inner_desc
262
- # ensure the signatures line up when logged
263
- if signatures.length > 1
264
- path + " \n#{to_rbs}\n"
265
- else
266
- super
267
- end
268
- end
269
-
270
- def to_rbs
271
- return nil if signatures.empty?
272
-
273
- rbs = "def #{name}: #{signatures.first.to_rbs}"
274
- signatures[1..].each do |sig|
275
- rbs += "\n"
276
- rbs += (' ' * (4 + name.length))
277
- rbs += "| #{name}: #{sig.to_rbs}"
278
- end
279
- rbs
280
- end
281
-
282
- def path
283
- @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}"
284
- end
285
-
286
- # @return [String]
287
- def method_name
288
- name
289
- end
290
-
291
- def typify api_map
292
- logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context.rooted_tags}, return_type=#{return_type.rooted_tags}) - starting" }
293
- decl = super
294
- unless decl.undefined?
295
- logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context}) => #{decl.rooted_tags.inspect} - decl found" }
296
- return decl
297
- end
298
- type = see_reference(api_map) || typify_from_super(api_map)
299
- logger.debug { "Method#typify(self=#{self}) - type=#{type&.rooted_tags.inspect}" }
300
- unless type.nil?
301
- qualified = type.qualify(api_map, namespace)
302
- logger.debug { "Method#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" }
303
- return qualified
304
- end
305
- super
306
- end
307
-
308
- def documentation
309
- if @documentation.nil?
310
- method_docs ||= super || ''
311
- param_tags = docstring.tags(:param)
312
- unless param_tags.nil? or param_tags.empty?
313
- method_docs += "\n\n" unless method_docs.empty?
314
- method_docs += "Params:\n"
315
- lines = []
316
- param_tags.each do |p|
317
- l = "* #{p.name}"
318
- l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty?
319
- l += " #{p.text}"
320
- lines.push l
321
- end
322
- method_docs += lines.join("\n")
323
- end
324
- yieldparam_tags = docstring.tags(:yieldparam)
325
- unless yieldparam_tags.nil? or yieldparam_tags.empty?
326
- method_docs += "\n\n" unless method_docs.empty?
327
- method_docs += "Block Params:\n"
328
- lines = []
329
- yieldparam_tags.each do |p|
330
- l = "* #{p.name}"
331
- l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty?
332
- l += " #{p.text}"
333
- lines.push l
334
- end
335
- method_docs += lines.join("\n")
336
- end
337
- yieldreturn_tags = docstring.tags(:yieldreturn)
338
- unless yieldreturn_tags.empty?
339
- method_docs += "\n\n" unless method_docs.empty?
340
- method_docs += "Block Returns:\n"
341
- lines = []
342
- yieldreturn_tags.each do |r|
343
- l = "*"
344
- l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty?
345
- l += " #{r.text}"
346
- lines.push l
347
- end
348
- method_docs += lines.join("\n")
349
- end
350
- return_tags = docstring.tags(:return)
351
- unless return_tags.empty?
352
- method_docs += "\n\n" unless method_docs.empty?
353
- method_docs += "Returns:\n"
354
- lines = []
355
- return_tags.each do |r|
356
- l = "*"
357
- l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty?
358
- l += " #{r.text}"
359
- lines.push l
360
- end
361
- method_docs += lines.join("\n")
362
- end
363
- method_docs += "\n\n" unless method_docs.empty?
364
- method_docs += "Visibility: #{visibility}"
365
- @documentation = method_docs
366
- concat_example_tags
367
- end
368
- @documentation.to_s
369
- end
370
-
371
- def explicit?
372
- @explicit
373
- end
374
-
375
- def attribute?
376
- @attribute
377
- end
378
-
379
- # @parm other [Method]
380
- def nearly? other
381
- super &&
382
- parameters == other.parameters &&
383
- scope == other.scope &&
384
- visibility == other.visibility
385
- end
386
-
387
- def probe api_map
388
- attribute? ? infer_from_iv(api_map) : infer_from_return_nodes(api_map)
389
- end
390
-
391
- # @return [::Array<Pin::Method>]
392
- def overloads
393
- # Ignore overload tags with nil parameters. If it's not an array, the
394
- # tag's source is likely malformed.
395
- @overloads ||= docstring.tags(:overload).select(&:parameters).map do |tag|
396
- Pin::Signature.new(
397
- generics: generics,
398
- parameters: tag.parameters.map do |src|
399
- name, decl = parse_overload_param(src.first)
400
- Pin::Parameter.new(
401
- location: location,
402
- closure: self,
403
- comments: tag.docstring.all.to_s,
404
- name: name,
405
- decl: decl,
406
- presence: location ? location.range : nil,
407
- return_type: param_type_from_name(tag, src.first),
408
- source: :overloads
409
- )
410
- end,
411
- closure: self,
412
- return_type: ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)),
413
- source: :overloads,
414
- )
415
- end
416
- @overloads
417
- end
418
-
419
- def anon_splat?
420
- @anon_splat
421
- end
422
-
423
- # @param api_map [ApiMap]
424
- # @return [self]
425
- def resolve_ref_tag api_map
426
- return self if @resolved_ref_tag
427
-
428
- @resolved_ref_tag = true
429
- return self unless docstring.ref_tags.any?
430
- docstring.ref_tags.each do |tag|
431
- ref = if tag.owner.to_s.start_with?(/[#.]/)
432
- api_map.get_methods(namespace)
433
- .select { |pin| pin.path.end_with?(tag.owner.to_s) }
434
- .first
435
- else
436
- # @todo Resolve relative namespaces
437
- api_map.get_path_pins(tag.owner.to_s).first
438
- end
439
- next unless ref
440
-
441
- docstring.add_tag(*ref.docstring.tags(:param))
442
- end
443
- self
444
- end
445
-
446
- # @param api_map [ApiMap]
447
- # @return [Array<Pin::Method>]
448
- def rest_of_stack api_map
449
- api_map.get_method_stack(method_namespace, method_name, scope: scope).reject { |pin| pin.path == path }
450
- end
451
-
452
- protected
453
-
454
- attr_writer :block
455
-
456
- attr_writer :signature_help
457
-
458
- attr_writer :documentation
459
-
460
- def dodgy_visibility_source?
461
- # as of 2025-03-12, the RBS generator used for
462
- # e.g. activesupport did not understand 'private' markings
463
- # inside 'class << self' blocks, but YARD did OK at it
464
- source == :rbs && scope == :class && type_location&.filename&.include?('generated') && return_type.undefined? ||
465
- # YARD's RBS generator seems to miss a lot of should-be protected instance methods
466
- source == :rbs && scope == :instance && namespace.start_with?('YARD::') ||
467
- # private on attr_readers seems to be broken in Prism's auto-generator script
468
- source == :rbs && scope == :instance && namespace.start_with?('Prism::') ||
469
- # The RBS for the RBS gem itself seems to use private as a
470
- # 'is this a public API' concept, more aggressively than the
471
- # actual code. Let's respect that and ignore the actual .rb file.
472
- source == :yardoc && scope == :instance && namespace.start_with?('RBS::')
473
- end
474
-
475
- private
476
-
477
- # @param name [String]
478
- # @param asgn [Boolean]
479
- #
480
- # @return [::Symbol]
481
- def select_decl name, asgn
482
- if name.start_with?('**')
483
- :kwrestarg
484
- elsif name.start_with?('*')
485
- :restarg
486
- elsif name.start_with?('&')
487
- :blockarg
488
- elsif name.end_with?(':') && asgn
489
- :kwoptarg
490
- elsif name.end_with?(':')
491
- :kwarg
492
- elsif asgn
493
- :optarg
494
- else
495
- :arg
496
- end
497
- end
498
-
499
- # @param name [String]
500
- # @return [String]
501
- def clean_param name
502
- name.gsub(/[*&:]/, '')
503
- end
504
-
505
- # @param tag [YARD::Tags::OverloadTag]
506
- # @param name [String]
507
- #
508
- # @return [ComplexType]
509
- def param_type_from_name(tag, name)
510
- param = tag.tags(:param).select { |t| t.name == name }.first
511
- return ComplexType::UNDEFINED unless param
512
- ComplexType.try_parse(*param.types)
513
- end
514
-
515
- # @return [ComplexType]
516
- def generate_complex_type
517
- tags = docstring.tags(:return).map(&:types).flatten.compact
518
- return ComplexType::UNDEFINED if tags.empty?
519
- ComplexType.try_parse *tags
520
- end
521
-
522
- # @param api_map [ApiMap]
523
- # @return [ComplexType, nil]
524
- def see_reference api_map
525
- docstring.ref_tags.each do |ref|
526
- next unless ref.tag_name == 'return' && ref.owner
527
- result = resolve_reference(ref.owner.to_s, api_map)
528
- return result unless result.nil?
529
- end
530
- match = comments.match(/^[ \t]*\(see (.*)\)/m)
531
- return nil if match.nil?
532
- resolve_reference match[1], api_map
533
- end
534
-
535
- # @return [String]
536
- def method_namespace
537
- namespace
538
- end
539
-
540
- # @param api_map [ApiMap]
541
- # @return [ComplexType, nil]
542
- def typify_from_super api_map
543
- stack = rest_of_stack api_map
544
- return nil if stack.empty?
545
- stack.each do |pin|
546
- return pin.return_type unless pin.return_type.undefined?
547
- end
548
- nil
549
- end
550
-
551
- # @param ref [String]
552
- # @param api_map [ApiMap]
553
- # @return [ComplexType, nil]
554
- def resolve_reference ref, api_map
555
- parts = ref.split(/[.#]/)
556
- if parts.first.empty? || parts.one?
557
- path = "#{namespace}#{ref}"
558
- else
559
- fqns = api_map.qualify(parts.first, namespace)
560
- return ComplexType::UNDEFINED if fqns.nil?
561
- path = fqns + ref[parts.first.length] + parts.last
562
- end
563
- pins = api_map.get_path_pins(path)
564
- pins.each do |pin|
565
- type = pin.typify(api_map)
566
- return type unless type.undefined?
567
- end
568
- nil
569
- end
570
-
571
- # @return [Parser::AST::Node, nil]
572
- def method_body_node
573
- return nil if node.nil?
574
- return node.children[1].children.last if node.type == :DEFN
575
- return node.children[2].children.last if node.type == :DEFS
576
- return node.children[2] if node.type == :def || node.type == :DEFS
577
- return node.children[3] if node.type == :defs
578
- nil
579
- end
580
-
581
- # @param api_map [ApiMap]
582
- # @return [ComplexType]
583
- def infer_from_return_nodes api_map
584
- return ComplexType::UNDEFINED if node.nil?
585
- result = []
586
- has_nil = false
587
- return ComplexType::NIL if method_body_node.nil?
588
- returns_from_method_body(method_body_node).each do |n|
589
- if n.nil? || [:NIL, :nil].include?(n.type)
590
- has_nil = true
591
- next
592
- end
593
- rng = Range.from_node(n)
594
- next unless rng
595
- clip = api_map.clip_at(
596
- location.filename,
597
- rng.ending
598
- )
599
- chain = Solargraph::Parser.chain(n, location.filename)
600
- type = chain.infer(api_map, self, clip.locals)
601
- result.push type unless type.undefined?
602
- end
603
- result.push ComplexType::NIL if has_nil
604
- return ComplexType::UNDEFINED if result.empty?
605
- ComplexType.new(result.uniq)
606
- end
607
-
608
- # @param [ApiMap] api_map
609
- # @return [ComplexType]
610
- def infer_from_iv api_map
611
- types = []
612
- varname = "@#{name.gsub(/=$/, '')}"
613
- pins = api_map.get_instance_variable_pins(binder.namespace, binder.scope).select { |iv| iv.name == varname }
614
- pins.each do |pin|
615
- type = pin.typify(api_map)
616
- type = pin.probe(api_map) if type.undefined?
617
- types.push type if type.defined?
618
- end
619
- return ComplexType::UNDEFINED if types.empty?
620
- ComplexType.new(types.uniq)
621
- end
622
-
623
- # When YARD parses an overload tag, it includes rest modifiers in the parameters names.
624
- #
625
- # @param name [String]
626
- # @return [::Array(String, ::Symbol)]
627
- def parse_overload_param(name)
628
- # @todo this needs to handle mandatory vs not args, kwargs, blocks, etc
629
- if name.start_with?('**')
630
- [name[2..-1], :kwrestarg]
631
- elsif name.start_with?('*')
632
- [name[1..-1], :restarg]
633
- else
634
- [name, :arg]
635
- end
636
- end
637
-
638
- # @return [void]
639
- def concat_example_tags
640
- example_tags = docstring.tags(:example)
641
- return if example_tags.empty?
642
- @documentation += "\n\nExamples:\n\n```ruby\n"
643
- @documentation += example_tags.map do |tag|
644
- (tag.name && !tag.name.empty? ? "# #{tag.name}\n" : '') +
645
- "#{tag.text}\n"
646
- end
647
- .join("\n")
648
- .concat("```\n")
649
- end
650
-
651
- protected
652
-
653
- attr_writer :return_type
654
- end
655
- end
656
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Pin
5
+ # The base class for method and attribute pins.
6
+ #
7
+ class Method < Callable
8
+ include Solargraph::Parser::NodeMethods
9
+
10
+ # @return [::Symbol] :public, :private, or :protected
11
+ attr_reader :visibility
12
+
13
+ attr_writer :signatures
14
+
15
+ # @return [Parser::AST::Node]
16
+ attr_reader :node
17
+
18
+ # @param visibility [::Symbol] :public, :protected, or :private
19
+ # @param explicit [Boolean]
20
+ # @param block [Pin::Signature, nil, :undefined]
21
+ # @param node [Parser::AST::Node, nil]
22
+ # @param attribute [Boolean]
23
+ # @param signatures [::Array<Signature>, nil]
24
+ # @param anon_splat [Boolean]
25
+ def initialize visibility: :public, explicit: true, block: :undefined, node: nil, attribute: false, signatures: nil, anon_splat: false,
26
+ **splat
27
+ super(**splat)
28
+ @visibility = visibility
29
+ @explicit = explicit
30
+ @block = block
31
+ @node = node
32
+ @attribute = attribute
33
+ @signatures = signatures
34
+ @anon_splat = anon_splat
35
+ end
36
+
37
+ # @param signature_pins [Array<Pin::Signature>]
38
+ # @return [Array<Pin::Signature>]
39
+ def combine_all_signature_pins(*signature_pins)
40
+ # @type [Hash{Array => Array<Pin::Signature>}]
41
+ by_arity = {}
42
+ signature_pins.each do |signature_pin|
43
+ by_arity[signature_pin.arity] ||= []
44
+ by_arity[signature_pin.arity] << signature_pin
45
+ end
46
+ by_arity.transform_values! do |same_arity_pins|
47
+ # @param memo [Pin::Signature, nil]
48
+ # @param signature [Pin::Signature]
49
+ same_arity_pins.reduce(nil) do |memo, signature|
50
+ next signature if memo.nil?
51
+ memo.combine_with(signature)
52
+ end
53
+ end
54
+ by_arity.values.flatten
55
+ end
56
+
57
+ # @param other [Pin::Method]
58
+ # @return [::Symbol]
59
+ def combine_visibility(other)
60
+ if dodgy_visibility_source? && !other.dodgy_visibility_source?
61
+ other.visibility
62
+ elsif other.dodgy_visibility_source? && !dodgy_visibility_source?
63
+ visibility
64
+ else
65
+ assert_same(other, :visibility)
66
+ end
67
+ end
68
+
69
+ # @param other [Pin::Method]
70
+ # @return [Array<Pin::Signature>]
71
+ def combine_signatures(other)
72
+ all_undefined = signatures.all? { |sig| sig.return_type.undefined? }
73
+ other_all_undefined = other.signatures.all? { |sig| sig.return_type.undefined? }
74
+ if all_undefined && !other_all_undefined
75
+ other.signatures
76
+ elsif other_all_undefined && !all_undefined
77
+ signatures
78
+ else
79
+ combine_all_signature_pins(*signatures, *other.signatures)
80
+ end
81
+ end
82
+
83
+ def combine_with(other, attrs = {})
84
+ priority_choice = choose_priority(other)
85
+ return priority_choice unless priority_choice.nil?
86
+
87
+ sigs = combine_signatures(other)
88
+ parameters = if sigs.length > 0
89
+ [].freeze
90
+ else
91
+ choose(other, :parameters).clone.freeze
92
+ end
93
+ new_attrs = {
94
+ visibility: combine_visibility(other),
95
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1050
96
+ explicit: explicit? || other.explicit?,
97
+ block: combine_blocks(other),
98
+ node: choose_node(other, :node),
99
+ attribute: prefer_rbs_location(other, :attribute?),
100
+ parameters: parameters,
101
+ signatures: sigs,
102
+ anon_splat: assert_same(other, :anon_splat?),
103
+ return_type: nil # pulled from signatures on first call
104
+ }.merge(attrs)
105
+ super(other, new_attrs)
106
+ end
107
+
108
+ # @param other [Pin::Method]
109
+ def == other
110
+ super && other.node == node
111
+ end
112
+
113
+ def transform_types(&transform)
114
+ # @todo 'super' alone should work here I think, but doesn't typecheck at level typed
115
+ m = super(&transform)
116
+ m.signatures = m.signatures.map do |sig|
117
+ sig.transform_types(&transform)
118
+ end
119
+ m.block = block&.transform_types(&transform)
120
+ m.reset_generated!
121
+ m
122
+ end
123
+
124
+ # @return [void]
125
+ def reset_generated!
126
+ super
127
+ unless signatures.empty?
128
+ return_type = nil
129
+ @block = :undefined
130
+ parameters = []
131
+ end
132
+ block&.reset_generated!
133
+ @signatures&.each(&:reset_generated!)
134
+ signature_help = nil
135
+ documentation = nil
136
+ end
137
+
138
+ def all_rooted?
139
+ super && parameters.all?(&:all_rooted?) && (!block || block&.all_rooted?) && signatures.all?(&:all_rooted?)
140
+ end
141
+
142
+ # @param signature [Pin::Signature]
143
+ # @return [Pin::Method]
144
+ def with_single_signature(signature)
145
+ m = proxy signature.return_type
146
+ m.reset_generated!
147
+ # @todo populating the single parameters/return_type/block
148
+ # arguments here seems to be needed for some specs to pass,
149
+ # even though we have a signature with the same information.
150
+ # Is this a problem for RBS-populated methods, which don't
151
+ # populate these three?
152
+ m.parameters = signature.parameters
153
+ m.return_type = signature.return_type
154
+ m.block = signature.block
155
+ m.signatures = [signature]
156
+ m
157
+ end
158
+
159
+ def block?
160
+ !block.nil?
161
+ end
162
+
163
+ # @return [Pin::Signature, nil]
164
+ def block
165
+ return @block unless @block == :undefined
166
+ @block = signatures.first&.block
167
+ end
168
+
169
+ def completion_item_kind
170
+ attribute? ? Solargraph::LanguageServer::CompletionItemKinds::PROPERTY : Solargraph::LanguageServer::CompletionItemKinds::METHOD
171
+ end
172
+
173
+ def symbol_kind
174
+ attribute? ? Solargraph::LanguageServer::SymbolKinds::PROPERTY : LanguageServer::SymbolKinds::METHOD
175
+ end
176
+
177
+ def return_type
178
+ @return_type ||= ComplexType.new(signatures.map(&:return_type).flat_map(&:items))
179
+ end
180
+
181
+ # @param parameters [::Array<Parameter>]
182
+ # @param return_type [ComplexType]
183
+ # @return [Signature]
184
+ def generate_signature(parameters, return_type)
185
+ block = nil
186
+ yieldparam_tags = docstring.tags(:yieldparam)
187
+ yieldreturn_tags = docstring.tags(:yieldreturn)
188
+ generics = docstring.tags(:generic).map(&:name)
189
+ needs_block_param_signature =
190
+ parameters.last&.block? || !yieldreturn_tags.empty? || !yieldparam_tags.empty?
191
+ if needs_block_param_signature
192
+ yield_parameters = yieldparam_tags.map do |p|
193
+ name = p.name
194
+ decl = :arg
195
+ if name
196
+ decl = select_decl(name, false)
197
+ name = clean_param(name)
198
+ end
199
+ Pin::Parameter.new(
200
+ location: location,
201
+ closure: self,
202
+ comments: p.text,
203
+ name: name,
204
+ decl: decl,
205
+ presence: location ? location.range : nil,
206
+ return_type: ComplexType.try_parse(*p.types),
207
+ source: source
208
+ )
209
+ end
210
+ yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types))
211
+ block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source,
212
+ closure: self, location: location, type_location: type_location)
213
+ end
214
+ signature = Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, closure: self, source: source,
215
+ location: location, type_location: type_location)
216
+ block.closure = signature if block
217
+ signature
218
+ end
219
+
220
+ # @return [::Array<Signature>]
221
+ def signatures
222
+ @signatures ||= begin
223
+ top_type = generate_complex_type
224
+ result = []
225
+ result.push generate_signature(parameters, top_type) if top_type.defined?
226
+ result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty?
227
+ result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
228
+ result
229
+ end
230
+ end
231
+
232
+ # @param return_type [ComplexType]
233
+ # @return [self]
234
+ def proxy_with_signatures return_type
235
+ out = proxy return_type
236
+ out.signatures = out.signatures.map { |sig| sig.proxy return_type }
237
+ out
238
+ end
239
+
240
+ # @return [String, nil]
241
+ def detail
242
+ # This property is not cached in an instance variable because it can
243
+ # change when pins get proxied.
244
+ detail = String.new
245
+ detail += if signatures.length > 1
246
+ "(*) "
247
+ else
248
+ "(#{signatures.first.parameters.map(&:full).join(', ')}) " unless signatures.first.parameters.empty?
249
+ end.to_s
250
+ detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined?
251
+ detail.strip!
252
+ return nil if detail.empty?
253
+ detail
254
+ end
255
+
256
+ # @return [::Array<Hash>]
257
+ def signature_help
258
+ @signature_help ||= signatures.map do |sig|
259
+ {
260
+ label: name + '(' + sig.parameters.map(&:full).join(', ') + ')',
261
+ documentation: documentation
262
+ }
263
+ end
264
+ end
265
+
266
+ def inner_desc
267
+ # ensure the signatures line up when logged
268
+ if signatures.length > 1
269
+ path + " \n#{to_rbs}\n"
270
+ else
271
+ super
272
+ end
273
+ end
274
+
275
+ def to_rbs
276
+ return nil if signatures.empty?
277
+
278
+ rbs = "def #{name}: #{signatures.first.to_rbs}"
279
+ signatures[1..].each do |sig|
280
+ rbs += "\n"
281
+ rbs += (' ' * (4 + name.length))
282
+ rbs += "| #{name}: #{sig.to_rbs}"
283
+ end
284
+ rbs
285
+ end
286
+
287
+ def path
288
+ @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}"
289
+ end
290
+
291
+ # @return [String]
292
+ def method_name
293
+ name
294
+ end
295
+
296
+ def typify api_map
297
+ logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context.rooted_tags}, return_type=#{return_type.rooted_tags}) - starting" }
298
+ decl = super
299
+ unless decl.undefined?
300
+ logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context}) => #{decl.rooted_tags.inspect} - decl found" }
301
+ return decl
302
+ end
303
+ type = see_reference(api_map) || typify_from_super(api_map)
304
+ logger.debug { "Method#typify(self=#{self}) - type=#{type&.rooted_tags.inspect}" }
305
+ unless type.nil?
306
+ qualified = type.qualify(api_map, *closure.gates)
307
+ logger.debug { "Method#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" }
308
+ return qualified
309
+ end
310
+ super
311
+ end
312
+
313
+ def documentation
314
+ if @documentation.nil?
315
+ method_docs ||= super || ''
316
+ param_tags = docstring.tags(:param)
317
+ unless param_tags.nil? or param_tags.empty?
318
+ method_docs += "\n\n" unless method_docs.empty?
319
+ method_docs += "Params:\n"
320
+ lines = []
321
+ param_tags.each do |p|
322
+ l = "* #{p.name}"
323
+ l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty?
324
+ l += " #{p.text}"
325
+ lines.push l
326
+ end
327
+ method_docs += lines.join("\n")
328
+ end
329
+ yieldparam_tags = docstring.tags(:yieldparam)
330
+ unless yieldparam_tags.nil? or yieldparam_tags.empty?
331
+ method_docs += "\n\n" unless method_docs.empty?
332
+ method_docs += "Block Params:\n"
333
+ lines = []
334
+ yieldparam_tags.each do |p|
335
+ l = "* #{p.name}"
336
+ l += " [#{escape_brackets(p.types.join(', '))}]" unless p.types.nil? or p.types.empty?
337
+ l += " #{p.text}"
338
+ lines.push l
339
+ end
340
+ method_docs += lines.join("\n")
341
+ end
342
+ yieldreturn_tags = docstring.tags(:yieldreturn)
343
+ unless yieldreturn_tags.empty?
344
+ method_docs += "\n\n" unless method_docs.empty?
345
+ method_docs += "Block Returns:\n"
346
+ lines = []
347
+ yieldreturn_tags.each do |r|
348
+ l = "*"
349
+ l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty?
350
+ l += " #{r.text}"
351
+ lines.push l
352
+ end
353
+ method_docs += lines.join("\n")
354
+ end
355
+ return_tags = docstring.tags(:return)
356
+ unless return_tags.empty?
357
+ method_docs += "\n\n" unless method_docs.empty?
358
+ method_docs += "Returns:\n"
359
+ lines = []
360
+ return_tags.each do |r|
361
+ l = "*"
362
+ l += " [#{escape_brackets(r.types.join(', '))}]" unless r.types.nil? or r.types.empty?
363
+ l += " #{r.text}"
364
+ lines.push l
365
+ end
366
+ method_docs += lines.join("\n")
367
+ end
368
+ method_docs += "\n\n" unless method_docs.empty?
369
+ method_docs += "Visibility: #{visibility}"
370
+ @documentation = method_docs
371
+ concat_example_tags
372
+ end
373
+ @documentation.to_s
374
+ end
375
+
376
+ def explicit?
377
+ @explicit
378
+ end
379
+
380
+ def attribute?
381
+ @attribute
382
+ end
383
+
384
+ # @parm other [self]
385
+ def nearly? other
386
+ super &&
387
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1050
388
+ parameters == other.parameters &&
389
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1050
390
+ scope == other.scope &&
391
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1050
392
+ visibility == other.visibility
393
+ end
394
+
395
+ def probe api_map
396
+ attribute? ? infer_from_iv(api_map) : infer_from_return_nodes(api_map)
397
+ end
398
+
399
+ # @return [::Array<Pin::Method>]
400
+ def overloads
401
+ # Ignore overload tags with nil parameters. If it's not an array, the
402
+ # tag's source is likely malformed.
403
+
404
+ # @param tag [YARD::Tags::OverloadTag]
405
+ @overloads ||= docstring.tags(:overload).select(&:parameters).map do |tag|
406
+ Pin::Signature.new(
407
+ generics: generics,
408
+ # @param src [Array(String, String)]
409
+ parameters: tag.parameters.map do |src|
410
+ name, decl = parse_overload_param(src.first)
411
+ Pin::Parameter.new(
412
+ location: location,
413
+ closure: self,
414
+ comments: tag.docstring.all.to_s,
415
+ name: name,
416
+ decl: decl,
417
+ presence: location ? location.range : nil,
418
+ return_type: param_type_from_name(tag, src.first),
419
+ source: :overloads
420
+ )
421
+ end,
422
+ closure: self,
423
+ return_type: ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)),
424
+ source: :overloads,
425
+ )
426
+ end
427
+ @overloads
428
+ end
429
+
430
+ def anon_splat?
431
+ @anon_splat
432
+ end
433
+
434
+ # @param api_map [ApiMap]
435
+ # @return [self]
436
+ def resolve_ref_tag api_map
437
+ return self if @resolved_ref_tag
438
+
439
+ @resolved_ref_tag = true
440
+ return self unless docstring.ref_tags.any?
441
+ docstring.ref_tags.each do |tag|
442
+ ref = if tag.owner.to_s.start_with?(/[#.]/)
443
+ api_map.get_methods(namespace)
444
+ .select { |pin| pin.path.end_with?(tag.owner.to_s) }
445
+ .first
446
+ else
447
+ # @todo Resolve relative namespaces
448
+ api_map.get_path_pins(tag.owner.to_s).first
449
+ end
450
+ next unless ref
451
+
452
+ docstring.add_tag(*ref.docstring.tags(:param))
453
+ end
454
+ self
455
+ end
456
+
457
+ # @param api_map [ApiMap]
458
+ # @return [Array<Pin::Method>]
459
+ def rest_of_stack api_map
460
+ api_map.get_method_stack(method_namespace, method_name, scope: scope).reject { |pin| pin.path == path }
461
+ end
462
+
463
+ protected
464
+
465
+ attr_writer :block
466
+
467
+ attr_writer :signature_help
468
+
469
+ attr_writer :documentation
470
+
471
+ def dodgy_visibility_source?
472
+ # as of 2025-03-12, the RBS generator used for
473
+ # e.g. activesupport did not understand 'private' markings
474
+ # inside 'class << self' blocks, but YARD did OK at it
475
+ source == :rbs && scope == :class && type_location&.filename&.include?('generated') && return_type.undefined? ||
476
+ # YARD's RBS generator seems to miss a lot of should-be protected instance methods
477
+ source == :rbs && scope == :instance && namespace.start_with?('YARD::') ||
478
+ # private on attr_readers seems to be broken in Prism's auto-generator script
479
+ source == :rbs && scope == :instance && namespace.start_with?('Prism::') ||
480
+ # The RBS for the RBS gem itself seems to use private as a
481
+ # 'is this a public API' concept, more aggressively than the
482
+ # actual code. Let's respect that and ignore the actual .rb file.
483
+ source == :yardoc && scope == :instance && namespace.start_with?('RBS::')
484
+ end
485
+
486
+ private
487
+
488
+ # @param name [String]
489
+ # @param asgn [Boolean]
490
+ #
491
+ # @return [::Symbol]
492
+ def select_decl name, asgn
493
+ if name.start_with?('**')
494
+ :kwrestarg
495
+ elsif name.start_with?('*')
496
+ :restarg
497
+ elsif name.start_with?('&')
498
+ :blockarg
499
+ elsif name.end_with?(':') && asgn
500
+ :kwoptarg
501
+ elsif name.end_with?(':')
502
+ :kwarg
503
+ elsif asgn
504
+ :optarg
505
+ else
506
+ :arg
507
+ end
508
+ end
509
+
510
+ # @param name [String]
511
+ # @return [String]
512
+ def clean_param name
513
+ name.gsub(/[*&:]/, '')
514
+ end
515
+
516
+ # @param tag [YARD::Tags::OverloadTag]
517
+ # @param name [String]
518
+ #
519
+ # @return [ComplexType]
520
+ def param_type_from_name(tag, name)
521
+ # @param t [YARD::Tags::Tag]
522
+ param = tag.tags(:param).select { |t| t.name == name }.first
523
+ return ComplexType::UNDEFINED unless param
524
+ ComplexType.try_parse(*param.types)
525
+ end
526
+
527
+ # @return [ComplexType]
528
+ def generate_complex_type
529
+ tags = docstring.tags(:return).map(&:types).flatten.compact
530
+ return ComplexType::UNDEFINED if tags.empty?
531
+ ComplexType.try_parse *tags
532
+ end
533
+
534
+ # @param api_map [ApiMap]
535
+ # @return [ComplexType, nil]
536
+ def see_reference api_map
537
+ # This should actually be an intersection type
538
+ # @param ref [YARD::Tags::Tag, Solargraph::Yard::Tags::RefTag]
539
+ docstring.ref_tags.each do |ref|
540
+ # @sg-ignore ref should actually be an intersection type
541
+ next unless ref.tag_name == 'return' && ref.owner
542
+ # @sg-ignore ref should actually be an intersection type
543
+ result = resolve_reference(ref.owner.to_s, api_map)
544
+ return result unless result.nil?
545
+ end
546
+ match = comments.match(/^[ \t]*\(see (.*)\)/m)
547
+ return nil if match.nil?
548
+ resolve_reference match[1], api_map
549
+ end
550
+
551
+ # @return [String]
552
+ def method_namespace
553
+ namespace
554
+ end
555
+
556
+ # @param api_map [ApiMap]
557
+ # @return [ComplexType, nil]
558
+ def typify_from_super api_map
559
+ stack = rest_of_stack api_map
560
+ return nil if stack.empty?
561
+ stack.each do |pin|
562
+ return pin.return_type unless pin.return_type.undefined?
563
+ end
564
+ nil
565
+ end
566
+
567
+ # @param ref [String]
568
+ # @param api_map [ApiMap]
569
+ # @return [ComplexType, nil]
570
+ def resolve_reference ref, api_map
571
+ parts = ref.split(/[.#]/)
572
+ if parts.first.empty? || parts.one?
573
+ path = "#{namespace}#{ref}"
574
+ else
575
+ fqns = api_map.qualify(parts.first, *gates)
576
+ return ComplexType::UNDEFINED if fqns.nil?
577
+ path = fqns + ref[parts.first.length] + parts.last
578
+ end
579
+ pins = api_map.get_path_pins(path)
580
+ pins.each do |pin|
581
+ type = pin.typify(api_map)
582
+ return type unless type.undefined?
583
+ end
584
+ nil
585
+ end
586
+
587
+ # @return [Parser::AST::Node, nil]
588
+ def method_body_node
589
+ return nil if node.nil?
590
+ return node.children[1].children.last if node.type == :DEFN
591
+ return node.children[2].children.last if node.type == :DEFS
592
+ return node.children[2] if node.type == :def || node.type == :DEFS
593
+ return node.children[3] if node.type == :defs
594
+ nil
595
+ end
596
+
597
+ # @param api_map [ApiMap]
598
+ # @return [ComplexType]
599
+ def infer_from_return_nodes api_map
600
+ return ComplexType::UNDEFINED if node.nil?
601
+ result = []
602
+ has_nil = false
603
+ return ComplexType::NIL if method_body_node.nil?
604
+ returns_from_method_body(method_body_node).each do |n|
605
+ if n.nil? || [:NIL, :nil].include?(n.type)
606
+ has_nil = true
607
+ next
608
+ end
609
+ rng = Range.from_node(n)
610
+ next unless rng
611
+ clip = api_map.clip_at(
612
+ location.filename,
613
+ rng.ending
614
+ )
615
+ chain = Solargraph::Parser.chain(n, location.filename)
616
+ type = chain.infer(api_map, self, clip.locals)
617
+ result.push type unless type.undefined?
618
+ end
619
+ result.push ComplexType::NIL if has_nil
620
+ return ComplexType::UNDEFINED if result.empty?
621
+ ComplexType.new(result.uniq)
622
+ end
623
+
624
+ # @param [ApiMap] api_map
625
+ # @return [ComplexType]
626
+ def infer_from_iv api_map
627
+ types = []
628
+ varname = "@#{name.gsub(/=$/, '')}"
629
+ pins = api_map.get_instance_variable_pins(binder.namespace, binder.scope).select { |iv| iv.name == varname }
630
+ pins.each do |pin|
631
+ type = pin.typify(api_map)
632
+ type = pin.probe(api_map) if type.undefined?
633
+ types.push type if type.defined?
634
+ end
635
+ return ComplexType::UNDEFINED if types.empty?
636
+ ComplexType.new(types.uniq)
637
+ end
638
+
639
+ # When YARD parses an overload tag, it includes rest modifiers in the parameters names.
640
+ #
641
+ # @param name [String]
642
+ # @return [::Array(String, ::Symbol)]
643
+ def parse_overload_param(name)
644
+ # @todo this needs to handle mandatory vs not args, kwargs, blocks, etc
645
+ if name.start_with?('**')
646
+ [name[2..-1], :kwrestarg]
647
+ elsif name.start_with?('*')
648
+ [name[1..-1], :restarg]
649
+ else
650
+ [name, :arg]
651
+ end
652
+ end
653
+
654
+ # @return [void]
655
+ def concat_example_tags
656
+ example_tags = docstring.tags(:example)
657
+ return if example_tags.empty?
658
+ @documentation += "\n\nExamples:\n\n```ruby\n"
659
+ @documentation += example_tags.map do |tag|
660
+ (tag.name && !tag.name.empty? ? "# #{tag.name}\n" : '') +
661
+ "#{tag.text}\n"
662
+ end
663
+ .join("\n")
664
+ .concat("```\n")
665
+ end
666
+
667
+ protected
668
+
669
+ attr_writer :return_type
670
+ end
671
+ end
672
+ end