solargraph 0.60.3 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/plugins.yml +1 -5
- data/.github/workflows/rspec.yml +3 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +0 -3
- data/lib/solargraph/api_map/index.rb +15 -2
- data/lib/solargraph/api_map/store.rb +13 -0
- data/lib/solargraph/api_map.rb +60 -81
- data/lib/solargraph/cache_dir.rb +58 -0
- data/lib/solargraph/collection/base.rb +50 -0
- data/lib/solargraph/collection/core.rb +17 -0
- data/lib/solargraph/collection/gem.rb +57 -0
- data/lib/solargraph/collection/rbs.rb +49 -0
- data/lib/solargraph/collection/stdlib.rb +25 -0
- data/lib/solargraph/collection.rb +31 -0
- data/lib/solargraph/complex_type.rb +18 -5
- data/lib/solargraph/convention.rb +5 -3
- data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
- data/lib/solargraph/external.rb +159 -0
- data/lib/solargraph/language_server/message/text_document/definition.rb +4 -1
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
- data/lib/solargraph/library.rb +11 -24
- data/lib/solargraph/metagem.rb +80 -0
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +14 -5
- data/lib/solargraph/pin/closure.rb +17 -0
- data/lib/solargraph/pin/factory_parameter.rb +84 -0
- data/lib/solargraph/pin/method.rb +7 -4
- data/lib/solargraph/pin.rb +1 -0
- data/lib/solargraph/rbs_map/base.rb +31 -0
- data/lib/solargraph/rbs_map/conversions.rb +3 -1
- data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
- data/lib/solargraph/rbs_map/core_fills.rb +1 -1
- data/lib/solargraph/rbs_map/gem.rb +13 -0
- data/lib/solargraph/rbs_map/helpers.rb +60 -0
- data/lib/solargraph/rbs_map/path.rb +19 -0
- data/lib/solargraph/rbs_map/stdlib.rb +24 -0
- data/lib/solargraph/rbs_map.rb +7 -198
- data/lib/solargraph/repo.rb +177 -0
- data/lib/solargraph/shell.rb +49 -99
- data/lib/solargraph/source/chain/call.rb +190 -67
- data/lib/solargraph/source/chain/literal.rb +17 -17
- data/lib/solargraph/source/chain/parameter.rb +79 -0
- data/lib/solargraph/source/chain.rb +44 -5
- data/lib/solargraph/source/source_chainer.rb +40 -44
- data/lib/solargraph/source_map/clip.rb +5 -3
- data/lib/solargraph/source_map/mapper.rb +6 -2
- data/lib/solargraph/source_map.rb +10 -5
- data/lib/solargraph/type_checker/rules.rb +2 -1
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace.rb +0 -1
- data/lib/solargraph/yard_map/cache.rb +1 -1
- data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
- data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives.rb +5 -2
- data/lib/solargraph/yard_map/helpers.rb +1 -1
- data/lib/solargraph/yard_map/macro.rb +34 -5
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
- data/lib/solargraph/yard_map/mapper.rb +37 -4
- data/lib/solargraph/yard_map.rb +1 -1
- data/lib/solargraph/yardoc.rb +43 -60
- data/lib/solargraph.rb +6 -3
- data/solargraph.gemspec +1 -1
- metadata +22 -9
- data/lib/solargraph/doc_map.rb +0 -439
- data/lib/solargraph/gem_pins.rb +0 -87
- data/lib/solargraph/pin_cache.rb +0 -248
- data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
- data/lib/solargraph/workspace/gemspecs.rb +0 -373
data/lib/solargraph/shell.rb
CHANGED
|
@@ -98,60 +98,37 @@ module Solargraph
|
|
|
98
98
|
# @return [void]
|
|
99
99
|
def clear
|
|
100
100
|
puts 'Deleting all cached documentation (gems, core and stdlib)'
|
|
101
|
-
Solargraph::
|
|
101
|
+
Solargraph::CacheDir.clear
|
|
102
102
|
end
|
|
103
103
|
map 'clear-cache' => :clear
|
|
104
104
|
map 'clear-cores' => :clear
|
|
105
105
|
|
|
106
|
-
desc 'cache', 'Cache a gem', hide: true
|
|
107
|
-
option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
|
|
108
|
-
# @return [void]
|
|
109
|
-
# @param gem [String]
|
|
110
|
-
# @param version [String, nil]
|
|
111
|
-
def cache gem, version = nil
|
|
112
|
-
gemspec = Gem::Specification.find_by_name(gem, version)
|
|
113
|
-
|
|
114
|
-
if options[:rebuild] || !PinCache.has_yard?(gemspec)
|
|
115
|
-
pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
|
|
116
|
-
PinCache.serialize_yard_gem(gemspec, pins)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
workspace = Solargraph::Workspace.new(Dir.pwd) if File.exist?('rbs_collection.yaml')
|
|
120
|
-
rbs_map = RbsMap.from_gemspec(gemspec, workspace&.rbs_collection_path, workspace&.rbs_collection_config_path)
|
|
121
|
-
if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
|
|
122
|
-
PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, rbs_map.pins)
|
|
123
|
-
end
|
|
124
|
-
rescue Gem::MissingSpecError
|
|
125
|
-
warn "Gem '#{gem}' not found"
|
|
126
|
-
end
|
|
127
|
-
|
|
128
106
|
desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
|
|
129
107
|
long_desc %(
|
|
130
108
|
Specify one or more gem names to clear. 'core' or 'stdlib' may
|
|
131
109
|
also be specified to clear cached system documentation.
|
|
132
110
|
Documentation will be regenerated as needed.
|
|
133
111
|
)
|
|
112
|
+
option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
|
|
134
113
|
# @param gems [Array<String>]
|
|
135
114
|
# @return [void]
|
|
136
115
|
def uncache *gems
|
|
137
116
|
raise ArgumentError, 'No gems specified.' if gems.empty?
|
|
117
|
+
repo = Solargraph::Repo.new(options[:directory])
|
|
118
|
+
|
|
138
119
|
gems.each do |gem|
|
|
139
120
|
if gem == 'core'
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
next
|
|
121
|
+
Solargraph::Collection::Core.uncache
|
|
122
|
+
elsif gem == 'stdlib'
|
|
123
|
+
FileUtils.rm_rf CacheDir.stdlib_dir
|
|
124
|
+
else
|
|
125
|
+
metagem = repo.find_by_name(gem)
|
|
126
|
+
Solargraph::Collection::Gem.uncache(metagem) if metagem
|
|
147
127
|
end
|
|
148
|
-
|
|
149
|
-
spec = Gem::Specification.find_by_name(gem)
|
|
150
|
-
PinCache.uncache_gem(spec, out: $stdout)
|
|
151
128
|
end
|
|
152
129
|
end
|
|
153
130
|
|
|
154
|
-
desc '
|
|
131
|
+
desc 'cache GEM_NAME [...GEM_NAME] [core]', 'Cache documentation for
|
|
155
132
|
installed libraries'
|
|
156
133
|
long_desc %( This command will cache the
|
|
157
134
|
generated type documentation for the specified libraries. While
|
|
@@ -172,56 +149,51 @@ module Solargraph
|
|
|
172
149
|
If the library is already cached, it will be rebuilt if the
|
|
173
150
|
--rebuild option is set.
|
|
174
151
|
|
|
175
|
-
Cached documentation is stored in #{
|
|
152
|
+
Cached documentation is stored in #{CacheDir.base_dir}, which
|
|
176
153
|
can be stored between CI runs.
|
|
177
154
|
)
|
|
155
|
+
option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
|
|
178
156
|
option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
|
|
179
157
|
# @param names [Array<String>]
|
|
180
158
|
# @return [void]
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
warn "Gem '#{name}' not found"
|
|
216
|
-
rescue Gem::Requirement::BadRequirementError => e
|
|
217
|
-
warn "Gem '#{name}' failed while loading"
|
|
218
|
-
warn e.message
|
|
219
|
-
# @sg-ignore Need to add nil check here
|
|
220
|
-
warn e.backtrace.join("\n")
|
|
221
|
-
end
|
|
222
|
-
warn "Documentation cached for #{names.count} gems."
|
|
159
|
+
# @param [Array<Object>] gem_names
|
|
160
|
+
def cache *gem_names
|
|
161
|
+
repo = Solargraph::Repo.new(options[:directory])
|
|
162
|
+
metagems = if gem_names.empty?
|
|
163
|
+
if repo.bundled?
|
|
164
|
+
repo.bundled.select(&:cacheable?)
|
|
165
|
+
else
|
|
166
|
+
Gem::Specification.all_names
|
|
167
|
+
.map { |name| Gem::Specification.find_by_full_name(name) }
|
|
168
|
+
.map { |gemspec| Metagem.from_specification(gemspec) }
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
gem_names.each_with_object([]) do |name, result|
|
|
172
|
+
if name == 'core'
|
|
173
|
+
Collection::Core.uncache if options[:rebuild]
|
|
174
|
+
puts 'Caching core'
|
|
175
|
+
Collection::Core.load
|
|
176
|
+
else
|
|
177
|
+
# @todo Quick and dirty hack for solargraph-rspec require bug
|
|
178
|
+
# (see https://github.com/lekemula/solargraph-rspec/pull/38)
|
|
179
|
+
# TL;DR: `repo.find_by_path` should not be necessary
|
|
180
|
+
found = repo.find_by_name(name) || repo.find_by_path(name)
|
|
181
|
+
if found
|
|
182
|
+
result.push found
|
|
183
|
+
else
|
|
184
|
+
warn "Gem #{name} not found"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
metagems.each do |metagem|
|
|
190
|
+
Collection::Gem.uncache(metagem) if options[:rebuild]
|
|
191
|
+
puts "Caching #{metagem.name} #{metagem.version} (#{metagem.cache_name})"
|
|
192
|
+
Collection::Gem.load(metagem)
|
|
223
193
|
end
|
|
194
|
+
puts "Documentation cached for #{metagems.count} gems."
|
|
224
195
|
end
|
|
196
|
+
map 'gems' => :cache
|
|
225
197
|
|
|
226
198
|
desc 'reporters', 'Get a list of diagnostics reporters'
|
|
227
199
|
# @return [void]
|
|
@@ -596,27 +568,5 @@ module Solargraph
|
|
|
596
568
|
puts pin.inspect
|
|
597
569
|
end
|
|
598
570
|
end
|
|
599
|
-
|
|
600
|
-
# @param gemspec [Gem::Specification, nil]
|
|
601
|
-
# @param rebuild [Boolean]
|
|
602
|
-
# @return [void]
|
|
603
|
-
def do_cache gemspec, rebuild: false
|
|
604
|
-
if gemspec.nil?
|
|
605
|
-
warn "Gem '#{gemspec&.name}' not found"
|
|
606
|
-
else
|
|
607
|
-
if rebuild || !PinCache.has_yard?(gemspec)
|
|
608
|
-
pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
|
|
609
|
-
PinCache.serialize_yard_gem(gemspec, pins)
|
|
610
|
-
end
|
|
611
|
-
|
|
612
|
-
workspace = Solargraph::Workspace.new(Dir.pwd)
|
|
613
|
-
rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
|
|
614
|
-
if rebuild || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
|
|
615
|
-
# cache pins even if result is zero, so we don't retry building pins
|
|
616
|
-
pins = rbs_map.pins || []
|
|
617
|
-
PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
|
|
618
|
-
end
|
|
619
|
-
end
|
|
620
|
-
end
|
|
621
571
|
end
|
|
622
572
|
end
|
|
@@ -64,13 +64,106 @@ module Solargraph
|
|
|
64
64
|
[stack.first].compact
|
|
65
65
|
end
|
|
66
66
|
pin_groups = [] if !api_map.loose_unions && pin_groups.any?(&:empty?)
|
|
67
|
-
|
|
67
|
+
# Several union members can end up with the same pin and return type.
|
|
68
|
+
# @sg-ignore Array#flatten returns a bare Array
|
|
69
|
+
pins = pin_groups.flatten.uniq { |p| [p.path, p.return_type.rooted_tags] }
|
|
68
70
|
return [] if pins.empty?
|
|
69
71
|
inferred_pins(pins, api_map, name_pin, locals)
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
private
|
|
73
75
|
|
|
76
|
+
# Checks whether a single overload signature matches the call's
|
|
77
|
+
# arguments/block and, if so, resolves its return type. Threaded
|
|
78
|
+
# through the caller's accumulator so behavior matches the
|
|
79
|
+
# original inline loop: if the overload doesn't match, the
|
|
80
|
+
# incoming type/signature are returned unchanged.
|
|
81
|
+
#
|
|
82
|
+
# @param overload [Pin::Signature]
|
|
83
|
+
# @param pin [Pin::Method]
|
|
84
|
+
# @param api_map [ApiMap]
|
|
85
|
+
# @param name_pin [Pin::Base]
|
|
86
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
87
|
+
# @param type [ComplexType]
|
|
88
|
+
# @param new_signature_pin [Pin::Signature, nil]
|
|
89
|
+
# @return [::Array(ComplexType, Pin::Signature)]
|
|
90
|
+
def match_overload_type overload, pin, api_map, name_pin, locals, type, new_signature_pin
|
|
91
|
+
return [type, new_signature_pin] unless overload.arity_matches?(arguments, with_block?)
|
|
92
|
+
|
|
93
|
+
match = true
|
|
94
|
+
atypes = []
|
|
95
|
+
arguments.each_with_index do |arg, idx|
|
|
96
|
+
param = overload.parameters[idx]
|
|
97
|
+
if param.nil?
|
|
98
|
+
match = overload.parameters.any?(&:restarg?)
|
|
99
|
+
break
|
|
100
|
+
end
|
|
101
|
+
arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
|
|
102
|
+
closure: name_pin.closure,
|
|
103
|
+
gates: name_pin.gates,
|
|
104
|
+
source: :chain)
|
|
105
|
+
atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
|
|
106
|
+
# @sg-ignore flow sensitive typing should handle is_a? and next
|
|
107
|
+
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
|
108
|
+
match = false
|
|
109
|
+
break
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
return [type, new_signature_pin] unless match
|
|
113
|
+
|
|
114
|
+
if overload.block && with_block?
|
|
115
|
+
block_atypes = overload.block.parameters.map(&:return_type)
|
|
116
|
+
# @todo Need to add nil check here
|
|
117
|
+
# @sg-ignore Need to add nil check here
|
|
118
|
+
blocktype = if block.links.map(&:class) == [BlockSymbol]
|
|
119
|
+
# like the bar in foo(&:bar)
|
|
120
|
+
block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
|
|
121
|
+
else
|
|
122
|
+
block_call_type(api_map, name_pin, locals)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
new_signature_pin = overload.resolve_generics_from_context_until_complete(overload.generics, atypes, nil, nil,
|
|
126
|
+
blocktype)
|
|
127
|
+
# @todo It shouldn't be necessary to choose either generics or macros
|
|
128
|
+
# @sg-ignore Need to add nil check here
|
|
129
|
+
new_return_type = if new_signature_pin.return_type.defined?
|
|
130
|
+
# @sg-ignore Need to add nil check here
|
|
131
|
+
new_signature_pin.return_type
|
|
132
|
+
else
|
|
133
|
+
# @sg-ignore Need to add nil check here
|
|
134
|
+
named_types = pin.parameter_names.zip(arguments.map { |arg| ComplexType.try_parse(simple_convert(arg.node).to_s) }).to_h
|
|
135
|
+
pin.typify(api_map).expand(named_types)
|
|
136
|
+
end
|
|
137
|
+
self_type = if head?
|
|
138
|
+
# If we're at the head of the chain, we called a
|
|
139
|
+
# method somewhere that marked itself as returning
|
|
140
|
+
# self. Given we didn't invoke this on an object,
|
|
141
|
+
# this must be a method in this same class - so we
|
|
142
|
+
# use our own self type
|
|
143
|
+
name_pin.context
|
|
144
|
+
else
|
|
145
|
+
# if we're past the head in the chain, whatever the
|
|
146
|
+
# type of the lhs side is what 'self' will be in its
|
|
147
|
+
# declaration - we can't just use the type of the
|
|
148
|
+
# method pin, as this might be a subclass of the
|
|
149
|
+
# place where the method is defined
|
|
150
|
+
name_pin.binder
|
|
151
|
+
end
|
|
152
|
+
# This same logic applies to the YARD work done by
|
|
153
|
+
# 'with_params()'.
|
|
154
|
+
#
|
|
155
|
+
# qualify(), however, happens in the namespace where
|
|
156
|
+
# the docs were written - from the method pin.
|
|
157
|
+
# @todo Need to add nil check here
|
|
158
|
+
type = if new_return_type.defined?
|
|
159
|
+
with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *pin.gates)
|
|
160
|
+
else
|
|
161
|
+
inferr_from_factory_parameters(api_map, pin)
|
|
162
|
+
end
|
|
163
|
+
type ||= ComplexType::UNDEFINED
|
|
164
|
+
[type, new_signature_pin]
|
|
165
|
+
end
|
|
166
|
+
|
|
74
167
|
# @param pins [::Enumerable<Pin::Base>]
|
|
75
168
|
# @param api_map [ApiMap]
|
|
76
169
|
# @param name_pin [Pin::Base]
|
|
@@ -96,76 +189,20 @@ module Solargraph
|
|
|
96
189
|
# @sg-ignore flow sensitive typing should handle is_a? and next
|
|
97
190
|
# @param ol [Pin::Signature]
|
|
98
191
|
sorted_overloads.each do |ol|
|
|
99
|
-
|
|
100
|
-
match = true
|
|
101
|
-
|
|
102
|
-
atypes = []
|
|
103
|
-
arguments.each_with_index do |arg, idx|
|
|
104
|
-
param = ol.parameters[idx]
|
|
105
|
-
if param.nil?
|
|
106
|
-
match = ol.parameters.any?(&:restarg?)
|
|
107
|
-
break
|
|
108
|
-
end
|
|
109
|
-
arg_name_pin = Pin::ProxyType.anonymous(name_pin.context,
|
|
110
|
-
closure: name_pin.closure,
|
|
111
|
-
gates: name_pin.gates,
|
|
112
|
-
source: :chain)
|
|
113
|
-
atype = atypes[idx] ||= arg.infer(api_map, arg_name_pin, locals)
|
|
114
|
-
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
|
115
|
-
match = false
|
|
116
|
-
break
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
if match
|
|
120
|
-
if ol.block && with_block?
|
|
121
|
-
block_atypes = ol.block.parameters.map(&:return_type)
|
|
122
|
-
# @todo Need to add nil check here
|
|
123
|
-
blocktype = if block.links.map(&:class) == [BlockSymbol]
|
|
124
|
-
# like the bar in foo(&:bar)
|
|
125
|
-
block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
|
|
126
|
-
else
|
|
127
|
-
block_call_type(api_map, name_pin, locals)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil,
|
|
131
|
-
blocktype)
|
|
132
|
-
# @todo It shouldn't be necessary to choose either generics or macros
|
|
133
|
-
new_return_type = if new_signature_pin.return_type.defined?
|
|
134
|
-
new_signature_pin.return_type
|
|
135
|
-
else
|
|
136
|
-
named_types = p.parameter_names.zip(arguments.map { |arg| ComplexType.try_parse(simple_convert(arg.node).to_s) }).to_h
|
|
137
|
-
p.typify(api_map).expand(named_types)
|
|
138
|
-
end
|
|
139
|
-
self_type = if head?
|
|
140
|
-
# If we're at the head of the chain, we called a
|
|
141
|
-
# method somewhere that marked itself as returning
|
|
142
|
-
# self. Given we didn't invoke this on an object,
|
|
143
|
-
# this must be a method in this same class - so we
|
|
144
|
-
# use our own self type
|
|
145
|
-
name_pin.context
|
|
146
|
-
else
|
|
147
|
-
# if we're past the head in the chain, whatever the
|
|
148
|
-
# type of the lhs side is what 'self' will be in its
|
|
149
|
-
# declaration - we can't just use the type of the
|
|
150
|
-
# method pin, as this might be a subclass of the
|
|
151
|
-
# place where the method is defined
|
|
152
|
-
name_pin.binder
|
|
153
|
-
end
|
|
154
|
-
# This same logic applies to the YARD work done by
|
|
155
|
-
# 'with_params()'.
|
|
156
|
-
#
|
|
157
|
-
# qualify(), however, happens in the namespace where
|
|
158
|
-
# the docs were written - from the method pin.
|
|
159
|
-
# @todo Need to add nil check here
|
|
160
|
-
if new_return_type.defined?
|
|
161
|
-
type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *p.gates)
|
|
162
|
-
end
|
|
163
|
-
type ||= ComplexType::UNDEFINED
|
|
164
|
-
end
|
|
192
|
+
type, new_signature_pin = match_overload_type(ol, p, api_map, name_pin, locals, type, new_signature_pin)
|
|
165
193
|
break if type.defined?
|
|
166
194
|
end
|
|
167
195
|
p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
|
|
168
196
|
next p.proxy(type) if type.defined?
|
|
197
|
+
if !p.macros.empty?
|
|
198
|
+
result = process_macro(p, api_map, name_pin.context, locals)
|
|
199
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
200
|
+
next result unless result.return_type.undefined?
|
|
201
|
+
elsif !p.directives.empty?
|
|
202
|
+
result = process_directive(p, api_map, name_pin.context, locals)
|
|
203
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
204
|
+
next result unless result.return_type.undefined?
|
|
205
|
+
end
|
|
169
206
|
p
|
|
170
207
|
end
|
|
171
208
|
logger.debug do
|
|
@@ -186,6 +223,92 @@ module Solargraph
|
|
|
186
223
|
end
|
|
187
224
|
end
|
|
188
225
|
|
|
226
|
+
# @param api_map [ApiMap]
|
|
227
|
+
# @param method_pin [Pin::Method]
|
|
228
|
+
# @return [ComplexType, nil]
|
|
229
|
+
def inferr_from_factory_parameters api_map, method_pin
|
|
230
|
+
factory_parameter = api_map.factory_parameters_for_method(method_pin).find do |factory_param|
|
|
231
|
+
method_pin.parameters.each_with_index.find do |param, index|
|
|
232
|
+
current_argument = arguments[index]
|
|
233
|
+
next unless current_argument&.literal?
|
|
234
|
+
# @type [Solargraph::Source::Chain::Literal]
|
|
235
|
+
last_link = current_argument.links.last
|
|
236
|
+
argument_value = last_link.value
|
|
237
|
+
|
|
238
|
+
param.name == factory_param.param_name && argument_value == factory_param.value
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
return nil if factory_parameter.nil?
|
|
243
|
+
|
|
244
|
+
factory_parameter.return_type.qualify(api_map, method_pin.namespace)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# @param pin [Pin::Base]
|
|
248
|
+
# @param api_map [ApiMap]
|
|
249
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
250
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
251
|
+
# @return [Pin::Base]
|
|
252
|
+
def process_macro pin, api_map, context, locals
|
|
253
|
+
pin.macros.each do |macro|
|
|
254
|
+
# @todo 'Wrong argument type for
|
|
255
|
+
# Solargraph::Source::Chain::Call#inner_process_macro:
|
|
256
|
+
# macro expected YARD::Tags::MacroDirective, received
|
|
257
|
+
# generic<Elem>' is because we lose 'rooted' information
|
|
258
|
+
# in the 'Chain::Array' class internally, leaving
|
|
259
|
+
# ::Array#each shadowed when it shouldn't be.
|
|
260
|
+
# @sg-ignore macro is Solargraph::YardMap::Macro, wraps a YARD::Tags::MacroDirective
|
|
261
|
+
result = inner_process_macro(pin, macro, api_map, context, locals)
|
|
262
|
+
return result unless result.return_type.undefined?
|
|
263
|
+
end
|
|
264
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# @param pin [Pin::Method]
|
|
268
|
+
# @param api_map [ApiMap]
|
|
269
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
270
|
+
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
271
|
+
# @return [Pin::ProxyType]
|
|
272
|
+
def process_directive pin, api_map, context, locals
|
|
273
|
+
pin.directives.each do |dir|
|
|
274
|
+
macro = api_map.named_macro(dir.tag.name)
|
|
275
|
+
next if macro.nil?
|
|
276
|
+
# @sg-ignore macro is Solargraph::YardMap::Macro, wraps a YARD::Tags::MacroDirective
|
|
277
|
+
result = inner_process_macro(pin, macro, api_map, context, locals)
|
|
278
|
+
return result unless result.return_type.undefined?
|
|
279
|
+
end
|
|
280
|
+
Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# @param pin [Pin::Base]
|
|
284
|
+
# @param macro [YARD::Tags::MacroDirective]
|
|
285
|
+
# @param api_map [ApiMap]
|
|
286
|
+
# @param context [ComplexType, ComplexType::UniqueType]
|
|
287
|
+
# @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
|
|
288
|
+
# @return [Pin::ProxyType]
|
|
289
|
+
def inner_process_macro pin, macro, api_map, context, locals
|
|
290
|
+
vals = arguments.map { |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
|
|
291
|
+
txt = macro.tag.text.clone
|
|
292
|
+
# @sg-ignore Need to add nil check here
|
|
293
|
+
if txt.empty? && macro.tag.name
|
|
294
|
+
named = api_map.named_macro(macro.tag.name)
|
|
295
|
+
txt = named.tag.text.clone if named
|
|
296
|
+
end
|
|
297
|
+
i = 1
|
|
298
|
+
vals.each do |v|
|
|
299
|
+
# @sg-ignore Need to add nil check here
|
|
300
|
+
txt.gsub!(/\$#{i}/, v.context.namespace)
|
|
301
|
+
i += 1
|
|
302
|
+
end
|
|
303
|
+
# @sg-ignore Need to add nil check here
|
|
304
|
+
docstring = Solargraph::Source.parse_docstring(txt).to_docstring
|
|
305
|
+
tag = docstring.tag(:return)
|
|
306
|
+
unless tag.nil? || tag.types.nil?
|
|
307
|
+
return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
|
|
308
|
+
end
|
|
309
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
|
310
|
+
end
|
|
311
|
+
|
|
189
312
|
# @param docstring [YARD::Docstring]
|
|
190
313
|
# @param context [ComplexType]
|
|
191
314
|
# @return [ComplexType, nil]
|
|
@@ -6,29 +6,29 @@ module Solargraph
|
|
|
6
6
|
class Source
|
|
7
7
|
class Chain
|
|
8
8
|
class Literal < Link
|
|
9
|
-
attr_reader :word
|
|
9
|
+
attr_reader :word
|
|
10
|
+
|
|
11
|
+
# @return [::String, ::Symbol]
|
|
12
|
+
attr_reader :value
|
|
13
|
+
|
|
14
|
+
# @return [Parser::AST::Node]
|
|
15
|
+
attr_reader :node
|
|
10
16
|
|
|
11
17
|
# @param type [String]
|
|
12
18
|
# @param node [Parser::AST::Node, Object]
|
|
13
19
|
def initialize type, node
|
|
14
20
|
super("<#{type}>")
|
|
21
|
+
@node = node
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# @value = false
|
|
26
|
-
# # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
27
|
-
# elsif %i[int sym].include?(node.type)
|
|
28
|
-
# # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
29
|
-
# @value = node.children.first
|
|
30
|
-
# end
|
|
31
|
-
# end
|
|
23
|
+
if node.is_a?(::Parser::AST::Node)
|
|
24
|
+
if node.type == :true
|
|
25
|
+
@value = true
|
|
26
|
+
elsif node.type == :false
|
|
27
|
+
@value = false
|
|
28
|
+
elsif %i[int sym].include?(node.type)
|
|
29
|
+
@value = node.children.first
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
32
|
@type = type
|
|
33
33
|
# @literal_type = ComplexType.try_parse(@value.inspect)
|
|
34
34
|
@literal_type = ComplexType::UNDEFINED
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class Source
|
|
5
|
+
class Chain
|
|
6
|
+
# A method parameter represented as a link in a method call chain.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# some_method('literal_value')
|
|
10
|
+
# ^^^^^^^^^^^^^
|
|
11
|
+
class Parameter < Link
|
|
12
|
+
# @param literal [Chain::Literal] - literal argument
|
|
13
|
+
# @param method_call_chain [Chain] method call that contains the argument
|
|
14
|
+
def initialize literal, method_call_chain # rubocop:disable Lint/MissingSuper
|
|
15
|
+
@literal = literal
|
|
16
|
+
@method_call_chain = method_call_chain
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def word
|
|
20
|
+
@word ||= "#{method_name}(.., #{@literal.word}, ..)"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param api_map [ApiMap]
|
|
24
|
+
# @param name_pin [Pin::Base]
|
|
25
|
+
# @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
|
|
26
|
+
# @return [::Array<Pin::Base>]
|
|
27
|
+
def resolve api_map, name_pin, locals
|
|
28
|
+
# @type [Pin::Method]
|
|
29
|
+
method_pin = method_call_chain.define(api_map, name_pin, locals)&.first
|
|
30
|
+
return [] unless method_pin
|
|
31
|
+
|
|
32
|
+
# Use indexed lookup for better performance
|
|
33
|
+
api_map.factory_parameters_for_method(method_pin).select do |fp|
|
|
34
|
+
param_index = method_pin.parameters.find_index { |param| param.name == fp.param_name }
|
|
35
|
+
next if param_index.nil?
|
|
36
|
+
next false unless fp.value == literal_value
|
|
37
|
+
|
|
38
|
+
# a splat takes every argument from its position on
|
|
39
|
+
method_pin.parameters[param_index].restarg? ? current_index >= param_index : current_index == param_index
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Boolean] true if this is a parameter of Kernel#require
|
|
44
|
+
def require_parameter?
|
|
45
|
+
method_call_chain.links.last.word == 'require'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# @return [Chain::Literal]
|
|
51
|
+
attr_reader :literal
|
|
52
|
+
# @ return [Chain]
|
|
53
|
+
attr_reader :method_call_chain
|
|
54
|
+
|
|
55
|
+
# @return [String] The name of the method that this parameter belongs to
|
|
56
|
+
def method_name
|
|
57
|
+
@method_name ||= method_call.word
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [Chain::Call]
|
|
61
|
+
def method_call
|
|
62
|
+
@method_call_chain.links.last
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The index of the literal in the method call chain.
|
|
66
|
+
# @return [Integer]
|
|
67
|
+
def current_index
|
|
68
|
+
@current_index ||= method_call_chain.node.children[2..].index(literal.node)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [::String, ::Symbol] The literal value of the parameter
|
|
72
|
+
# @sg-ignore false "return type could not be inferred"
|
|
73
|
+
def literal_value
|
|
74
|
+
literal.node.children.first
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|