solargraph 0.54.1 → 0.56.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 +2 -0
- data/.github/workflows/typecheck.yml +3 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +70 -0
- data/README.md +13 -3
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +175 -0
- data/lib/solargraph/api_map/store.rb +79 -126
- data/lib/solargraph/api_map.rb +247 -93
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +13 -1
- data/lib/solargraph/complex_type/unique_type.rb +118 -9
- data/lib/solargraph/complex_type.rb +48 -18
- data/lib/solargraph/convention/base.rb +3 -3
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +101 -0
- data/lib/solargraph/convention.rb +5 -3
- data/lib/solargraph/doc_map.rb +274 -56
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/gem_pins.rb +53 -37
- data/lib/solargraph/language_server/host/message_worker.rb +31 -11
- data/lib/solargraph/language_server/host.rb +24 -13
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
- data/lib/solargraph/language_server/message/extended/document.rb +5 -2
- data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
- data/lib/solargraph/language_server/message/initialize.rb +3 -1
- data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
- data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
- data/lib/solargraph/library.rb +13 -11
- data/lib/solargraph/location.rb +28 -0
- data/lib/solargraph/logging.rb +1 -0
- data/lib/solargraph/parser/comment_ripper.rb +12 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/node_processor.rb +3 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
- data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
- data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
- data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser/region.rb +1 -1
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +326 -43
- data/lib/solargraph/pin/base_variable.rb +18 -10
- data/lib/solargraph/pin/block.rb +3 -3
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +77 -6
- data/lib/solargraph/pin/closure.rb +18 -1
- data/lib/solargraph/pin/common.rb +5 -0
- data/lib/solargraph/pin/delegated_method.rb +20 -1
- data/lib/solargraph/pin/documenting.rb +16 -0
- data/lib/solargraph/pin/keyword.rb +7 -2
- data/lib/solargraph/pin/local_variable.rb +15 -6
- data/lib/solargraph/pin/method.rb +172 -42
- data/lib/solargraph/pin/namespace.rb +17 -9
- data/lib/solargraph/pin/parameter.rb +61 -11
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/reference/override.rb +10 -6
- data/lib/solargraph/pin/reference/require.rb +2 -2
- data/lib/solargraph/pin/signature.rb +42 -0
- data/lib/solargraph/pin/singleton.rb +1 -1
- data/lib/solargraph/pin/symbol.rb +3 -2
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +4 -1
- data/lib/solargraph/pin_cache.rb +185 -0
- data/lib/solargraph/position.rb +16 -0
- data/lib/solargraph/range.rb +16 -0
- data/lib/solargraph/rbs_map/conversions.rb +199 -66
- data/lib/solargraph/rbs_map/core_fills.rb +32 -16
- data/lib/solargraph/rbs_map/core_map.rb +34 -11
- data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
- data/lib/solargraph/rbs_map.rb +74 -16
- data/lib/solargraph/shell.rb +19 -18
- data/lib/solargraph/source/chain/array.rb +12 -8
- data/lib/solargraph/source/chain/block_symbol.rb +1 -1
- data/lib/solargraph/source/chain/block_variable.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +62 -25
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +9 -3
- data/lib/solargraph/source/chain/head.rb +1 -1
- data/lib/solargraph/source/chain/if.rb +6 -1
- data/lib/solargraph/source/chain/link.rb +19 -5
- data/lib/solargraph/source/chain/literal.rb +27 -2
- data/lib/solargraph/source/chain/or.rb +1 -1
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +106 -62
- data/lib/solargraph/source/cursor.rb +1 -11
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source.rb +2 -1
- data/lib/solargraph/source_map/clip.rb +4 -2
- data/lib/solargraph/source_map/mapper.rb +9 -5
- data/lib/solargraph/source_map.rb +0 -17
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +41 -14
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +10 -10
- data/lib/solargraph/views/_namespace.erb +3 -3
- data/lib/solargraph/views/document.erb +10 -10
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +16 -6
- data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -2
- data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +5 -2
- data/lib/solargraph/yard_map/mapper.rb +5 -3
- data/lib/solargraph/yard_map/to_method.rb +4 -2
- data/lib/solargraph/yardoc.rb +6 -9
- data/lib/solargraph.rb +20 -1
- data/rbs/fills/tuple.rbs +150 -0
- data/rbs_collection.yaml +19 -0
- data/solargraph.gemspec +6 -5
- metadata +56 -29
- data/lib/solargraph/cache.rb +0 -77
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'parser'
|
|
4
|
+
|
|
3
5
|
module Solargraph
|
|
4
6
|
class Source
|
|
5
7
|
class Chain
|
|
@@ -8,14 +10,37 @@ module Solargraph
|
|
|
8
10
|
@word ||= "<#{@type}>"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
attr_reader :value
|
|
14
|
+
|
|
11
15
|
# @param type [String]
|
|
12
|
-
|
|
16
|
+
# @param node [Parser::AST::Node, Object]
|
|
17
|
+
def initialize type, node
|
|
18
|
+
if node.is_a?(::Parser::AST::Node)
|
|
19
|
+
if node.type == :true
|
|
20
|
+
@value = true
|
|
21
|
+
elsif node.type == :false
|
|
22
|
+
@value = false
|
|
23
|
+
elsif [:int, :sym].include?(node.type)
|
|
24
|
+
@value = node.children.first
|
|
25
|
+
end
|
|
26
|
+
end
|
|
13
27
|
@type = type
|
|
28
|
+
@literal_type = ComplexType.try_parse(@value.inspect)
|
|
14
29
|
@complex_type = ComplexType.try_parse(type)
|
|
15
30
|
end
|
|
16
31
|
|
|
32
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
33
|
+
protected def equality_fields
|
|
34
|
+
super + [@value, @type, @literal_type, @complex_type]
|
|
35
|
+
end
|
|
36
|
+
|
|
17
37
|
def resolve api_map, name_pin, locals
|
|
18
|
-
|
|
38
|
+
if api_map.super_and_sub?(@complex_type.name, @literal_type.name)
|
|
39
|
+
[Pin::ProxyType.anonymous(@literal_type, source: :chain)]
|
|
40
|
+
else
|
|
41
|
+
# we don't support this value as a literal type
|
|
42
|
+
[Pin::ProxyType.anonymous(@complex_type, source: :chain)]
|
|
43
|
+
end
|
|
19
44
|
end
|
|
20
45
|
end
|
|
21
46
|
end
|
|
@@ -15,7 +15,7 @@ module Solargraph
|
|
|
15
15
|
|
|
16
16
|
def resolve api_map, name_pin, locals
|
|
17
17
|
types = @links.map { |link| link.infer(api_map, name_pin, locals) }
|
|
18
|
-
[Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq))]
|
|
18
|
+
[Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq), source: :chain)]
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -15,7 +15,7 @@ module Solargraph
|
|
|
15
15
|
# @param with_block [Boolean] True if the chain is inside a block
|
|
16
16
|
# @param head [Boolean] True if the call is the start of its chain
|
|
17
17
|
def initialize word, with_block = false
|
|
18
|
-
super(word, [], with_block)
|
|
18
|
+
super(word, nil, [], with_block)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @param api_map [ApiMap]
|
|
@@ -14,10 +14,8 @@ module Solargraph
|
|
|
14
14
|
# expression.
|
|
15
15
|
#
|
|
16
16
|
class Chain
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# values.
|
|
20
|
-
#
|
|
17
|
+
include Equality
|
|
18
|
+
|
|
21
19
|
autoload :Link, 'solargraph/source/chain/link'
|
|
22
20
|
autoload :Call, 'solargraph/source/chain/call'
|
|
23
21
|
autoload :QCall, 'solargraph/source/chain/q_call'
|
|
@@ -41,7 +39,7 @@ module Solargraph
|
|
|
41
39
|
@@inference_invalidation_key = nil
|
|
42
40
|
@@inference_cache = {}
|
|
43
41
|
|
|
44
|
-
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
|
42
|
+
UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
|
|
45
43
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
|
46
44
|
|
|
47
45
|
# @return [::Array<Source::Chain::Link>]
|
|
@@ -49,6 +47,11 @@ module Solargraph
|
|
|
49
47
|
|
|
50
48
|
attr_reader :node
|
|
51
49
|
|
|
50
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
51
|
+
protected def equality_fields
|
|
52
|
+
[links, node]
|
|
53
|
+
end
|
|
54
|
+
|
|
52
55
|
# @param node [Parser::AST::Node, nil]
|
|
53
56
|
# @param links [::Array<Chain::Link>]
|
|
54
57
|
# @param splat [Boolean]
|
|
@@ -72,16 +75,27 @@ module Solargraph
|
|
|
72
75
|
|
|
73
76
|
# Determine potential Pins returned by this chain of words
|
|
74
77
|
#
|
|
75
|
-
# @param api_map [ApiMap]
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
78
|
+
# @param api_map [ApiMap] @param name_pin [Pin::Base] A pin
|
|
79
|
+
# representing the place in which expression is evaluated (e.g.,
|
|
80
|
+
# a Method pin, or a Module or Class pin if not run within a
|
|
81
|
+
# method - both in terms of the closure around the chain, as well
|
|
82
|
+
# as the self type used for any method calls in head position.
|
|
83
|
+
#
|
|
84
|
+
# Requirements for name_pin:
|
|
79
85
|
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
86
|
+
# * name_pin.context: This should be a type representing the
|
|
87
|
+
# namespace where we can look up non-local variables and
|
|
88
|
+
# method names. If it is a Class<X>, we will look up
|
|
89
|
+
# :class scoped methods/variables.
|
|
83
90
|
#
|
|
84
|
-
#
|
|
91
|
+
# * name_pin.binder: Used for method call lookups only
|
|
92
|
+
# (Chain::Call links). For method calls as the first
|
|
93
|
+
# element in the chain, 'name_pin.binder' should be the
|
|
94
|
+
# same as name_pin.context above. For method calls later
|
|
95
|
+
# in the chain (e.g., 'b' in a.b.c), it should represent
|
|
96
|
+
# 'a'.
|
|
97
|
+
#
|
|
98
|
+
# @param locals [::Array<Pin::LocalVariable>] Any local
|
|
85
99
|
# variables / method parameters etc visible by the statement
|
|
86
100
|
#
|
|
87
101
|
# @return [::Array<Pin::Base>] Pins representing possible return
|
|
@@ -98,9 +112,18 @@ module Solargraph
|
|
|
98
112
|
working_pin = name_pin
|
|
99
113
|
links[0..-2].each do |link|
|
|
100
114
|
pins = link.resolve(api_map, working_pin, locals)
|
|
101
|
-
type =
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
type = infer_from_definitions(pins, working_pin, api_map, locals)
|
|
116
|
+
if type.undefined?
|
|
117
|
+
logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" }
|
|
118
|
+
return []
|
|
119
|
+
end
|
|
120
|
+
# We continue to use the context from the head pin, in case
|
|
121
|
+
# we need it to, for instance, provide context for a block
|
|
122
|
+
# evaluation. However, we use the last link's return type
|
|
123
|
+
# for the binder, as this is chaining off of it, and the
|
|
124
|
+
# binder is now the lhs of the rhs we are evaluating.
|
|
125
|
+
working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain)
|
|
126
|
+
logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" }
|
|
104
127
|
end
|
|
105
128
|
links.last.last_context = working_pin
|
|
106
129
|
links.last.resolve(api_map, working_pin, locals)
|
|
@@ -108,30 +131,37 @@ module Solargraph
|
|
|
108
131
|
|
|
109
132
|
# @param api_map [ApiMap]
|
|
110
133
|
# @param name_pin [Pin::Base] The pin for the closure in which this code runs
|
|
111
|
-
# @param locals [::
|
|
134
|
+
# @param locals [::Array<Pin::LocalVariable>]
|
|
112
135
|
# @return [ComplexType]
|
|
113
136
|
# @sg-ignore
|
|
114
137
|
def infer api_map, name_pin, locals
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@@inference_cache = {}
|
|
138
|
+
cache_key = [node, node&.location, links, name_pin&.return_type, locals]
|
|
139
|
+
if @@inference_invalidation_key == api_map.hash
|
|
140
|
+
cached = @@inference_cache[cache_key]
|
|
141
|
+
return cached if cached
|
|
142
|
+
else
|
|
121
143
|
@@inference_invalidation_key = api_map.hash
|
|
144
|
+
@@inference_cache = {}
|
|
122
145
|
end
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
|
|
147
|
+
logger.debug { "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" }
|
|
148
|
+
@@inference_cache[cache_key] = out
|
|
125
149
|
end
|
|
126
150
|
|
|
127
151
|
# @param api_map [ApiMap]
|
|
128
152
|
# @param name_pin [Pin::Base]
|
|
129
|
-
# @param locals [::
|
|
153
|
+
# @param locals [::Array<Pin::LocalVariable>]
|
|
130
154
|
# @return [ComplexType]
|
|
131
155
|
def infer_uncached api_map, name_pin, locals
|
|
132
156
|
pins = define(api_map, name_pin, locals)
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
if pins.empty?
|
|
158
|
+
logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" }
|
|
159
|
+
return ComplexType::UNDEFINED
|
|
160
|
+
end
|
|
161
|
+
type = infer_from_definitions(pins, links.last.last_context, api_map, locals)
|
|
162
|
+
out = maybe_nil(type)
|
|
163
|
+
logger.debug { "Chain#infer_uncached(links=#{self.links.map(&:desc)}, locals=#{locals.map(&:desc)}, name_pin=#{name_pin}, name_pin.closure=#{name_pin.closure.inspect}, name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" }
|
|
164
|
+
out
|
|
135
165
|
end
|
|
136
166
|
|
|
137
167
|
# @return [Boolean]
|
|
@@ -160,6 +190,18 @@ module Solargraph
|
|
|
160
190
|
links.any?(&:nullable?)
|
|
161
191
|
end
|
|
162
192
|
|
|
193
|
+
include Logging
|
|
194
|
+
|
|
195
|
+
def desc
|
|
196
|
+
links.map(&:desc).to_s
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def to_s
|
|
200
|
+
desc
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
include Logging
|
|
204
|
+
|
|
163
205
|
private
|
|
164
206
|
|
|
165
207
|
# @param pins [::Array<Pin::Base>]
|
|
@@ -167,16 +209,18 @@ module Solargraph
|
|
|
167
209
|
# @param api_map [ApiMap]
|
|
168
210
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
|
169
211
|
# @return [ComplexType]
|
|
170
|
-
def
|
|
171
|
-
|
|
212
|
+
def infer_from_definitions pins, context, api_map, locals
|
|
213
|
+
# @type [::Array<ComplexType>]
|
|
214
|
+
types = []
|
|
215
|
+
unresolved_pins = []
|
|
172
216
|
# @todo this param tag shouldn't be needed to probe the type
|
|
173
217
|
# @todo ...but given it is needed, typecheck should complain that it is needed
|
|
174
218
|
# @param pin [Pin::Base]
|
|
175
219
|
pins.each do |pin|
|
|
176
220
|
# Avoid infinite recursion
|
|
177
|
-
next if @@inference_stack.include?(pin
|
|
221
|
+
next if @@inference_stack.include?(pin)
|
|
178
222
|
|
|
179
|
-
@@inference_stack.push pin
|
|
223
|
+
@@inference_stack.push pin
|
|
180
224
|
type = pin.typify(api_map)
|
|
181
225
|
@@inference_stack.pop
|
|
182
226
|
if type.defined?
|
|
@@ -186,41 +230,41 @@ module Solargraph
|
|
|
186
230
|
# that accepts only [Pin::Namespace] as an argument
|
|
187
231
|
type = type.resolve_generics(pin.closure, context.binder)
|
|
188
232
|
end
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
end
|
|
233
|
+
types << type
|
|
234
|
+
else
|
|
235
|
+
unresolved_pins << pin
|
|
193
236
|
end
|
|
194
237
|
end
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@@inference_depth += 1
|
|
200
|
-
# @param pin [Pin::Base]
|
|
201
|
-
pins.each do |pin|
|
|
202
|
-
# Avoid infinite recursion
|
|
203
|
-
next if @@inference_stack.include?(pin.identity)
|
|
204
|
-
|
|
205
|
-
@@inference_stack.push pin.identity
|
|
206
|
-
type = pin.probe(api_map)
|
|
207
|
-
@@inference_stack.pop
|
|
208
|
-
if type.defined?
|
|
209
|
-
possibles.push type
|
|
210
|
-
break if pin.is_a?(Pin::Method)
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
@@inference_depth -= 1
|
|
238
|
+
|
|
239
|
+
# Limit method inference recursion
|
|
240
|
+
if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
|
|
241
|
+
return ComplexType::UNDEFINED
|
|
214
242
|
end
|
|
215
|
-
return ComplexType::UNDEFINED if possibles.empty?
|
|
216
243
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
244
|
+
@@inference_depth += 1
|
|
245
|
+
# @param pin [Pin::Base]
|
|
246
|
+
unresolved_pins.each do |pin|
|
|
247
|
+
# Avoid infinite recursion
|
|
248
|
+
if @@inference_stack.include?(pin.identity)
|
|
249
|
+
next
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
@@inference_stack.push(pin.identity)
|
|
253
|
+
type = pin.probe(api_map)
|
|
254
|
+
@@inference_stack.pop
|
|
255
|
+
types.push type if type
|
|
223
256
|
end
|
|
257
|
+
@@inference_depth -= 1
|
|
258
|
+
|
|
259
|
+
type = if types.empty?
|
|
260
|
+
ComplexType::UNDEFINED
|
|
261
|
+
elsif types.length > 1
|
|
262
|
+
# Move nil to the end by convention
|
|
263
|
+
sorted = types.flat_map(&:items).sort { |a, _| a.tag == 'nil' ? 1 : 0 }
|
|
264
|
+
ComplexType.new(sorted.uniq)
|
|
265
|
+
else
|
|
266
|
+
ComplexType.new(types)
|
|
267
|
+
end
|
|
224
268
|
return type if context.nil? || context.return_type.undefined?
|
|
225
269
|
|
|
226
270
|
type.self_to_type(context.return_type)
|
|
@@ -35,6 +35,7 @@ module Solargraph
|
|
|
35
35
|
# The part of the word before the current position. Given the text
|
|
36
36
|
# `foo.bar`, the start_of_word at position(0, 6) is `ba`.
|
|
37
37
|
#
|
|
38
|
+
# @sg-ignore Improve resolution of String#match below
|
|
38
39
|
# @return [String]
|
|
39
40
|
def start_of_word
|
|
40
41
|
@start_of_word ||= begin
|
|
@@ -103,17 +104,6 @@ module Solargraph
|
|
|
103
104
|
@string ||= source.string_at?(position)
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
# True if the cursor's chain is an assignment to a variable.
|
|
108
|
-
#
|
|
109
|
-
# When the chain is an assignment, `Cursor#word` will contain the
|
|
110
|
-
# variable name.
|
|
111
|
-
#
|
|
112
|
-
# @return [Boolean]
|
|
113
|
-
def assign?
|
|
114
|
-
[:lvasgn, :ivasgn, :gvasgn, :cvasgn].include? chain&.node&.type
|
|
115
|
-
end
|
|
116
|
-
|
|
117
107
|
# Get a cursor pointing to the method that receives the current statement
|
|
118
108
|
# as an argument.
|
|
119
109
|
#
|
|
@@ -32,8 +32,8 @@ module Solargraph
|
|
|
32
32
|
# @return [Source::Chain]
|
|
33
33
|
def chain
|
|
34
34
|
# Special handling for files that end with an integer and a period
|
|
35
|
-
return Chain.new([Chain::Literal.new('Integer'), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
|
|
36
|
-
return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
|
|
35
|
+
return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
|
|
36
|
+
return Chain.new([Chain::Literal.new('Symbol', phrase[1..].to_sym)]) if phrase.start_with?(':') && !phrase.start_with?('::')
|
|
37
37
|
return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i)
|
|
38
38
|
begin
|
|
39
39
|
return Chain.new([]) if phrase.end_with?('..')
|
data/lib/solargraph/source.rb
CHANGED
|
@@ -307,7 +307,7 @@ module Solargraph
|
|
|
307
307
|
|
|
308
308
|
# A hash of line numbers and their associated comments.
|
|
309
309
|
#
|
|
310
|
-
# @return [Hash{Integer => Array<String
|
|
310
|
+
# @return [Hash{Integer => Array<String>, nil}]
|
|
311
311
|
def stringified_comments
|
|
312
312
|
@stringified_comments ||= {}
|
|
313
313
|
end
|
|
@@ -375,6 +375,7 @@ module Solargraph
|
|
|
375
375
|
|
|
376
376
|
protected
|
|
377
377
|
|
|
378
|
+
# @return [Array<Change>]
|
|
378
379
|
def changes
|
|
379
380
|
@changes ||= []
|
|
380
381
|
end
|
|
@@ -11,7 +11,9 @@ module Solargraph
|
|
|
11
11
|
def initialize api_map, cursor
|
|
12
12
|
@api_map = api_map
|
|
13
13
|
@cursor = cursor
|
|
14
|
-
|
|
14
|
+
block_pin = block
|
|
15
|
+
block_pin.rebind(api_map) if block_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(block_pin.receiver).contain?(cursor.range.start)
|
|
16
|
+
@in_block = nil
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
# @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
|
|
@@ -40,7 +42,7 @@ module Solargraph
|
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
|
|
43
|
-
# @return [Array<Pin::
|
|
45
|
+
# @return [Array<Pin::Method>]
|
|
44
46
|
def signify
|
|
45
47
|
return [] unless cursor.argument?
|
|
46
48
|
chain = Parser.chain(cursor.recipient_node, cursor.filename)
|
|
@@ -41,7 +41,7 @@ module Solargraph
|
|
|
41
41
|
s = Position.new(0, 0)
|
|
42
42
|
e = Position.from_offset(code, code.length)
|
|
43
43
|
location = Location.new(filename, Range.new(s, e))
|
|
44
|
-
[[Pin::Namespace.new(location: location, name: '')], []]
|
|
44
|
+
[[Pin::Namespace.new(location: location, name: '', source: :source_map)], []]
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
class << self
|
|
@@ -55,6 +55,7 @@ module Solargraph
|
|
|
55
55
|
|
|
56
56
|
# @return [Array<Solargraph::Pin::Base>]
|
|
57
57
|
def pins
|
|
58
|
+
# @type [Array<Solargraph::Pin::Base>]
|
|
58
59
|
@pins ||= []
|
|
59
60
|
end
|
|
60
61
|
|
|
@@ -140,7 +141,8 @@ module Solargraph
|
|
|
140
141
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
|
141
142
|
visibility: :public,
|
|
142
143
|
explicit: false,
|
|
143
|
-
attribute: true
|
|
144
|
+
attribute: true,
|
|
145
|
+
source: :source_map
|
|
144
146
|
)
|
|
145
147
|
end
|
|
146
148
|
if t.nil? || t.include?('w')
|
|
@@ -151,10 +153,11 @@ module Solargraph
|
|
|
151
153
|
comments: docstring.all.to_s,
|
|
152
154
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
|
153
155
|
visibility: :public,
|
|
154
|
-
attribute: true
|
|
156
|
+
attribute: true,
|
|
157
|
+
source: :source_map
|
|
155
158
|
)
|
|
156
159
|
pins.push method_pin
|
|
157
|
-
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
|
160
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map)
|
|
158
161
|
if pins.last.return_type.defined?
|
|
159
162
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
|
160
163
|
end
|
|
@@ -205,7 +208,8 @@ module Solargraph
|
|
|
205
208
|
namespace = closure_at(source_position) || Pin::ROOT_PIN
|
|
206
209
|
namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
|
|
207
210
|
when 'override'
|
|
208
|
-
pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags
|
|
211
|
+
pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
|
|
212
|
+
source: :source_map)
|
|
209
213
|
when 'macro'
|
|
210
214
|
# @todo Handle macros
|
|
211
215
|
end
|
|
@@ -118,23 +118,6 @@ module Solargraph
|
|
|
118
118
|
_locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
-
# @todo Candidate for deprecation
|
|
122
|
-
#
|
|
123
|
-
# @param other_map [SourceMap]
|
|
124
|
-
# @return [Boolean]
|
|
125
|
-
def try_merge! other_map
|
|
126
|
-
return false if pins.length != other_map.pins.length || locals.length != other_map.locals.length || requires.map(&:name).uniq.sort != other_map.requires.map(&:name).uniq.sort
|
|
127
|
-
|
|
128
|
-
pins.each_index do |i|
|
|
129
|
-
return false unless pins[i].try_merge!(other_map.pins[i])
|
|
130
|
-
end
|
|
131
|
-
locals.each_index do |i|
|
|
132
|
-
return false unless locals[i].try_merge!(other_map.locals[i])
|
|
133
|
-
end
|
|
134
|
-
@source = other_map.source
|
|
135
|
-
true
|
|
136
|
-
end
|
|
137
|
-
|
|
138
121
|
# @param name [String]
|
|
139
122
|
# @return [Array<Location>]
|
|
140
123
|
def references name
|
|
@@ -50,6 +50,8 @@ module Solargraph
|
|
|
50
50
|
# @param inferred [ComplexType]
|
|
51
51
|
# @return [Boolean]
|
|
52
52
|
def any_types_match? api_map, expected, inferred
|
|
53
|
+
expected = expected.downcast_to_literal_if_possible
|
|
54
|
+
inferred = inferred.downcast_to_literal_if_possible
|
|
53
55
|
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
|
54
56
|
# walk through the union expected type and see if any members
|
|
55
57
|
# of the union match the inferred type
|
|
@@ -71,6 +73,8 @@ module Solargraph
|
|
|
71
73
|
# @param expected [ComplexType]
|
|
72
74
|
# @return [Boolean]
|
|
73
75
|
def all_types_match? api_map, inferred, expected
|
|
76
|
+
expected = expected.downcast_to_literal_if_possible
|
|
77
|
+
inferred = inferred.downcast_to_literal_if_possible
|
|
74
78
|
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
|
75
79
|
inferred.each do |inf|
|
|
76
80
|
next if inf.duck_type?
|
|
@@ -29,6 +29,7 @@ module Solargraph
|
|
|
29
29
|
# @todo Smarter directory resolution
|
|
30
30
|
@api_map = api_map || Solargraph::ApiMap.load(File.dirname(filename))
|
|
31
31
|
@rules = Rules.new(level)
|
|
32
|
+
# @type [Array<Range>]
|
|
32
33
|
@marked_ranges = []
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -413,7 +414,7 @@ module Solargraph
|
|
|
413
414
|
# @param locals [Array<Pin::LocalVariable>]
|
|
414
415
|
# @param location [Location]
|
|
415
416
|
# @param pin [Pin::Method]
|
|
416
|
-
# @param params [Hash{String =>
|
|
417
|
+
# @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}]
|
|
417
418
|
# @param idx [Integer]
|
|
418
419
|
#
|
|
419
420
|
# @return [Array<Problem>]
|
|
@@ -467,13 +468,24 @@ module Solargraph
|
|
|
467
468
|
end
|
|
468
469
|
|
|
469
470
|
# @param pin [Pin::Method]
|
|
470
|
-
# @return [Hash{String => Hash{Symbol =>
|
|
471
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
471
472
|
def param_hash(pin)
|
|
472
|
-
|
|
473
|
-
return {} if tags.empty?
|
|
473
|
+
# @type [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
474
474
|
result = {}
|
|
475
|
+
pin.parameters.each do |param|
|
|
476
|
+
type = param.typify(api_map)
|
|
477
|
+
next if type.nil? || type.undefined?
|
|
478
|
+
result[param.name.to_s] = {
|
|
479
|
+
tagged: type.tags,
|
|
480
|
+
qualified: type
|
|
481
|
+
}
|
|
482
|
+
end
|
|
483
|
+
# see if we have additional tags to pay attention to from YARD -
|
|
484
|
+
# e.g., kwargs in a **restkwargs splat
|
|
485
|
+
tags = pin.docstring.tags(:param)
|
|
475
486
|
tags.each do |tag|
|
|
476
|
-
next if
|
|
487
|
+
next if result.key? tag.name.to_s
|
|
488
|
+
next if tag.types.nil?
|
|
477
489
|
result[tag.name.to_s] = {
|
|
478
490
|
tagged: tag.types.join(', '),
|
|
479
491
|
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
|
@@ -483,15 +495,29 @@ module Solargraph
|
|
|
483
495
|
end
|
|
484
496
|
|
|
485
497
|
# @param pins [Array<Pin::Method>]
|
|
486
|
-
# @return [Hash{String => Hash{Symbol =>
|
|
498
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
487
499
|
def first_param_hash(pins)
|
|
488
|
-
pins.
|
|
500
|
+
return {} if pins.empty?
|
|
501
|
+
first_pin_type = pins.first.typify(api_map)
|
|
502
|
+
first_pin = pins.first.proxy first_pin_type
|
|
503
|
+
param_names = first_pin.parameter_names
|
|
504
|
+
results = param_hash(first_pin)
|
|
505
|
+
pins[1..].each do |pin|
|
|
489
506
|
# @todo this assignment from parametric use of Hash should not lose its generic
|
|
490
507
|
# @type [Hash{String => Hash{Symbol => BasicObject}}]
|
|
491
|
-
|
|
492
|
-
|
|
508
|
+
|
|
509
|
+
# documentation of types in superclasses should fail back to
|
|
510
|
+
# subclasses if the subclass hasn't documented something
|
|
511
|
+
superclass_results = param_hash(pin)
|
|
512
|
+
superclass_results.each do |param_name, details|
|
|
513
|
+
next unless param_names.include?(param_name)
|
|
514
|
+
|
|
515
|
+
results[param_name] ||= {}
|
|
516
|
+
results[param_name][:tagged] ||= details[:tagged]
|
|
517
|
+
results[param_name][:qualified] ||= details[:qualified]
|
|
518
|
+
end
|
|
493
519
|
end
|
|
494
|
-
|
|
520
|
+
results
|
|
495
521
|
end
|
|
496
522
|
|
|
497
523
|
# @param pin [Pin::Base]
|
|
@@ -512,7 +538,7 @@ module Solargraph
|
|
|
512
538
|
!internal? pin
|
|
513
539
|
end
|
|
514
540
|
|
|
515
|
-
# @param pin [Pin::
|
|
541
|
+
# @param pin [Pin::BaseVariable]
|
|
516
542
|
def declared_externally? pin
|
|
517
543
|
return true if pin.assignment.nil?
|
|
518
544
|
chain = Solargraph::Parser.chain(pin.assignment, filename)
|
|
@@ -562,7 +588,7 @@ module Solargraph
|
|
|
562
588
|
return [] unless pin.explicit?
|
|
563
589
|
return [] if parameters.empty? && arguments.empty?
|
|
564
590
|
return [] if pin.anon_splat?
|
|
565
|
-
unchecked = arguments.
|
|
591
|
+
unchecked = arguments.dup # creates copy of and unthaws array
|
|
566
592
|
add_params = 0
|
|
567
593
|
if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
|
|
568
594
|
return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
|
|
@@ -584,7 +610,8 @@ module Solargraph
|
|
|
584
610
|
kwargs.delete param.name.to_sym
|
|
585
611
|
settled_kwargs += 1
|
|
586
612
|
elsif param.decl == :kwarg
|
|
587
|
-
|
|
613
|
+
last_arg_last_link = arguments.last.links.last
|
|
614
|
+
return [] if last_arg_last_link.is_a?(Solargraph::Source::Chain::Hash) && last_arg_last_link.splatted?
|
|
588
615
|
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
|
589
616
|
end
|
|
590
617
|
end
|
|
@@ -638,7 +665,7 @@ module Solargraph
|
|
|
638
665
|
(pin.closure && pin.closure.docstring.has_tag?('abstract'))
|
|
639
666
|
end
|
|
640
667
|
|
|
641
|
-
# @param pin [Pin::
|
|
668
|
+
# @param pin [Pin::Method]
|
|
642
669
|
# @return [Array<Source::Chain>]
|
|
643
670
|
def fake_args_for(pin)
|
|
644
671
|
args = []
|
data/lib/solargraph/version.rb
CHANGED