solargraph 0.58.1 → 0.58.2
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +7 -1
- data/lib/solargraph/api_map/cache.rb +110 -110
- data/lib/solargraph/api_map/constants.rb +279 -279
- data/lib/solargraph/api_map/index.rb +193 -193
- data/lib/solargraph/api_map/source_to_yard.rb +97 -97
- data/lib/solargraph/api_map/store.rb +384 -384
- data/lib/solargraph/api_map.rb +945 -945
- data/lib/solargraph/complex_type/type_methods.rb +228 -228
- data/lib/solargraph/complex_type/unique_type.rb +482 -482
- data/lib/solargraph/complex_type.rb +444 -444
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
- data/lib/solargraph/convention/data_definition.rb +105 -105
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
- data/lib/solargraph/convention/struct_definition.rb +164 -164
- data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
- data/lib/solargraph/diagnostics/rubocop.rb +118 -118
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -68
- data/lib/solargraph/diagnostics/type_check.rb +55 -55
- data/lib/solargraph/doc_map.rb +439 -439
- data/lib/solargraph/equality.rb +34 -34
- data/lib/solargraph/gem_pins.rb +98 -98
- data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
- data/lib/solargraph/language_server/host/dispatch.rb +130 -130
- data/lib/solargraph/language_server/host/message_worker.rb +112 -112
- data/lib/solargraph/language_server/host/sources.rb +99 -99
- data/lib/solargraph/language_server/host.rb +878 -878
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
- data/lib/solargraph/language_server/message/extended/document.rb +23 -23
- data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
- data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
- data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -148
- data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
- data/lib/solargraph/library.rb +683 -683
- data/lib/solargraph/location.rb +82 -82
- data/lib/solargraph/logging.rb +37 -37
- data/lib/solargraph/parser/comment_ripper.rb +69 -69
- data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
- data/lib/solargraph/parser/node_processor/base.rb +92 -92
- data/lib/solargraph/parser/node_processor.rb +62 -62
- data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -149
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
- data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -486
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -52
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -291
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
- data/lib/solargraph/parser/region.rb +69 -69
- data/lib/solargraph/parser/snippet.rb +17 -17
- data/lib/solargraph/pin/base.rb +729 -729
- data/lib/solargraph/pin/base_variable.rb +126 -126
- data/lib/solargraph/pin/block.rb +104 -104
- data/lib/solargraph/pin/breakable.rb +9 -9
- data/lib/solargraph/pin/callable.rb +231 -231
- data/lib/solargraph/pin/closure.rb +72 -72
- data/lib/solargraph/pin/common.rb +79 -79
- data/lib/solargraph/pin/conversions.rb +123 -123
- data/lib/solargraph/pin/delegated_method.rb +120 -120
- data/lib/solargraph/pin/documenting.rb +114 -114
- data/lib/solargraph/pin/instance_variable.rb +34 -34
- data/lib/solargraph/pin/keyword.rb +20 -20
- data/lib/solargraph/pin/local_variable.rb +75 -75
- data/lib/solargraph/pin/method.rb +672 -672
- data/lib/solargraph/pin/method_alias.rb +34 -34
- data/lib/solargraph/pin/namespace.rb +115 -115
- data/lib/solargraph/pin/parameter.rb +275 -275
- data/lib/solargraph/pin/proxy_type.rb +39 -39
- data/lib/solargraph/pin/reference/override.rb +47 -47
- data/lib/solargraph/pin/reference/superclass.rb +15 -15
- data/lib/solargraph/pin/reference.rb +39 -39
- data/lib/solargraph/pin/search.rb +61 -61
- data/lib/solargraph/pin/signature.rb +61 -61
- data/lib/solargraph/pin/symbol.rb +53 -53
- data/lib/solargraph/pin/until.rb +18 -18
- data/lib/solargraph/pin/while.rb +18 -18
- data/lib/solargraph/pin.rb +44 -44
- data/lib/solargraph/pin_cache.rb +245 -245
- data/lib/solargraph/position.rb +132 -119
- data/lib/solargraph/range.rb +112 -112
- data/lib/solargraph/rbs_map/conversions.rb +823 -823
- data/lib/solargraph/rbs_map/core_map.rb +58 -58
- data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
- data/lib/solargraph/rbs_map.rb +163 -163
- data/lib/solargraph/shell.rb +352 -352
- data/lib/solargraph/source/chain/call.rb +337 -337
- data/lib/solargraph/source/chain/constant.rb +26 -26
- data/lib/solargraph/source/chain/hash.rb +34 -34
- data/lib/solargraph/source/chain/if.rb +28 -28
- data/lib/solargraph/source/chain/instance_variable.rb +13 -13
- data/lib/solargraph/source/chain/literal.rb +48 -48
- data/lib/solargraph/source/chain/or.rb +23 -23
- data/lib/solargraph/source/chain.rb +291 -291
- data/lib/solargraph/source/change.rb +82 -82
- data/lib/solargraph/source/cursor.rb +166 -166
- data/lib/solargraph/source/source_chainer.rb +194 -194
- data/lib/solargraph/source/updater.rb +55 -55
- data/lib/solargraph/source.rb +498 -498
- data/lib/solargraph/source_map/clip.rb +226 -226
- data/lib/solargraph/source_map/data.rb +34 -34
- data/lib/solargraph/source_map/mapper.rb +259 -259
- data/lib/solargraph/source_map.rb +212 -212
- data/lib/solargraph/type_checker/checks.rb +124 -124
- data/lib/solargraph/type_checker/param_def.rb +37 -37
- data/lib/solargraph/type_checker/problem.rb +32 -32
- data/lib/solargraph/type_checker/rules.rb +84 -84
- data/lib/solargraph/type_checker.rb +814 -814
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +255 -255
- data/lib/solargraph/workspace/require_paths.rb +97 -97
- data/lib/solargraph/workspace.rb +220 -220
- data/lib/solargraph/yard_map/helpers.rb +44 -44
- data/lib/solargraph/yard_map/mapper/to_method.rb +130 -130
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -31
- data/lib/solargraph/yard_map/mapper.rb +79 -79
- data/lib/solargraph/yard_map/to_method.rb +89 -89
- data/lib/solargraph/yardoc.rb +87 -87
- data/lib/solargraph.rb +105 -105
- data/rbs_collection.yaml +1 -1
- metadata +12 -12
- /data/{sig → rbs}/shims/ast/0/node.rbs +0 -0
- /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
|
@@ -1,672 +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, :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
|
|
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
|