solargraph 0.58.2 → 0.59.0.dev.1
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/.envrc +3 -0
- data/.github/workflows/linting.yml +4 -5
- data/.github/workflows/plugins.yml +40 -36
- data/.github/workflows/rspec.yml +45 -13
- data/.github/workflows/typecheck.yml +2 -2
- data/.gitignore +0 -1
- data/.rubocop_todo.yml +27 -49
- data/CHANGELOG.md +1 -7
- data/README.md +3 -3
- data/Rakefile +1 -0
- data/lib/solargraph/api_map/cache.rb +3 -3
- data/lib/solargraph/api_map/constants.rb +13 -3
- data/lib/solargraph/api_map/index.rb +22 -11
- data/lib/solargraph/api_map/source_to_yard.rb +13 -1
- data/lib/solargraph/api_map/store.rb +11 -8
- data/lib/solargraph/api_map.rb +105 -50
- data/lib/solargraph/complex_type/conformance.rb +176 -0
- data/lib/solargraph/complex_type/type_methods.rb +16 -2
- data/lib/solargraph/complex_type/unique_type.rb +170 -20
- data/lib/solargraph/complex_type.rb +119 -14
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +3 -1
- data/lib/solargraph/convention/data_definition.rb +4 -1
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +1 -0
- data/lib/solargraph/convention/struct_definition.rb +5 -1
- data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
- data/lib/solargraph/diagnostics/rubocop.rb +1 -0
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +2 -0
- data/lib/solargraph/diagnostics/type_check.rb +1 -0
- data/lib/solargraph/doc_map.rb +134 -373
- data/lib/solargraph/equality.rb +1 -1
- data/lib/solargraph/gem_pins.rb +14 -15
- data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
- data/lib/solargraph/language_server/host/dispatch.rb +1 -0
- data/lib/solargraph/language_server/host/message_worker.rb +2 -1
- data/lib/solargraph/language_server/host/sources.rb +1 -0
- data/lib/solargraph/language_server/host.rb +6 -1
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -7
- data/lib/solargraph/language_server/message/extended/document.rb +1 -0
- data/lib/solargraph/language_server/message/text_document/completion.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -0
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +2 -0
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -0
- data/lib/solargraph/library.rb +59 -13
- data/lib/solargraph/location.rb +9 -4
- data/lib/solargraph/logging.rb +21 -1
- data/lib/solargraph/parser/comment_ripper.rb +7 -0
- data/lib/solargraph/parser/flow_sensitive_typing.rb +330 -102
- data/lib/solargraph/parser/node_processor/base.rb +32 -2
- data/lib/solargraph/parser/node_processor.rb +7 -6
- data/lib/solargraph/parser/parser_gem/class_methods.rb +28 -10
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +31 -6
- data/lib/solargraph/parser/parser_gem/node_methods.rb +27 -7
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +2 -0
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -11
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +7 -0
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +3 -2
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +2 -2
- data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +12 -7
- data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +5 -1
- data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
- data/lib/solargraph/parser/region.rb +9 -3
- data/lib/solargraph/parser/snippet.rb +1 -1
- data/lib/solargraph/pin/base.rb +53 -21
- data/lib/solargraph/pin/base_variable.rb +312 -20
- data/lib/solargraph/pin/block.rb +26 -4
- data/lib/solargraph/pin/breakable.rb +5 -1
- data/lib/solargraph/pin/callable.rb +50 -3
- data/lib/solargraph/pin/closure.rb +2 -6
- data/lib/solargraph/pin/common.rb +20 -5
- data/lib/solargraph/pin/compound_statement.rb +55 -0
- data/lib/solargraph/pin/conversions.rb +2 -1
- data/lib/solargraph/pin/delegated_method.rb +15 -4
- data/lib/solargraph/pin/documenting.rb +1 -0
- data/lib/solargraph/pin/instance_variable.rb +5 -1
- data/lib/solargraph/pin/keyword.rb +0 -4
- data/lib/solargraph/pin/local_variable.rb +13 -57
- data/lib/solargraph/pin/method.rb +90 -42
- data/lib/solargraph/pin/method_alias.rb +8 -0
- data/lib/solargraph/pin/namespace.rb +7 -1
- data/lib/solargraph/pin/parameter.rb +76 -13
- data/lib/solargraph/pin/proxy_type.rb +2 -1
- data/lib/solargraph/pin/reference/override.rb +1 -1
- data/lib/solargraph/pin/reference/superclass.rb +2 -0
- data/lib/solargraph/pin/reference.rb +2 -0
- data/lib/solargraph/pin/search.rb +1 -0
- data/lib/solargraph/pin/signature.rb +8 -0
- data/lib/solargraph/pin/symbol.rb +1 -1
- data/lib/solargraph/pin/until.rb +1 -1
- data/lib/solargraph/pin/while.rb +1 -1
- data/lib/solargraph/pin.rb +2 -0
- data/lib/solargraph/pin_cache.rb +477 -57
- data/lib/solargraph/position.rb +12 -26
- data/lib/solargraph/range.rb +6 -6
- data/lib/solargraph/rbs_map/conversions.rb +33 -10
- data/lib/solargraph/rbs_map/core_map.rb +24 -17
- data/lib/solargraph/rbs_map/stdlib_map.rb +34 -5
- data/lib/solargraph/rbs_map.rb +74 -20
- data/lib/solargraph/shell.rb +73 -28
- data/lib/solargraph/source/chain/call.rb +52 -17
- data/lib/solargraph/source/chain/constant.rb +2 -0
- data/lib/solargraph/source/chain/hash.rb +1 -0
- data/lib/solargraph/source/chain/if.rb +1 -0
- data/lib/solargraph/source/chain/instance_variable.rb +22 -1
- data/lib/solargraph/source/chain/literal.rb +5 -0
- data/lib/solargraph/source/chain/or.rb +9 -1
- data/lib/solargraph/source/chain.rb +25 -22
- data/lib/solargraph/source/change.rb +9 -2
- data/lib/solargraph/source/cursor.rb +7 -1
- data/lib/solargraph/source/source_chainer.rb +13 -3
- data/lib/solargraph/source/updater.rb +4 -0
- data/lib/solargraph/source.rb +33 -7
- data/lib/solargraph/source_map/clip.rb +13 -2
- data/lib/solargraph/source_map/data.rb +4 -1
- data/lib/solargraph/source_map/mapper.rb +24 -1
- data/lib/solargraph/source_map.rb +14 -6
- data/lib/solargraph/type_checker/problem.rb +3 -1
- data/lib/solargraph/type_checker/rules.rb +75 -2
- data/lib/solargraph/type_checker.rb +111 -30
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +3 -1
- data/lib/solargraph/workspace/gemspecs.rb +367 -0
- data/lib/solargraph/workspace/require_paths.rb +1 -0
- data/lib/solargraph/workspace.rb +158 -16
- data/lib/solargraph/yard_map/helpers.rb +2 -1
- data/lib/solargraph/yard_map/mapper/to_method.rb +5 -1
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
- data/lib/solargraph/yard_map/mapper.rb +5 -0
- data/lib/solargraph/yardoc.rb +33 -23
- data/lib/solargraph.rb +24 -3
- data/rbs/fills/rubygems/0/dependency.rbs +193 -0
- data/rbs/fills/tuple/tuple.rbs +28 -0
- data/rbs/shims/ast/0/node.rbs +1 -1
- data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
- data/solargraph.gemspec +2 -1
- metadata +12 -7
- data/lib/solargraph/type_checker/checks.rb +0 -124
- data/lib/solargraph/type_checker/param_def.rb +0 -37
- data/lib/solargraph/yard_map/to_method.rb +0 -89
data/lib/solargraph/api_map.rb
CHANGED
|
@@ -24,12 +24,26 @@ module Solargraph
|
|
|
24
24
|
attr_reader :missing_docs
|
|
25
25
|
|
|
26
26
|
# @param pins [Array<Solargraph::Pin::Base>]
|
|
27
|
-
|
|
27
|
+
# @param loose_unions [Boolean] if true, a potential type can be
|
|
28
|
+
# inferred if ANY of the UniqueTypes in the base chain's
|
|
29
|
+
# ComplexType match it. If false, every single UniqueTypes in
|
|
30
|
+
# the base must be ALL able to independently provide this
|
|
31
|
+
# type. The former is useful during completion, but the
|
|
32
|
+
# latter is best for typechecking at higher levels.
|
|
33
|
+
#
|
|
34
|
+
def initialize pins: [], loose_unions: true
|
|
28
35
|
@source_map_hash = {}
|
|
29
36
|
@cache = Cache.new
|
|
37
|
+
@loose_unions = loose_unions
|
|
30
38
|
index pins
|
|
31
39
|
end
|
|
32
40
|
|
|
41
|
+
# @param out [StringIO, IO, nil] output stream for logging
|
|
42
|
+
# @return [void]
|
|
43
|
+
def self.reset_core out: nil
|
|
44
|
+
@@core_map = RbsMap::CoreMap.new
|
|
45
|
+
end
|
|
46
|
+
|
|
33
47
|
#
|
|
34
48
|
# This is a mutable object, which is cached in the Chain class -
|
|
35
49
|
# if you add any fields which change the results of calls (not
|
|
@@ -39,7 +53,7 @@ module Solargraph
|
|
|
39
53
|
# @param other [Object]
|
|
40
54
|
def eql?(other)
|
|
41
55
|
self.class == other.class &&
|
|
42
|
-
# @sg-ignore
|
|
56
|
+
# @sg-ignore flow sensitive typing needs to handle self.class == other.class
|
|
43
57
|
equality_fields == other.equality_fields
|
|
44
58
|
end
|
|
45
59
|
|
|
@@ -48,10 +62,13 @@ module Solargraph
|
|
|
48
62
|
self.eql?(other)
|
|
49
63
|
end
|
|
50
64
|
|
|
65
|
+
# @return [Integer]
|
|
51
66
|
def hash
|
|
52
67
|
equality_fields.hash
|
|
53
68
|
end
|
|
54
69
|
|
|
70
|
+
attr_reader :loose_unions
|
|
71
|
+
|
|
55
72
|
def to_s
|
|
56
73
|
self.class.to_s
|
|
57
74
|
end
|
|
@@ -98,11 +115,11 @@ module Solargraph
|
|
|
98
115
|
end
|
|
99
116
|
unresolved_requires = (bench.external_requires + conventions_environ.requires + bench.workspace.config.required).to_a.compact.uniq
|
|
100
117
|
recreate_docmap = @unresolved_requires != unresolved_requires ||
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
workspace.rbs_collection_path != bench.workspace.rbs_collection_path ||
|
|
119
|
+
@doc_map.any_uncached?
|
|
120
|
+
|
|
104
121
|
if recreate_docmap
|
|
105
|
-
@doc_map = DocMap.new(unresolved_requires,
|
|
122
|
+
@doc_map = DocMap.new(unresolved_requires, bench.workspace, out: nil) # @todo Implement gem preferences
|
|
106
123
|
@unresolved_requires = @doc_map.unresolved_requires
|
|
107
124
|
end
|
|
108
125
|
@cache.clear if store.update(@@core_map.pins, @doc_map.pins, conventions_environ.pins, iced_pins, live_pins)
|
|
@@ -114,27 +131,17 @@ module Solargraph
|
|
|
114
131
|
# that this overload of 'protected' will typecheck @sg-ignore
|
|
115
132
|
# @sg-ignore
|
|
116
133
|
protected def equality_fields
|
|
117
|
-
[self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires]
|
|
134
|
+
[self.class, @source_map_hash, conventions_environ, @doc_map, @unresolved_requires, @missing_docs, @loose_unions]
|
|
118
135
|
end
|
|
119
136
|
|
|
120
137
|
# @return [DocMap]
|
|
121
138
|
def doc_map
|
|
122
|
-
@doc_map ||= DocMap.new([],
|
|
139
|
+
@doc_map ||= DocMap.new([], Workspace.new('.'))
|
|
123
140
|
end
|
|
124
141
|
|
|
125
142
|
# @return [::Array<Gem::Specification>]
|
|
126
143
|
def uncached_gemspecs
|
|
127
|
-
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# @return [::Array<Gem::Specification>]
|
|
131
|
-
def uncached_rbs_collection_gemspecs
|
|
132
|
-
@doc_map.uncached_rbs_collection_gemspecs
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# @return [::Array<Gem::Specification>]
|
|
136
|
-
def uncached_yard_gemspecs
|
|
137
|
-
@doc_map.uncached_yard_gemspecs
|
|
144
|
+
doc_map.uncached_gemspecs || []
|
|
138
145
|
end
|
|
139
146
|
|
|
140
147
|
# @return [Enumerable<Pin::Base>]
|
|
@@ -142,9 +149,10 @@ module Solargraph
|
|
|
142
149
|
@@core_map.pins
|
|
143
150
|
end
|
|
144
151
|
|
|
145
|
-
# @param name [String]
|
|
152
|
+
# @param name [String, nil]
|
|
146
153
|
# @return [YARD::Tags::MacroDirective, nil]
|
|
147
154
|
def named_macro name
|
|
155
|
+
# @sg-ignore Need to add nil check here
|
|
148
156
|
store.named_macros[name]
|
|
149
157
|
end
|
|
150
158
|
|
|
@@ -180,10 +188,11 @@ module Solargraph
|
|
|
180
188
|
# Create an ApiMap with a workspace in the specified directory.
|
|
181
189
|
#
|
|
182
190
|
# @param directory [String]
|
|
191
|
+
# @param loose_unions [Boolean] See #initialize
|
|
183
192
|
#
|
|
184
193
|
# @return [ApiMap]
|
|
185
|
-
def self.load directory
|
|
186
|
-
api_map = new
|
|
194
|
+
def self.load directory, loose_unions: true
|
|
195
|
+
api_map = new(loose_unions: loose_unions)
|
|
187
196
|
workspace = Solargraph::Workspace.new(directory)
|
|
188
197
|
# api_map.catalog Bench.new(workspace: workspace)
|
|
189
198
|
library = Library.new(workspace)
|
|
@@ -192,18 +201,19 @@ module Solargraph
|
|
|
192
201
|
api_map
|
|
193
202
|
end
|
|
194
203
|
|
|
195
|
-
# @param out [IO, nil]
|
|
204
|
+
# @param out [StringIO, IO, nil]
|
|
205
|
+
# @param rebuild [Boolean] whether to rebuild the pins even if they are cached
|
|
196
206
|
# @return [void]
|
|
197
|
-
def
|
|
198
|
-
|
|
207
|
+
def cache_all_for_doc_map! out: $stderr, rebuild: false
|
|
208
|
+
doc_map.cache_doc_map_gems!(out, rebuild: rebuild)
|
|
199
209
|
end
|
|
200
210
|
|
|
201
211
|
# @param gemspec [Gem::Specification]
|
|
202
212
|
# @param rebuild [Boolean]
|
|
203
|
-
# @param out [IO, nil]
|
|
213
|
+
# @param out [StringIO, IO, nil]
|
|
204
214
|
# @return [void]
|
|
205
215
|
def cache_gem(gemspec, rebuild: false, out: nil)
|
|
206
|
-
|
|
216
|
+
doc_map.cache(gemspec, rebuild: rebuild, out: out)
|
|
207
217
|
end
|
|
208
218
|
|
|
209
219
|
class << self
|
|
@@ -215,18 +225,19 @@ module Solargraph
|
|
|
215
225
|
#
|
|
216
226
|
#
|
|
217
227
|
# @param directory [String]
|
|
218
|
-
# @param out [IO] The output stream for messages
|
|
228
|
+
# @param out [IO, StringIO, nil] The output stream for messages
|
|
229
|
+
# @param loose_unions [Boolean] See #initialize
|
|
219
230
|
#
|
|
220
231
|
# @return [ApiMap]
|
|
221
|
-
def self.load_with_cache directory, out
|
|
222
|
-
api_map = load(directory)
|
|
232
|
+
def self.load_with_cache directory, out = $stderr, loose_unions: true
|
|
233
|
+
api_map = load(directory, loose_unions: loose_unions)
|
|
223
234
|
if api_map.uncached_gemspecs.empty?
|
|
224
235
|
logger.info { "All gems cached for #{directory}" }
|
|
225
236
|
return api_map
|
|
226
237
|
end
|
|
227
238
|
|
|
228
|
-
api_map.
|
|
229
|
-
load(directory)
|
|
239
|
+
api_map.cache_all_for_doc_map!(out: out)
|
|
240
|
+
load(directory, loose_unions: loose_unions)
|
|
230
241
|
end
|
|
231
242
|
|
|
232
243
|
# @return [Array<Solargraph::Pin::Base>]
|
|
@@ -328,29 +339,47 @@ module Solargraph
|
|
|
328
339
|
# @param namespace [String] A fully qualified namespace
|
|
329
340
|
# @param scope [Symbol] :instance or :class
|
|
330
341
|
# @return [Array<Solargraph::Pin::InstanceVariable>]
|
|
331
|
-
def get_instance_variable_pins
|
|
342
|
+
def get_instance_variable_pins namespace, scope = :instance
|
|
332
343
|
result = []
|
|
333
344
|
used = [namespace]
|
|
334
345
|
result.concat store.get_instance_variables(namespace, scope)
|
|
335
346
|
sc_fqns = namespace
|
|
336
347
|
while (sc = store.get_superclass(sc_fqns))
|
|
348
|
+
# @sg-ignore flow sensitive typing needs to handle "if foo = bar"
|
|
337
349
|
sc_fqns = store.constants.dereference(sc)
|
|
338
350
|
result.concat store.get_instance_variables(sc_fqns, scope)
|
|
339
351
|
end
|
|
340
352
|
result
|
|
341
353
|
end
|
|
342
354
|
|
|
343
|
-
#
|
|
344
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
355
|
+
# Find a variable pin by name and where it is used.
|
|
356
|
+
#
|
|
357
|
+
# Resolves our most specific view of this variable's type by
|
|
358
|
+
# preferring pins created by flow-sensitive typing when we have
|
|
359
|
+
# them based on the Closure and Location.
|
|
360
|
+
#
|
|
361
|
+
# @param candidates [Array<Pin::BaseVariable>]
|
|
362
|
+
# @param name [String]
|
|
363
|
+
# @param closure [Pin::Closure]
|
|
364
|
+
# @param location [Location]
|
|
365
|
+
#
|
|
366
|
+
# @return [Pin::BaseVariable, nil]
|
|
367
|
+
def var_at_location(candidates, name, closure, location)
|
|
368
|
+
with_correct_name = candidates.select { |pin| pin.name == name}
|
|
369
|
+
vars_at_location = with_correct_name.reject do |pin|
|
|
370
|
+
# visible_at? excludes the starting position, but we want to
|
|
371
|
+
# include it for this purpose
|
|
372
|
+
(!pin.visible_at?(closure, location) && !pin.starts_at?(location))
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
vars_at_location.inject(&:combine_with)
|
|
347
376
|
end
|
|
348
377
|
|
|
349
378
|
# Get an array of class variable pins for a namespace.
|
|
350
379
|
#
|
|
351
380
|
# @param namespace [String] A fully qualified namespace
|
|
352
381
|
# @return [Enumerable<Solargraph::Pin::ClassVariable>]
|
|
353
|
-
def get_class_variable_pins
|
|
382
|
+
def get_class_variable_pins namespace
|
|
354
383
|
prefer_non_nil_variables(store.get_class_variables(namespace))
|
|
355
384
|
end
|
|
356
385
|
|
|
@@ -512,7 +541,8 @@ module Solargraph
|
|
|
512
541
|
fqns = rooted_type.namespace
|
|
513
542
|
namespace_pin = store.get_path_pins(fqns).first
|
|
514
543
|
methods = if namespace_pin.is_a?(Pin::Constant)
|
|
515
|
-
type = namespace_pin.
|
|
544
|
+
type = namespace_pin.typify(self)
|
|
545
|
+
type = namespace_pin.probe(self) unless type.defined?
|
|
516
546
|
if type.defined?
|
|
517
547
|
namespace_pin = store.get_path_pins(type.namespace).first
|
|
518
548
|
get_methods(type.namespace, scope: scope, visibility: visibility).select { |p| p.name == name }
|
|
@@ -589,6 +619,7 @@ module Solargraph
|
|
|
589
619
|
# @param cursor [Source::Cursor]
|
|
590
620
|
# @return [SourceMap::Clip]
|
|
591
621
|
def clip cursor
|
|
622
|
+
# @sg-ignore Need to add nil check here
|
|
592
623
|
raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
|
|
593
624
|
|
|
594
625
|
SourceMap::Clip.new(self, cursor)
|
|
@@ -636,12 +667,16 @@ module Solargraph
|
|
|
636
667
|
# @todo If two literals are different values of the same type, it would
|
|
637
668
|
# make more sense for super_and_sub? to return true, but there are a
|
|
638
669
|
# few callers that currently expect this to be false.
|
|
670
|
+
# @sg-ignore flow-sensitive typing should be able to handle redefinition
|
|
639
671
|
return false if sup.literal? && sub.literal? && sup.to_s != sub.to_s
|
|
672
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
640
673
|
sup = sup.simplify_literals.to_s
|
|
674
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
641
675
|
sub = sub.simplify_literals.to_s
|
|
642
676
|
return true if sup == sub
|
|
643
677
|
sc_fqns = sub
|
|
644
678
|
while (sc = store.get_superclass(sc_fqns))
|
|
679
|
+
# @sg-ignore flow sensitive typing needs to handle "if foo = bar"
|
|
645
680
|
sc_new = store.constants.dereference(sc)
|
|
646
681
|
# Cyclical inheritance is invalid
|
|
647
682
|
return false if sc_new == sc_fqns
|
|
@@ -669,13 +704,21 @@ module Solargraph
|
|
|
669
704
|
with_resolved_aliases = pins.map do |pin|
|
|
670
705
|
next pin unless pin.is_a?(Pin::MethodAlias)
|
|
671
706
|
resolved = resolve_method_alias(pin)
|
|
707
|
+
# @sg-ignore Need to add nil check here
|
|
672
708
|
next nil if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
|
|
673
709
|
resolved
|
|
674
710
|
end.compact
|
|
675
|
-
logger.debug
|
|
711
|
+
logger.debug do
|
|
712
|
+
"ApiMap#resolve_method_aliases(pins=#{pins.map(&:name)}, visibility=#{visibility}) => #{with_resolved_aliases.map(&:name)}"
|
|
713
|
+
end
|
|
676
714
|
GemPins.combine_method_pins_by_path(with_resolved_aliases)
|
|
677
715
|
end
|
|
678
716
|
|
|
717
|
+
# @return [Workspace]
|
|
718
|
+
def workspace
|
|
719
|
+
doc_map.workspace
|
|
720
|
+
end
|
|
721
|
+
|
|
679
722
|
# @param fq_reference_tag [String] A fully qualified whose method should be pulled in
|
|
680
723
|
# @param namespace_pin [Pin::Base] Namespace pin for the rooted_type
|
|
681
724
|
# parameter - used to pull generics information
|
|
@@ -775,11 +818,13 @@ module Solargraph
|
|
|
775
818
|
if scope == :instance
|
|
776
819
|
store.get_includes(fqns).reverse.each do |ref|
|
|
777
820
|
in_tag = dereference(ref)
|
|
821
|
+
# @sg-ignore Need to add nil check here
|
|
778
822
|
result.concat inner_get_methods_from_reference(in_tag, namespace_pin, rooted_type, scope, visibility, deep, skip, true)
|
|
779
823
|
end
|
|
780
824
|
rooted_sc_tag = qualify_superclass(rooted_tag)
|
|
781
825
|
unless rooted_sc_tag.nil?
|
|
782
|
-
result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
|
|
826
|
+
result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
|
|
827
|
+
visibility, true, skip, no_core)
|
|
783
828
|
end
|
|
784
829
|
else
|
|
785
830
|
logger.info { "ApiMap#inner_get_methods(#{fqns}, #{scope}, #{visibility}, #{deep}, #{skip}) - looking for get_extends() from #{fqns}" }
|
|
@@ -789,7 +834,8 @@ module Solargraph
|
|
|
789
834
|
end
|
|
790
835
|
rooted_sc_tag = qualify_superclass(rooted_tag)
|
|
791
836
|
unless rooted_sc_tag.nil?
|
|
792
|
-
result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
|
|
837
|
+
result.concat inner_get_methods_from_reference(rooted_sc_tag, namespace_pin, rooted_type, scope,
|
|
838
|
+
visibility, true, skip, true)
|
|
793
839
|
end
|
|
794
840
|
unless no_core || fqns.empty?
|
|
795
841
|
type = get_namespace_type(fqns)
|
|
@@ -841,18 +887,21 @@ module Solargraph
|
|
|
841
887
|
|
|
842
888
|
include Logging
|
|
843
889
|
|
|
844
|
-
private
|
|
845
|
-
|
|
846
890
|
# @param alias_pin [Pin::MethodAlias]
|
|
847
891
|
# @return [Pin::Method, nil]
|
|
848
892
|
def resolve_method_alias(alias_pin)
|
|
849
893
|
ancestors = store.get_ancestors(alias_pin.full_context.reduce_class_type.tag)
|
|
894
|
+
# @type [Pin::Method, nil]
|
|
850
895
|
original = nil
|
|
851
896
|
|
|
852
897
|
# Search each ancestor for the original method
|
|
853
898
|
ancestors.each do |ancestor_fqns|
|
|
854
899
|
next if ancestor_fqns.nil?
|
|
855
|
-
ancestor_method_path =
|
|
900
|
+
ancestor_method_path = if alias_pin.original == 'new' && alias_pin.scope == :class
|
|
901
|
+
"#{ancestor_fqns}#initialize"
|
|
902
|
+
else
|
|
903
|
+
"#{ancestor_fqns}#{alias_pin.scope == :instance ? '#' : '.'}#{alias_pin.original}"
|
|
904
|
+
end
|
|
856
905
|
|
|
857
906
|
# Search for the original method in the ancestor
|
|
858
907
|
original = store.get_path_pins(ancestor_method_path).find do |candidate_pin|
|
|
@@ -864,14 +913,20 @@ module Solargraph
|
|
|
864
913
|
break resolved if resolved
|
|
865
914
|
end
|
|
866
915
|
|
|
867
|
-
candidate_pin.is_a?(Pin::Method)
|
|
916
|
+
candidate_pin.is_a?(Pin::Method)
|
|
868
917
|
end
|
|
869
918
|
|
|
870
919
|
break if original
|
|
871
920
|
end
|
|
921
|
+
if original.nil?
|
|
922
|
+
# :nocov:
|
|
923
|
+
Solargraph.assert_or_log(:alias_target_missing) { "Rejecting alias - target is missing while looking for #{alias_pin.full_context.tag} #{alias_pin.original} in #{alias_pin.scope} scope = #{alias_pin.inspect}" }
|
|
924
|
+
return nil
|
|
925
|
+
# :nocov:
|
|
926
|
+
end
|
|
872
927
|
|
|
873
928
|
# @sg-ignore ignore `received nil` for original
|
|
874
|
-
create_resolved_alias_pin(alias_pin, original)
|
|
929
|
+
create_resolved_alias_pin(alias_pin, original)
|
|
875
930
|
end
|
|
876
931
|
|
|
877
932
|
# Fast path for creating resolved alias pins without individual method stack lookups
|
|
@@ -916,7 +971,7 @@ module Solargraph
|
|
|
916
971
|
# @param rooted_type [ComplexType]
|
|
917
972
|
# @param pins [Enumerable<Pin::Base>]
|
|
918
973
|
# @return [Array<Pin::Base>]
|
|
919
|
-
def erase_generics
|
|
974
|
+
def erase_generics namespace_pin, rooted_type, pins
|
|
920
975
|
return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
|
|
921
976
|
|
|
922
977
|
logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
|
|
@@ -927,7 +982,7 @@ module Solargraph
|
|
|
927
982
|
|
|
928
983
|
# @param namespace_pin [Pin::Namespace]
|
|
929
984
|
# @param rooted_type [ComplexType]
|
|
930
|
-
def should_erase_generics_when_done?
|
|
985
|
+
def should_erase_generics_when_done? namespace_pin, rooted_type
|
|
931
986
|
has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
|
|
932
987
|
end
|
|
933
988
|
|
|
@@ -938,7 +993,7 @@ module Solargraph
|
|
|
938
993
|
|
|
939
994
|
# @param namespace_pin [Pin::Namespace]
|
|
940
995
|
# @param rooted_type [ComplexType]
|
|
941
|
-
def can_resolve_generics?
|
|
996
|
+
def can_resolve_generics? namespace_pin, rooted_type
|
|
942
997
|
has_generics?(namespace_pin) && !rooted_type.all_params.empty?
|
|
943
998
|
end
|
|
944
999
|
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class ComplexType
|
|
5
|
+
# Checks whether a type can be used in a given situation
|
|
6
|
+
class Conformance
|
|
7
|
+
# @param api_map [ApiMap]
|
|
8
|
+
# @param inferred [ComplexType::UniqueType]
|
|
9
|
+
# @param expected [ComplexType::UniqueType]
|
|
10
|
+
# @param situation [:method_call, :return_type]
|
|
11
|
+
# @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match,
|
|
12
|
+
# :allow_any_match, :allow_undefined, :allow_unresolved_generic,
|
|
13
|
+
# :allow_unmatched_interface>]
|
|
14
|
+
# @param variance [:invariant, :covariant, :contravariant]
|
|
15
|
+
def initialize api_map, inferred, expected,
|
|
16
|
+
situation = :method_call, rules = [],
|
|
17
|
+
variance: inferred.erased_variance(situation)
|
|
18
|
+
@api_map = api_map
|
|
19
|
+
@inferred = inferred
|
|
20
|
+
@expected = expected
|
|
21
|
+
@situation = situation
|
|
22
|
+
@rules = rules
|
|
23
|
+
@variance = variance
|
|
24
|
+
# :nocov:
|
|
25
|
+
unless expected.is_a?(UniqueType)
|
|
26
|
+
# @sg-ignore This should never happen and the typechecker is angry about it
|
|
27
|
+
raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}"
|
|
28
|
+
end
|
|
29
|
+
# :nocov:
|
|
30
|
+
return if inferred.is_a?(UniqueType)
|
|
31
|
+
# :nocov:
|
|
32
|
+
# @sg-ignore This should never happen and the typechecker is angry about it
|
|
33
|
+
raise "Inferred type must be a UniqueType, got #{inferred.class} in #{inferred.inspect}"
|
|
34
|
+
# :nocov:
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def conforms_to_unique_type?
|
|
38
|
+
unless expected.is_a?(UniqueType)
|
|
39
|
+
# :nocov:
|
|
40
|
+
raise "Expected type must be a UniqueType, got #{expected.class} in #{expected.inspect}"
|
|
41
|
+
# :nocov:
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
return true if ignore_interface?
|
|
45
|
+
return true if conforms_via_reverse_match?
|
|
46
|
+
|
|
47
|
+
downcast_inferred = inferred.downcast_to_literal_if_possible
|
|
48
|
+
downcast_expected = expected.downcast_to_literal_if_possible
|
|
49
|
+
if (downcast_inferred.name != inferred.name) || (downcast_expected.name != expected.name)
|
|
50
|
+
return with_new_types(downcast_inferred, downcast_expected).conforms_to_unique_type?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if rules.include?(:allow_subtype_skew) && !expected.all_params.empty?
|
|
54
|
+
# parameters are not considered in this case
|
|
55
|
+
return with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
return with_new_types(inferred.erase_parameters, expected).conforms_to_unique_type? if only_inferred_parameters?
|
|
59
|
+
|
|
60
|
+
return conforms_via_stripped_expected_parameters? if can_strip_expected_parameters?
|
|
61
|
+
|
|
62
|
+
return true if inferred == expected
|
|
63
|
+
|
|
64
|
+
return false unless erased_type_conforms?
|
|
65
|
+
|
|
66
|
+
return true if inferred.all_params.empty? && rules.include?(:allow_empty_params)
|
|
67
|
+
|
|
68
|
+
# at this point we know the erased type is fine - time to look at parameters
|
|
69
|
+
|
|
70
|
+
# there's an implicit 'any' on the expectation parameters
|
|
71
|
+
# if there are none specified
|
|
72
|
+
return true if expected.all_params.empty?
|
|
73
|
+
|
|
74
|
+
return false unless key_types_conform?
|
|
75
|
+
|
|
76
|
+
subtypes_conform?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def only_inferred_parameters?
|
|
82
|
+
!expected.parameters? && inferred.parameters?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def conforms_via_stripped_expected_parameters?
|
|
86
|
+
with_new_types(inferred, expected.erase_parameters).conforms_to_unique_type?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def ignore_interface?
|
|
90
|
+
(expected.any?(&:interface?) && rules.include?(:allow_unmatched_interface)) ||
|
|
91
|
+
(inferred.interface? && rules.include?(:allow_unmatched_interface))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def can_strip_expected_parameters?
|
|
95
|
+
expected.parameters? && !inferred.parameters? && rules.include?(:allow_empty_params)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def conforms_via_reverse_match?
|
|
99
|
+
return false unless rules.include? :allow_reverse_match
|
|
100
|
+
|
|
101
|
+
expected.conforms_to?(api_map, inferred, situation,
|
|
102
|
+
rules - [:allow_reverse_match],
|
|
103
|
+
variance: variance)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def erased_type_conforms?
|
|
107
|
+
case variance
|
|
108
|
+
when :invariant
|
|
109
|
+
return false unless inferred.name == expected.name
|
|
110
|
+
when :covariant
|
|
111
|
+
# covariant: we can pass in a more specific type
|
|
112
|
+
# we contain the expected mix-in, or we have a more specific type
|
|
113
|
+
return false unless api_map.type_include?(inferred.name, expected.name) ||
|
|
114
|
+
api_map.super_and_sub?(expected.name, inferred.name) ||
|
|
115
|
+
inferred.name == expected.name
|
|
116
|
+
when :contravariant
|
|
117
|
+
# contravariant: we can pass in a more general type
|
|
118
|
+
# we contain the expected mix-in, or we have a more general type
|
|
119
|
+
return false unless api_map.type_include?(inferred.name, expected.name) ||
|
|
120
|
+
api_map.super_and_sub?(inferred.name, expected.name) ||
|
|
121
|
+
inferred.name == expected.name
|
|
122
|
+
else
|
|
123
|
+
# :nocov:
|
|
124
|
+
raise "Unknown variance: #{variance.inspect}"
|
|
125
|
+
# :nocov:
|
|
126
|
+
end
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def key_types_conform?
|
|
131
|
+
return true if expected.key_types.empty?
|
|
132
|
+
|
|
133
|
+
return false if inferred.key_types.empty?
|
|
134
|
+
|
|
135
|
+
unless ComplexType.new(inferred.key_types).conforms_to?(api_map,
|
|
136
|
+
ComplexType.new(expected.key_types),
|
|
137
|
+
situation,
|
|
138
|
+
rules,
|
|
139
|
+
variance: inferred.parameter_variance(situation))
|
|
140
|
+
return false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def subtypes_conform?
|
|
147
|
+
return true if expected.subtypes.empty?
|
|
148
|
+
|
|
149
|
+
return true if expected.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined)
|
|
150
|
+
|
|
151
|
+
return true if inferred.subtypes.any?(&:undefined?) && rules.include?(:allow_undefined)
|
|
152
|
+
|
|
153
|
+
return true if inferred.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic)
|
|
154
|
+
|
|
155
|
+
return true if expected.subtypes.all?(&:generic?) && rules.include?(:allow_unresolved_generic)
|
|
156
|
+
|
|
157
|
+
return false if inferred.subtypes.empty?
|
|
158
|
+
|
|
159
|
+
ComplexType.new(inferred.subtypes).conforms_to?(api_map,
|
|
160
|
+
ComplexType.new(expected.subtypes),
|
|
161
|
+
situation,
|
|
162
|
+
rules,
|
|
163
|
+
variance: inferred.parameter_variance(situation))
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @return [self]
|
|
167
|
+
# @param inferred [ComplexType::UniqueType]
|
|
168
|
+
# @param expected [ComplexType::UniqueType]
|
|
169
|
+
def with_new_types inferred, expected
|
|
170
|
+
self.class.new(api_map, inferred, expected, situation, rules, variance: variance)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
attr_reader :api_map, :inferred, :expected, :situation, :rules, :variance
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -73,6 +73,18 @@ module Solargraph
|
|
|
73
73
|
name == 'undefined'
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# Variance of the type ignoring any type parameters
|
|
77
|
+
# @return [Symbol]
|
|
78
|
+
# @param situation [Symbol] The situation in which the variance is being considered.
|
|
79
|
+
def erased_variance situation = :method_call
|
|
80
|
+
# :nocov:
|
|
81
|
+
unless %i[method_call return_type assignment].include?(situation)
|
|
82
|
+
raise "Unknown situation: #{situation.inspect}"
|
|
83
|
+
end
|
|
84
|
+
# :nocov:
|
|
85
|
+
:covariant
|
|
86
|
+
end
|
|
87
|
+
|
|
76
88
|
# @param generics_to_erase [Enumerable<String>]
|
|
77
89
|
# @return [self]
|
|
78
90
|
def erase_generics(generics_to_erase)
|
|
@@ -194,7 +206,7 @@ module Solargraph
|
|
|
194
206
|
# @param other [Object]
|
|
195
207
|
def == other
|
|
196
208
|
return false unless self.class == other.class
|
|
197
|
-
# @sg-ignore
|
|
209
|
+
# @sg-ignore flow sensitive typing should support .class == .class
|
|
198
210
|
tag == other.tag
|
|
199
211
|
end
|
|
200
212
|
|
|
@@ -218,7 +230,9 @@ module Solargraph
|
|
|
218
230
|
end
|
|
219
231
|
|
|
220
232
|
# @yieldparam [UniqueType]
|
|
221
|
-
# @return [
|
|
233
|
+
# @return [void]
|
|
234
|
+
# @overload each_unique_type()
|
|
235
|
+
# @return [Enumerator<UniqueType>]
|
|
222
236
|
def each_unique_type &block
|
|
223
237
|
return enum_for(__method__) unless block_given?
|
|
224
238
|
yield self
|