solargraph 0.54.5 → 0.55.4
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/typecheck.yml +1 -1
- data/CHANGELOG.md +27 -0
- data/lib/solargraph/api_map/store.rb +9 -4
- data/lib/solargraph/api_map.rb +116 -39
- data/lib/solargraph/complex_type/type_methods.rb +1 -0
- data/lib/solargraph/complex_type/unique_type.rb +91 -9
- data/lib/solargraph/complex_type.rb +35 -6
- 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 +1 -0
- data/lib/solargraph/doc_map.rb +83 -23
- data/lib/solargraph/gem_pins.rb +2 -1
- data/lib/solargraph/language_server/host/message_worker.rb +10 -7
- data/lib/solargraph/language_server/host.rb +3 -1
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -0
- data/lib/solargraph/location.rb +8 -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 +14 -0
- data/lib/solargraph/parser/node_processor.rb +3 -2
- data/lib/solargraph/parser/parser_gem/class_methods.rb +9 -0
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
- 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 +2 -1
- 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 +34 -5
- data/lib/solargraph/pin/base_variable.rb +7 -1
- data/lib/solargraph/pin/block.rb +2 -0
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +5 -3
- data/lib/solargraph/pin/closure.rb +6 -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 +7 -1
- data/lib/solargraph/pin/method.rb +34 -27
- data/lib/solargraph/pin/namespace.rb +17 -9
- data/lib/solargraph/pin/parameter.rb +17 -5
- 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 +4 -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/rbs_map/conversions.rb +172 -56
- data/lib/solargraph/rbs_map/core_fills.rb +32 -16
- data/lib/solargraph/rbs_map/core_map.rb +3 -2
- data/lib/solargraph/shell.rb +1 -0
- data/lib/solargraph/source/chain/array.rb +11 -7
- 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 +53 -23
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +4 -3
- data/lib/solargraph/source/chain/head.rb +1 -1
- data/lib/solargraph/source/chain/if.rb +1 -1
- data/lib/solargraph/source/chain/link.rb +2 -0
- data/lib/solargraph/source/chain/literal.rb +22 -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 +78 -48
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source_map/clip.rb +3 -1
- data/lib/solargraph/source_map/mapper.rb +9 -5
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +35 -8
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +4 -2
- data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +4 -2
- data/lib/solargraph/yard_map/mapper.rb +4 -3
- data/lib/solargraph/yard_map/to_method.rb +4 -2
- data/lib/solargraph.rb +20 -0
- data/rbs/fills/tuple.rbs +150 -0
- metadata +15 -2
@@ -3,12 +3,20 @@
|
|
3
3
|
module Solargraph
|
4
4
|
class Source
|
5
5
|
class Chain
|
6
|
+
#
|
7
|
+
# Handles both method calls and local variable references by
|
8
|
+
# first looking for a variable with the name 'word', then
|
9
|
+
# proceeding to method signature resolution if not found.
|
10
|
+
#
|
6
11
|
class Call < Chain::Link
|
7
12
|
include Solargraph::Parser::NodeMethods
|
8
13
|
|
9
14
|
# @return [String]
|
10
15
|
attr_reader :word
|
11
16
|
|
17
|
+
# @return [Location]
|
18
|
+
attr_reader :location
|
19
|
+
|
12
20
|
# @return [::Array<Chain>]
|
13
21
|
attr_reader :arguments
|
14
22
|
|
@@ -16,10 +24,12 @@ module Solargraph
|
|
16
24
|
attr_reader :block
|
17
25
|
|
18
26
|
# @param word [String]
|
27
|
+
# @param location [Location, nil]
|
19
28
|
# @param arguments [::Array<Chain>]
|
20
29
|
# @param block [Chain, nil]
|
21
|
-
def initialize word, arguments = [], block = nil
|
30
|
+
def initialize word, location = nil, arguments = [], block = nil
|
22
31
|
@word = word
|
32
|
+
@location = location
|
23
33
|
@arguments = arguments
|
24
34
|
@block = block
|
25
35
|
fix_block_pass
|
@@ -35,20 +45,21 @@ module Solargraph
|
|
35
45
|
end
|
36
46
|
|
37
47
|
# @param api_map [ApiMap]
|
38
|
-
# @param name_pin [Pin::Closure] name_pin.binder should give us the object on which 'word' will be invoked
|
48
|
+
# @param name_pin [Pin::Closure] name_pin.binder should give us the type of the object on which 'word' will be invoked
|
39
49
|
# @param locals [::Array<Pin::LocalVariable>]
|
40
50
|
def resolve api_map, name_pin, locals
|
41
51
|
return super_pins(api_map, name_pin) if word == 'super'
|
42
52
|
return yield_pins(api_map, name_pin) if word == 'yield'
|
43
53
|
found = if head?
|
44
|
-
locals
|
54
|
+
api_map.visible_pins(locals, word, name_pin, location)
|
45
55
|
else
|
46
56
|
[]
|
47
57
|
end
|
48
58
|
return inferred_pins(found, api_map, name_pin, locals) unless found.empty?
|
49
59
|
pins = name_pin.binder.each_unique_type.flat_map do |context|
|
50
|
-
|
51
|
-
api_map.get_method_stack(
|
60
|
+
ns_tag = context.namespace == '' ? '' : context.namespace_type.tag
|
61
|
+
stack = api_map.get_method_stack(ns_tag, word, scope: context.scope)
|
62
|
+
[stack.first].compact
|
52
63
|
end
|
53
64
|
return [] if pins.empty?
|
54
65
|
inferred_pins(pins, api_map, name_pin, locals)
|
@@ -72,7 +83,9 @@ module Solargraph
|
|
72
83
|
# use it. If we didn't pass a block, the logic below will
|
73
84
|
# reject it regardless
|
74
85
|
|
75
|
-
|
86
|
+
with_block, without_block = overloads.partition(&:block?)
|
87
|
+
sorted_overloads = with_block + without_block
|
88
|
+
# @type [Pin::Signature, nil]
|
76
89
|
new_signature_pin = nil
|
77
90
|
sorted_overloads.each do |ol|
|
78
91
|
next unless ol.arity_matches?(arguments, with_block?)
|
@@ -85,13 +98,8 @@ module Solargraph
|
|
85
98
|
match = ol.parameters.any?(&:restarg?)
|
86
99
|
break
|
87
100
|
end
|
88
|
-
atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context), locals)
|
89
|
-
|
90
|
-
# inheritance chain if we don't have them on this pin
|
91
|
-
ptype = param.typify api_map
|
92
|
-
# @todo Weak type comparison
|
93
|
-
# unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
|
94
|
-
unless ptype.undefined? || atype.name == ptype.name || ptype.any? { |current_ptype| api_map.super_and_sub?(current_ptype.name, atype.name) } || ptype.generic? || param.restarg?
|
101
|
+
atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context, source: :chain), locals)
|
102
|
+
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
95
103
|
match = false
|
96
104
|
break
|
97
105
|
end
|
@@ -106,9 +114,30 @@ module Solargraph
|
|
106
114
|
blocktype = block_call_type(api_map, name_pin, locals)
|
107
115
|
end
|
108
116
|
end
|
117
|
+
# @type new_signature_pin [Pin::Signature]
|
109
118
|
new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
|
110
119
|
new_return_type = new_signature_pin.return_type
|
111
|
-
|
120
|
+
if head?
|
121
|
+
# If we're at the head of the chain, we called a
|
122
|
+
# method somewhere that marked itself as returning
|
123
|
+
# self. Given we didn't invoke this on an object,
|
124
|
+
# this must be a method in this same class - so we
|
125
|
+
# use our own self type
|
126
|
+
self_type = name_pin.context
|
127
|
+
else
|
128
|
+
# if we're past the head in the chain, whatever the
|
129
|
+
# type of the lhs side is what 'self' will be in its
|
130
|
+
# declaration - we can't just use the type of the
|
131
|
+
# method pin, as this might be a subclass of the
|
132
|
+
# place where the method is defined
|
133
|
+
self_type = name_pin.binder
|
134
|
+
end
|
135
|
+
# This same logic applies to the YARD work done by
|
136
|
+
# 'with_params()'.
|
137
|
+
#
|
138
|
+
# qualify(), however, happens in the namespace where
|
139
|
+
# the docs were written - from the method pin.
|
140
|
+
type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, p.namespace) if new_return_type.defined?
|
112
141
|
type ||= ComplexType::UNDEFINED
|
113
142
|
end
|
114
143
|
break if type.defined?
|
@@ -124,13 +153,14 @@ module Solargraph
|
|
124
153
|
end
|
125
154
|
p
|
126
155
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
156
|
+
logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" }
|
157
|
+
out = result.map do |pin|
|
158
|
+
if pin.path == 'Class#new' && name_pin.binder.tag != 'Class'
|
159
|
+
reduced_context = name_pin.binder.reduce_class_type
|
130
160
|
pin.proxy(reduced_context)
|
131
161
|
else
|
132
162
|
next pin if pin.return_type.undefined?
|
133
|
-
selfy = pin.return_type.self_to_type(name_pin.
|
163
|
+
selfy = pin.return_type.self_to_type(name_pin.binder)
|
134
164
|
selfy == pin.return_type ? pin : pin.proxy(selfy)
|
135
165
|
end
|
136
166
|
end
|
@@ -152,7 +182,7 @@ module Solargraph
|
|
152
182
|
result = inner_process_macro(pin, macro, api_map, context, locals)
|
153
183
|
return result unless result.return_type.undefined?
|
154
184
|
end
|
155
|
-
Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
|
185
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
156
186
|
end
|
157
187
|
|
158
188
|
# @param pin [Pin::Method]
|
@@ -167,7 +197,7 @@ module Solargraph
|
|
167
197
|
result = inner_process_macro(pin, macro, api_map, context, locals)
|
168
198
|
return result unless result.return_type.undefined?
|
169
199
|
end
|
170
|
-
Pin::ProxyType.anonymous ComplexType::UNDEFINED
|
200
|
+
Pin::ProxyType.anonymous ComplexType::UNDEFINED, source: :chain
|
171
201
|
end
|
172
202
|
|
173
203
|
# @param pin [Pin::Base]
|
@@ -177,7 +207,7 @@ module Solargraph
|
|
177
207
|
# @param locals [::Array<Pin::LocalVariable, Pin::Parameter>]
|
178
208
|
# @return [Pin::ProxyType]
|
179
209
|
def inner_process_macro pin, macro, api_map, context, locals
|
180
|
-
vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
|
210
|
+
vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals), source: :chain) }
|
181
211
|
txt = macro.tag.text.clone
|
182
212
|
if txt.empty? && macro.tag.name
|
183
213
|
named = api_map.named_macro(macro.tag.name)
|
@@ -191,9 +221,9 @@ module Solargraph
|
|
191
221
|
docstring = Solargraph::Source.parse_docstring(txt).to_docstring
|
192
222
|
tag = docstring.tag(:return)
|
193
223
|
unless tag.nil? || tag.types.nil?
|
194
|
-
return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types))
|
224
|
+
return Pin::ProxyType.anonymous(ComplexType.try_parse(*tag.types), source: :chain)
|
195
225
|
end
|
196
|
-
Pin::ProxyType.anonymous(ComplexType::UNDEFINED)
|
226
|
+
Pin::ProxyType.anonymous(ComplexType::UNDEFINED, source: :chain)
|
197
227
|
end
|
198
228
|
|
199
229
|
# @param docstring [YARD::Docstring]
|
@@ -5,9 +5,10 @@ module Solargraph
|
|
5
5
|
class Chain
|
6
6
|
class Hash < Literal
|
7
7
|
# @param type [String]
|
8
|
+
# @param node [Parser::AST::Node]
|
8
9
|
# @param splatted [Boolean]
|
9
|
-
def initialize type, splatted = false
|
10
|
-
super(type)
|
10
|
+
def initialize type, node, splatted = false
|
11
|
+
super(type, node)
|
11
12
|
@splatted = splatted
|
12
13
|
end
|
13
14
|
|
@@ -21,7 +22,7 @@ module Solargraph
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def resolve api_map, name_pin, locals
|
24
|
-
[Pin::ProxyType.anonymous(@complex_type)]
|
25
|
+
[Pin::ProxyType.anonymous(@complex_type, source: :chain)]
|
25
26
|
end
|
26
27
|
|
27
28
|
def splatted?
|
@@ -9,7 +9,7 @@ module Solargraph
|
|
9
9
|
# @note Chain::Head is only intended to handle `self` and `super`.
|
10
10
|
class Head < Link
|
11
11
|
def resolve api_map, name_pin, locals
|
12
|
-
return [Pin::ProxyType.anonymous(name_pin.binder)] if word == 'self'
|
12
|
+
return [Pin::ProxyType.anonymous(name_pin.binder, source: :chain)] if word == 'self'
|
13
13
|
# return super_pins(api_map, name_pin) if word == 'super'
|
14
14
|
[]
|
15
15
|
end
|
@@ -20,7 +20,7 @@ module Solargraph
|
|
20
20
|
|
21
21
|
def resolve api_map, name_pin, locals
|
22
22
|
types = @links.map { |link| link.infer(api_map, name_pin, locals) }
|
23
|
-
[Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')))]
|
23
|
+
[Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.try_parse(types.map(&:tag).uniq.join(', ')), source: :chain)]
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -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,9 +10,22 @@ 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
|
|
@@ -20,7 +35,12 @@ module Solargraph
|
|
20
35
|
end
|
21
36
|
|
22
37
|
def resolve api_map, name_pin, locals
|
23
|
-
|
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
|
24
44
|
end
|
25
45
|
end
|
26
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]
|
@@ -39,7 +39,7 @@ module Solargraph
|
|
39
39
|
@@inference_invalidation_key = nil
|
40
40
|
@@inference_cache = {}
|
41
41
|
|
42
|
-
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
42
|
+
UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
|
43
43
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
44
44
|
|
45
45
|
# @return [::Array<Source::Chain::Link>]
|
@@ -75,16 +75,27 @@ module Solargraph
|
|
75
75
|
|
76
76
|
# Determine potential Pins returned by this chain of words
|
77
77
|
#
|
78
|
-
# @param api_map [ApiMap]
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
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:
|
85
|
+
#
|
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.
|
82
90
|
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
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'.
|
86
97
|
#
|
87
|
-
# @param locals [::
|
98
|
+
# @param locals [::Array<Pin::LocalVariable>] Any local
|
88
99
|
# variables / method parameters etc visible by the statement
|
89
100
|
#
|
90
101
|
# @return [::Array<Pin::Base>] Pins representing possible return
|
@@ -101,16 +112,25 @@ module Solargraph
|
|
101
112
|
working_pin = name_pin
|
102
113
|
links[0..-2].each do |link|
|
103
114
|
pins = link.resolve(api_map, working_pin, locals)
|
104
|
-
type =
|
105
|
-
|
106
|
-
|
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}" }
|
107
127
|
end
|
108
128
|
links.last.last_context = working_pin
|
109
129
|
links.last.resolve(api_map, working_pin, locals)
|
110
130
|
end
|
111
131
|
|
112
132
|
# @param api_map [ApiMap]
|
113
|
-
# @param name_pin [Pin::Base]
|
133
|
+
# @param name_pin [Pin::Base] The pin for the closure in which this code runs
|
114
134
|
# @param locals [::Array<Pin::LocalVariable>]
|
115
135
|
# @return [ComplexType]
|
116
136
|
# @sg-ignore
|
@@ -123,7 +143,7 @@ module Solargraph
|
|
123
143
|
@@inference_invalidation_key = api_map.hash
|
124
144
|
@@inference_cache = {}
|
125
145
|
end
|
126
|
-
out = infer_uncached
|
146
|
+
out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
|
127
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}" }
|
128
148
|
@@inference_cache[cache_key] = out
|
129
149
|
end
|
@@ -134,8 +154,14 @@ module Solargraph
|
|
134
154
|
# @return [ComplexType]
|
135
155
|
def infer_uncached api_map, name_pin, locals
|
136
156
|
pins = define(api_map, name_pin, locals)
|
137
|
-
|
138
|
-
|
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
|
139
165
|
end
|
140
166
|
|
141
167
|
# @return [Boolean]
|
@@ -174,6 +200,8 @@ module Solargraph
|
|
174
200
|
desc
|
175
201
|
end
|
176
202
|
|
203
|
+
include Logging
|
204
|
+
|
177
205
|
private
|
178
206
|
|
179
207
|
# @param pins [::Array<Pin::Base>]
|
@@ -181,8 +209,10 @@ module Solargraph
|
|
181
209
|
# @param api_map [ApiMap]
|
182
210
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
183
211
|
# @return [ComplexType]
|
184
|
-
def
|
185
|
-
|
212
|
+
def infer_from_definitions pins, context, api_map, locals
|
213
|
+
# @type [::Array<ComplexType>]
|
214
|
+
types = []
|
215
|
+
unresolved_pins = []
|
186
216
|
# @todo this param tag shouldn't be needed to probe the type
|
187
217
|
# @todo ...but given it is needed, typecheck should complain that it is needed
|
188
218
|
# @param pin [Pin::Base]
|
@@ -200,41 +230,41 @@ module Solargraph
|
|
200
230
|
# that accepts only [Pin::Namespace] as an argument
|
201
231
|
type = type.resolve_generics(pin.closure, context.binder)
|
202
232
|
end
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
233
|
+
types << type
|
234
|
+
else
|
235
|
+
unresolved_pins << pin
|
207
236
|
end
|
208
237
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
@@inference_depth += 1
|
214
|
-
# @param pin [Pin::Base]
|
215
|
-
pins.each do |pin|
|
216
|
-
# Avoid infinite recursion
|
217
|
-
next if @@inference_stack.include?(pin)
|
218
|
-
|
219
|
-
@@inference_stack.push pin
|
220
|
-
type = pin.probe(api_map)
|
221
|
-
@@inference_stack.pop
|
222
|
-
if type.defined?
|
223
|
-
possibles.push type
|
224
|
-
break if pin.is_a?(Pin::Method)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
@@inference_depth -= 1
|
238
|
+
|
239
|
+
# Limit method inference recursion
|
240
|
+
if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
|
241
|
+
return ComplexType::UNDEFINED
|
228
242
|
end
|
229
|
-
return ComplexType::UNDEFINED if possibles.empty?
|
230
243
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
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
|
237
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
|
238
268
|
return type if context.nil? || context.return_type.undefined?
|
239
269
|
|
240
270
|
type.self_to_type(context.return_type)
|
@@ -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?('..')
|
@@ -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
|
@@ -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
|
@@ -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?
|
@@ -470,12 +470,22 @@ module Solargraph
|
|
470
470
|
# @param pin [Pin::Method]
|
471
471
|
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
472
472
|
def param_hash(pin)
|
473
|
-
tags = pin.docstring.tags(:param)
|
474
|
-
return {} if tags.empty?
|
475
473
|
# @type [Hash{String => Hash{Symbol => String, ComplexType}}]
|
476
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)
|
477
486
|
tags.each do |tag|
|
478
|
-
next if
|
487
|
+
next if result.key? tag.name.to_s
|
488
|
+
next if tag.types.nil?
|
479
489
|
result[tag.name.to_s] = {
|
480
490
|
tagged: tag.types.join(', '),
|
481
491
|
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
@@ -487,11 +497,27 @@ module Solargraph
|
|
487
497
|
# @param pins [Array<Pin::Method>]
|
488
498
|
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
489
499
|
def first_param_hash(pins)
|
490
|
-
pins.
|
491
|
-
|
492
|
-
|
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|
|
506
|
+
# @todo this assignment from parametric use of Hash should not lose its generic
|
507
|
+
# @type [Hash{String => Hash{Symbol => BasicObject}}]
|
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]
|
@@ -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
|