solargraph 0.54.5 → 0.55.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/CHANGELOG.md +13 -0
- data/lib/solargraph/api_map/store.rb +3 -1
- data/lib/solargraph/api_map.rb +18 -8
- data/lib/solargraph/complex_type/unique_type.rb +88 -7
- data/lib/solargraph/complex_type.rb +35 -6
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -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 +42 -18
- data/lib/solargraph/language_server/host/message_worker.rb +10 -7
- data/lib/solargraph/language_server/host.rb +1 -0
- data/lib/solargraph/location.rb +8 -0
- data/lib/solargraph/parser/comment_ripper.rb +11 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
- data/lib/solargraph/parser/node_methods.rb +14 -0
- data/lib/solargraph/parser/node_processor.rb +0 -1
- 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 +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +9 -3
- 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/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +20 -18
- data/lib/solargraph/pin/namespace.rb +10 -7
- data/lib/solargraph/pin/parameter.rb +13 -5
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +3 -0
- data/lib/solargraph/rbs_map/conversions.rb +8 -8
- data/lib/solargraph/rbs_map/core_fills.rb +10 -3
- data/lib/solargraph/source/chain/array.rb +4 -3
- data/lib/solargraph/source/chain/call.rb +46 -17
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +3 -2
- data/lib/solargraph/source/chain/link.rb +2 -0
- data/lib/solargraph/source/chain/literal.rb +22 -2
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +77 -47
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source_map/clip.rb +3 -1
- 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_method.rb +42 -15
- metadata +14 -2
@@ -16,8 +16,6 @@ module Solargraph
|
|
16
16
|
].map { |k| Pin::Keyword.new(k) }
|
17
17
|
|
18
18
|
MISSING = [
|
19
|
-
Solargraph::Pin::Method.new(name: 'tap', scope: :instance,
|
20
|
-
closure: Solargraph::Pin::Namespace.new(name: 'Object')),
|
21
19
|
Solargraph::Pin::Method.new(name: 'class', scope: :instance,
|
22
20
|
closure: Solargraph::Pin::Namespace.new(name: 'Object'), comments: '@return [::Class<self>]')
|
23
21
|
]
|
@@ -35,6 +33,15 @@ module Solargraph
|
|
35
33
|
Override.method_return('Class#allocate', 'self')
|
36
34
|
]
|
37
35
|
|
36
|
+
# @todo I don't see any direct link in RBS to build this from -
|
37
|
+
# presumably RBS is using duck typing to match interfaces
|
38
|
+
# against concrete classes
|
39
|
+
INCLUDES = [
|
40
|
+
Solargraph::Pin::Reference::Include.new(name: '_ToAry',
|
41
|
+
closure: Solargraph::Pin::Namespace.new(name: 'Array'),
|
42
|
+
generic_values: ['generic<Elem>'])
|
43
|
+
]
|
44
|
+
|
38
45
|
# HACK: Add Errno exception classes
|
39
46
|
errno = Solargraph::Pin::Namespace.new(name: 'Errno')
|
40
47
|
errnos = []
|
@@ -44,7 +51,7 @@ module Solargraph
|
|
44
51
|
end
|
45
52
|
ERRNOS = errnos
|
46
53
|
|
47
|
-
ALL = KEYWORDS + MISSING + OVERRIDES + ERRNOS
|
54
|
+
ALL = KEYWORDS + MISSING + OVERRIDES + ERRNOS + INCLUDES
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
@@ -3,8 +3,9 @@ module Solargraph
|
|
3
3
|
class Chain
|
4
4
|
class Array < Literal
|
5
5
|
# @param children [::Array<Chain>]
|
6
|
-
|
7
|
-
|
6
|
+
# @param node [Parser::AST::Node]
|
7
|
+
def initialize children, node
|
8
|
+
super('::Array', node)
|
8
9
|
@children = children
|
9
10
|
end
|
10
11
|
|
@@ -17,7 +18,7 @@ module Solargraph
|
|
17
18
|
# @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
|
18
19
|
def resolve api_map, name_pin, locals
|
19
20
|
child_types = @children.map do |child|
|
20
|
-
child.infer(api_map, name_pin, locals)
|
21
|
+
child.infer(api_map, name_pin, locals).simplify_literals
|
21
22
|
end
|
22
23
|
|
23
24
|
type = if child_types.uniq.length == 1 && child_types.first.defined?
|
@@ -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,8 @@ 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
|
76
88
|
new_signature_pin = nil
|
77
89
|
sorted_overloads.each do |ol|
|
78
90
|
next unless ol.arity_matches?(arguments, with_block?)
|
@@ -85,13 +97,9 @@ module Solargraph
|
|
85
97
|
match = ol.parameters.any?(&:restarg?)
|
86
98
|
break
|
87
99
|
end
|
100
|
+
|
88
101
|
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?
|
102
|
+
unless param.compatible_arg?(atype, api_map) || param.restarg?
|
95
103
|
match = false
|
96
104
|
break
|
97
105
|
end
|
@@ -108,7 +116,27 @@ module Solargraph
|
|
108
116
|
end
|
109
117
|
new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
|
110
118
|
new_return_type = new_signature_pin.return_type
|
111
|
-
|
119
|
+
if head?
|
120
|
+
# If we're at the head of the chain, we called a
|
121
|
+
# method somewhere that marked itself as returning
|
122
|
+
# self. Given we didn't invoke this on an object,
|
123
|
+
# this must be a method in this same class - so we
|
124
|
+
# use our own self type
|
125
|
+
self_type = name_pin.context
|
126
|
+
else
|
127
|
+
# if we're past the head in the chain, whatever the
|
128
|
+
# type of the lhs side is what 'self' will be in its
|
129
|
+
# declaration - we can't just use the type of the
|
130
|
+
# method pin, as this might be a subclass of the
|
131
|
+
# place where the method is defined
|
132
|
+
self_type = name_pin.binder
|
133
|
+
end
|
134
|
+
# This same logic applies to the YARD work done by
|
135
|
+
# 'with_params()'.
|
136
|
+
#
|
137
|
+
# qualify(), however, happens in the namespace where
|
138
|
+
# the docs were written - from the method pin.
|
139
|
+
type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, p.namespace) if new_return_type.defined?
|
112
140
|
type ||= ComplexType::UNDEFINED
|
113
141
|
end
|
114
142
|
break if type.defined?
|
@@ -124,13 +152,14 @@ module Solargraph
|
|
124
152
|
end
|
125
153
|
p
|
126
154
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
155
|
+
logger.debug { "Call#inferred_pins(name_pin.binder=#{name_pin.binder}, word=#{word}, pins=#{pins.map(&:desc)}, name_pin=#{name_pin}) - result=#{result}" }
|
156
|
+
out = result.map do |pin|
|
157
|
+
if pin.path == 'Class#new' && name_pin.binder.tag != 'Class'
|
158
|
+
reduced_context = name_pin.binder.reduce_class_type
|
130
159
|
pin.proxy(reduced_context)
|
131
160
|
else
|
132
161
|
next pin if pin.return_type.undefined?
|
133
|
-
selfy = pin.return_type.self_to_type(name_pin.
|
162
|
+
selfy = pin.return_type.self_to_type(name_pin.binder)
|
134
163
|
selfy == pin.return_type ? pin : pin.proxy(selfy)
|
135
164
|
end
|
136
165
|
end
|
@@ -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
|
|
@@ -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)]
|
40
|
+
else
|
41
|
+
# we don't support this value as a literal type
|
42
|
+
[Pin::ProxyType.anonymous(@complex_type)]
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
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,9 +112,18 @@ 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)
|
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)
|
@@ -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
|
@@ -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
|
data/lib/solargraph/version.rb
CHANGED
@@ -16,29 +16,55 @@ module Solargraph
|
|
16
16
|
def self.make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil
|
17
17
|
closure ||= Solargraph::Pin::Namespace.new(
|
18
18
|
name: code_object.namespace.to_s,
|
19
|
-
gates: [code_object.namespace.to_s]
|
19
|
+
gates: [code_object.namespace.to_s],
|
20
|
+
type: code_object.namespace.is_a?(YARD::CodeObjects::ClassObject) ? :class : :module,
|
21
|
+
source: :yardoc,
|
20
22
|
)
|
21
23
|
location = object_location(code_object, spec)
|
22
24
|
name ||= code_object.name.to_s
|
23
25
|
return_type = ComplexType::SELF if name == 'new'
|
24
26
|
comments = code_object.docstring ? code_object.docstring.all.to_s : ''
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
final_scope = scope || code_object.scope
|
28
|
+
final_visibility = visibility || code_object.visibility
|
29
|
+
if code_object.is_alias?
|
30
|
+
origin_code_object = code_object.namespace.aliases[code_object]
|
31
|
+
pin = Pin::MethodAlias.new(
|
32
|
+
name: name,
|
33
|
+
location: location,
|
34
|
+
original: origin_code_object.name.to_s,
|
35
|
+
closure: closure,
|
36
|
+
comments: comments,
|
37
|
+
scope: final_scope,
|
38
|
+
visibility: final_visibility,
|
39
|
+
explicit: code_object.is_explicit?,
|
40
|
+
return_type: return_type,
|
41
|
+
parameters: [],
|
42
|
+
source: :yardoc,
|
43
|
+
)
|
44
|
+
else
|
45
|
+
pin = Pin::Method.new(
|
46
|
+
location: location,
|
47
|
+
closure: closure,
|
48
|
+
name: name,
|
49
|
+
comments: comments,
|
50
|
+
scope: final_scope,
|
51
|
+
visibility: final_visibility,
|
52
|
+
# @todo Might need to convert overloads to signatures
|
53
|
+
explicit: code_object.is_explicit?,
|
54
|
+
return_type: return_type,
|
55
|
+
attribute: code_object.is_attribute?,
|
56
|
+
parameters: [],
|
57
|
+
source: :yardoc,
|
58
|
+
)
|
59
|
+
pin.parameters.concat get_parameters(code_object, location, comments, pin)
|
60
|
+
end
|
61
|
+
logger.debug { "ToMethod.make: Just created method pin: #{pin.inspect}" }
|
38
62
|
pin
|
39
63
|
end
|
40
64
|
|
41
65
|
class << self
|
66
|
+
include Logging
|
67
|
+
|
42
68
|
private
|
43
69
|
|
44
70
|
# @param code_object [YARD::CodeObjects::Base]
|
@@ -59,7 +85,8 @@ module Solargraph
|
|
59
85
|
name: arg_name(a),
|
60
86
|
presence: nil,
|
61
87
|
decl: arg_type(a),
|
62
|
-
asgn_code: a[1]
|
88
|
+
asgn_code: a[1],
|
89
|
+
source: :yardoc,
|
63
90
|
)
|
64
91
|
end
|
65
92
|
end
|