solargraph 0.54.0 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/lib/solargraph/api_map/store.rb +6 -2
  4. data/lib/solargraph/api_map.rb +15 -8
  5. data/lib/solargraph/complex_type/type_methods.rb +10 -11
  6. data/lib/solargraph/complex_type/unique_type.rb +72 -9
  7. data/lib/solargraph/complex_type.rb +66 -17
  8. data/lib/solargraph/language_server/host/message_worker.rb +29 -3
  9. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  10. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  11. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  12. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  13. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  14. data/lib/solargraph/language_server/progress.rb +19 -2
  15. data/lib/solargraph/library.rb +30 -39
  16. data/lib/solargraph/location.rb +14 -1
  17. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -7
  18. data/lib/solargraph/parser/parser_gem/node_methods.rb +1 -1
  19. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  20. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  21. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  22. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  23. data/lib/solargraph/parser.rb +2 -5
  24. data/lib/solargraph/pin/base.rb +15 -1
  25. data/lib/solargraph/pin/base_variable.rb +1 -1
  26. data/lib/solargraph/pin/block.rb +5 -23
  27. data/lib/solargraph/pin/callable.rb +147 -0
  28. data/lib/solargraph/pin/closure.rb +8 -3
  29. data/lib/solargraph/pin/common.rb +2 -6
  30. data/lib/solargraph/pin/conversions.rb +3 -2
  31. data/lib/solargraph/pin/instance_variable.rb +2 -2
  32. data/lib/solargraph/pin/method.rb +51 -31
  33. data/lib/solargraph/pin/namespace.rb +4 -4
  34. data/lib/solargraph/pin/parameter.rb +9 -11
  35. data/lib/solargraph/pin/proxy_type.rb +1 -1
  36. data/lib/solargraph/pin/signature.rb +3 -129
  37. data/lib/solargraph/pin.rb +4 -1
  38. data/lib/solargraph/range.rb +2 -4
  39. data/lib/solargraph/rbs_map/conversions.rb +70 -37
  40. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  41. data/lib/solargraph/shell.rb +17 -2
  42. data/lib/solargraph/source/chain/array.rb +6 -5
  43. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  44. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  45. data/lib/solargraph/source/chain/call.rb +78 -50
  46. data/lib/solargraph/source/chain/link.rb +9 -0
  47. data/lib/solargraph/source/chain/or.rb +1 -1
  48. data/lib/solargraph/source/chain.rb +41 -17
  49. data/lib/solargraph/source/cursor.rb +13 -2
  50. data/lib/solargraph/source.rb +102 -85
  51. data/lib/solargraph/source_map/clip.rb +4 -4
  52. data/lib/solargraph/source_map/data.rb +30 -0
  53. data/lib/solargraph/source_map.rb +28 -16
  54. data/lib/solargraph/type_checker/rules.rb +6 -1
  55. data/lib/solargraph/type_checker.rb +7 -7
  56. data/lib/solargraph/version.rb +1 -1
  57. data/lib/solargraph/views/environment.erb +3 -5
  58. metadata +4 -2
@@ -3,7 +3,9 @@
3
3
  module Solargraph
4
4
  class Source
5
5
  class Chain
6
- class Call < Link
6
+ class Call < Chain::Link
7
+ include Solargraph::Parser::NodeMethods
8
+
7
9
  # @return [String]
8
10
  attr_reader :word
9
11
 
@@ -28,7 +30,7 @@ module Solargraph
28
30
  end
29
31
 
30
32
  # @param api_map [ApiMap]
31
- # @param name_pin [Pin::Base]
33
+ # @param name_pin [Pin::Closure] name_pin.binder should give us the object on which 'word' will be invoked
32
34
  # @param locals [::Array<Pin::LocalVariable>]
33
35
  def resolve api_map, name_pin, locals
34
36
  return super_pins(api_map, name_pin) if word == 'super'
@@ -38,23 +40,23 @@ module Solargraph
38
40
  else
39
41
  []
40
42
  end
41
- return inferred_pins(found, api_map, name_pin.context, locals) unless found.empty?
42
- # @param [ComplexType::UniqueType]
43
+ return inferred_pins(found, api_map, name_pin, locals) unless found.empty?
43
44
  pins = name_pin.binder.each_unique_type.flat_map do |context|
44
- api_map.get_method_stack(context.namespace == '' ? '' : context.tag, word, scope: context.scope)
45
+ method_context = context.namespace == '' ? '' : context.tag
46
+ api_map.get_method_stack(method_context, word, scope: context.scope)
45
47
  end
46
48
  return [] if pins.empty?
47
- inferred_pins(pins, api_map, name_pin.context, locals)
49
+ inferred_pins(pins, api_map, name_pin, locals)
48
50
  end
49
51
 
50
52
  private
51
53
 
52
54
  # @param pins [::Enumerable<Pin::Method>]
53
55
  # @param api_map [ApiMap]
54
- # @param context [ComplexType]
56
+ # @param name_pin [Pin::Base]
55
57
  # @param locals [::Array<Pin::LocalVariable>]
56
58
  # @return [::Array<Pin::Base>]
57
- def inferred_pins pins, api_map, context, locals
59
+ def inferred_pins pins, api_map, name_pin, locals
58
60
  result = pins.map do |p|
59
61
  next p unless p.is_a?(Pin::Method)
60
62
  overloads = p.signatures
@@ -68,7 +70,7 @@ module Solargraph
68
70
  sorted_overloads = overloads.sort { |ol| ol.block? ? -1 : 1 }
69
71
  new_signature_pin = nil
70
72
  sorted_overloads.each do |ol|
71
- next unless arity_matches?(arguments, ol)
73
+ next unless ol.arity_matches?(arguments, with_block?)
72
74
  match = true
73
75
 
74
76
  atypes = []
@@ -78,7 +80,8 @@ module Solargraph
78
80
  match = ol.parameters.any?(&:restarg?)
79
81
  break
80
82
  end
81
- atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(context), locals)
83
+ atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(name_pin.context), locals)
84
+ ptype = param.return_type
82
85
  # @todo Weak type comparison
83
86
  # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
84
87
  unless param.return_type.undefined? || atype.name == param.return_type.name || api_map.super_and_sub?(param.return_type.name, atype.name) || param.return_type.generic?
@@ -89,11 +92,16 @@ module Solargraph
89
92
  if match
90
93
  if ol.block && with_block?
91
94
  block_atypes = ol.block.parameters.map(&:return_type)
92
- blocktype = block_call_type(api_map, context, block_atypes, locals)
95
+ if block.links.map(&:class) == [BlockSymbol]
96
+ # like the bar in foo(&:bar)
97
+ blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
98
+ else
99
+ blocktype = block_call_type(api_map, name_pin, locals)
100
+ end
93
101
  end
94
102
  new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
95
103
  new_return_type = new_signature_pin.return_type
96
- type = with_params(new_return_type.self_to(context.to_s), context).qualify(api_map, context.namespace) if new_return_type.defined?
104
+ type = with_params(new_return_type.self_to_type(name_pin.context), name_pin.context).qualify(api_map, name_pin.context.namespace) if new_return_type.defined?
97
105
  type ||= ComplexType::UNDEFINED
98
106
  end
99
107
  break if type.defined?
@@ -101,20 +109,21 @@ module Solargraph
101
109
  p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
102
110
  next p.proxy(type) if type.defined?
103
111
  if !p.macros.empty?
104
- result = process_macro(p, api_map, context, locals)
112
+ result = process_macro(p, api_map, name_pin.context, locals)
105
113
  next result unless result.return_type.undefined?
106
114
  elsif !p.directives.empty?
107
- result = process_directive(p, api_map, context, locals)
115
+ result = process_directive(p, api_map, name_pin.context, locals)
108
116
  next result unless result.return_type.undefined?
109
117
  end
110
118
  p
111
119
  end
112
120
  result.map do |pin|
113
- if pin.path == 'Class#new' && context.tag != 'Class'
114
- pin.proxy(ComplexType.try_parse(context.namespace))
121
+ if pin.path == 'Class#new' && name_pin.context.tag != 'Class'
122
+ reduced_context = name_pin.context.reduce_class_type
123
+ pin.proxy(reduced_context)
115
124
  else
116
125
  next pin if pin.return_type.undefined?
117
- selfy = pin.return_type.self_to(context.tag)
126
+ selfy = pin.return_type.self_to_type(name_pin.context)
118
127
  selfy == pin.return_type ? pin : pin.proxy(selfy)
119
128
  end
120
129
  end
@@ -192,24 +201,24 @@ module Solargraph
192
201
  nil
193
202
  end
194
203
 
195
- # @param arguments [::Array<Chain>]
196
- # @param signature [Pin::Signature]
197
- # @return [Boolean]
198
- def arity_matches? arguments, signature
199
- parameters = signature.parameters
200
- argcount = arguments.length
201
- parcount = parameters.length
202
- parcount -= 1 if !parameters.empty? && parameters.last.block?
203
- return false if signature.block? && !with_block?
204
- return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.restarg?)
205
- true
204
+ # @param name_pin [Pin::Base]
205
+ # @return [Pin::Method, nil]
206
+ def find_method_pin(name_pin)
207
+ method_pin = name_pin
208
+ until method_pin.is_a?(Pin::Method)
209
+ method_pin = method_pin.closure
210
+ return if method_pin.nil?
211
+ end
212
+ method_pin
206
213
  end
207
214
 
208
215
  # @param api_map [ApiMap]
209
216
  # @param name_pin [Pin::Base]
210
217
  # @return [::Array<Pin::Base>]
211
218
  def super_pins api_map, name_pin
212
- pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.context.scope)
219
+ method_pin = find_method_pin(name_pin)
220
+ return [] if method_pin.nil?
221
+ pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope)
213
222
  pins.reject{|p| p.path == name_pin.path}
214
223
  end
215
224
 
@@ -217,10 +226,13 @@ module Solargraph
217
226
  # @param name_pin [Pin::Base]
218
227
  # @return [::Array<Pin::Base>]
219
228
  def yield_pins api_map, name_pin
220
- method_pin = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.context.scope).first
221
- return [] if method_pin.nil?
229
+ method_pin = find_method_pin(name_pin)
230
+ return [] unless method_pin
222
231
 
223
- method_pin.signatures.map(&:block).compact
232
+ method_pin.signatures.map(&:block).compact.map do |signature_pin|
233
+ return_type = signature_pin.return_type.qualify(api_map, name_pin.namespace)
234
+ signature_pin.proxy(return_type)
235
+ end
224
236
  end
225
237
 
226
238
  # @param type [ComplexType]
@@ -228,7 +240,7 @@ module Solargraph
228
240
  # @return [ComplexType]
229
241
  def with_params type, context
230
242
  return type unless type.to_s.include?('$')
231
- ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:tag).join(', ')).gsub('<>', ''))
243
+ ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:rooted_tag).join(', ')).gsub('<>', ''))
232
244
  end
233
245
 
234
246
  # @return [void]
@@ -242,25 +254,41 @@ module Solargraph
242
254
  # @param block_parameter_types [::Array<ComplexType>]
243
255
  # @param locals [::Array<Pin::LocalVariable>]
244
256
  # @return [ComplexType, nil]
245
- def block_call_type(api_map, context, block_parameter_types, locals)
257
+ def block_symbol_call_type(api_map, context, block_parameter_types, locals)
258
+ # Ruby's shorthand for sending the passed in method name
259
+ # to the first yield parameter with no arguments
260
+ block_symbol_name = block.links.first.word
261
+ block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
262
+ callee = api_map.get_path_pins(block_symbol_call_path).first
263
+ return_type = callee&.return_type
264
+ # @todo: Figure out why we get unresolved generics at
265
+ # this point and need to assume method return types
266
+ # based on the generic type
267
+ return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
268
+ return_type || ComplexType::UNDEFINED
269
+ end
270
+
271
+ # @param api_map [ApiMap]
272
+ # @return [Pin::Block, nil]
273
+ def find_block_pin(api_map)
274
+ node_location = Solargraph::Location.from_node(block.node)
275
+ return if node_location.nil?
276
+ block_pins = api_map.get_block_pins
277
+ block_pins.find { |pin| pin.location.contain?(node_location) }
278
+ end
279
+
280
+ # @param api_map [ApiMap]
281
+ # @param name_pin [Pin::Base]
282
+ # @param block_parameter_types [::Array<ComplexType>]
283
+ # @param locals [::Array<Pin::LocalVariable>]
284
+ # @return [ComplexType, nil]
285
+ def block_call_type(api_map, name_pin, locals)
246
286
  return nil unless with_block?
247
287
 
248
- # @todo Handle BlockVariable
249
- if block.links.map(&:class) == [BlockSymbol]
250
- # Ruby's shorthand for sending the passed in method name
251
- # to the first yield parameter with no arguments
252
- block_symbol_name = block.links.first.word
253
- block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
254
- callee = api_map.get_path_pins(block_symbol_call_path).first
255
- return_type = callee&.return_type
256
- # @todo: Figure out why we get unresolved generics at
257
- # this point and need to assume method return types
258
- # based on the generic type
259
- return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
260
- return_type || ComplexType::UNDEFINED
261
- else
262
- block.infer(api_map, Pin::ProxyType.anonymous(context), locals)
263
- end
288
+ block_context_pin = name_pin
289
+ block_pin = find_block_pin(api_map)
290
+ block_context_pin = block_pin.closure if block_pin
291
+ block.infer(api_map, block_context_pin, locals)
264
292
  end
265
293
  end
266
294
  end
@@ -61,6 +61,15 @@ module Solargraph
61
61
  false
62
62
  end
63
63
 
64
+ # debugging description of contents; not for machine use
65
+ def desc
66
+ word
67
+ end
68
+
69
+ def inspect
70
+ "#<#{self.class} - `#{self.desc}`>"
71
+ end
72
+
64
73
  protected
65
74
 
66
75
  # Mark whether this link is the head of a chain
@@ -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.try_parse(types.map(&:tag).uniq.join(', ')))]
18
+ [Solargraph::Pin::ProxyType.anonymous(Solargraph::ComplexType.new(types.uniq))]
19
19
  end
20
20
  end
21
21
  end
@@ -5,10 +5,19 @@ require 'solargraph/source/chain/link'
5
5
 
6
6
  module Solargraph
7
7
  class Source
8
- # A chain of constants, variables, and method calls for inferring types of
9
- # values.
8
+ #
9
+ # Represents an expression as a single call chain at the parse
10
+ # tree level, made up of constants, variables, and method calls,
11
+ # each represented as a Link object.
12
+ #
13
+ # Computes Pins and/or ComplexTypes representing the interpreted
14
+ # expression.
10
15
  #
11
16
  class Chain
17
+ #
18
+ # A chain of constants, variables, and method calls for inferring types of
19
+ # values.
20
+ #
12
21
  autoload :Link, 'solargraph/source/chain/link'
13
22
  autoload :Call, 'solargraph/source/chain/call'
14
23
  autoload :QCall, 'solargraph/source/chain/q_call'
@@ -61,13 +70,31 @@ module Solargraph
61
70
  @base ||= Chain.new(links[0..-2])
62
71
  end
63
72
 
73
+ # Determine potential Pins returned by this chain of words
74
+ #
64
75
  # @param api_map [ApiMap]
65
- # @param name_pin [Pin::Base]
66
- # @param locals [::Enumerable<Pin::LocalVariable>]
76
+ # @param name_pin [Pin::Closure] the surrounding closure pin for
77
+ # the statement represented by this chain for type resolution
78
+ # and method pin lookup.
79
+ #
80
+ # For method calls (Chain::Call objects) as the first element
81
+ # in the chain, 'name_pin.binder' should return the
82
+ # ComplexType representing the LHS / "self type" of the call.
83
+ #
84
+ # @param locals [::Enumerable<Pin::LocalVariable>] Any local
85
+ # variables / method parameters etc visible by the statement
67
86
  #
68
- # @return [::Array<Pin::Base>]
87
+ # @return [::Array<Pin::Base>] Pins representing possible return
88
+ # types of this method.
69
89
  def define api_map, name_pin, locals
70
90
  return [] if undefined?
91
+
92
+ # working_pin is the surrounding closure pin for the link
93
+ # being processed, whose #binder method will provide the LHS /
94
+ # 'self type' of the next link (same as the #return_type method
95
+ # --the type of the result so far).
96
+ #
97
+ # @todo ProxyType uses 'type' for the binder, but '
71
98
  working_pin = name_pin
72
99
  links[0..-2].each do |link|
73
100
  pins = link.resolve(api_map, working_pin, locals)
@@ -75,12 +102,12 @@ module Solargraph
75
102
  return [] if type.undefined?
76
103
  working_pin = Pin::ProxyType.anonymous(type)
77
104
  end
78
- links.last.last_context = name_pin
105
+ links.last.last_context = working_pin
79
106
  links.last.resolve(api_map, working_pin, locals)
80
107
  end
81
108
 
82
109
  # @param api_map [ApiMap]
83
- # @param name_pin [Pin::Base]
110
+ # @param name_pin [Pin::Base] The pin for the closure in which this code runs
84
111
  # @param locals [::Enumerable<Pin::LocalVariable>]
85
112
  # @return [ComplexType]
86
113
  # @sg-ignore
@@ -102,10 +129,6 @@ module Solargraph
102
129
  # @param locals [::Enumerable<Pin::LocalVariable>]
103
130
  # @return [ComplexType]
104
131
  def infer_uncached api_map, name_pin, locals
105
- from_here = base.infer(api_map, name_pin, locals) unless links.length == 1
106
- if from_here
107
- name_pin = name_pin.proxy(from_here)
108
- end
109
132
  pins = define(api_map, name_pin, locals)
110
133
  type = infer_first_defined(pins, links.last.last_context, api_map, locals)
111
134
  maybe_nil(type)
@@ -161,7 +184,7 @@ module Solargraph
161
184
  # @todo even at strong, no typechecking complaint
162
185
  # happens when a [Pin::Base,nil] is passed into a method
163
186
  # that accepts only [Pin::Namespace] as an argument
164
- type = type.resolve_generics(pin.closure, context.return_type)
187
+ type = type.resolve_generics(pin.closure, context.binder)
165
188
  end
166
189
  if type.defined?
167
190
  possibles.push type
@@ -192,14 +215,15 @@ module Solargraph
192
215
  return ComplexType::UNDEFINED if possibles.empty?
193
216
 
194
217
  type = if possibles.length > 1
195
- sorted = possibles.map { |t| t.rooted? ? "::#{t}" : t.to_s }.sort { |a, _| a == 'nil' ? 1 : 0 }
196
- ComplexType.parse(*sorted)
218
+ # Move nil to the end by convention
219
+ sorted = possibles.sort { |a, _| a.tag == 'nil' ? 1 : 0 }
220
+ ComplexType.new(sorted.uniq)
197
221
  else
198
- ComplexType.parse(possibles.map(&:to_s).join(', '))
222
+ ComplexType.new(possibles)
199
223
  end
200
224
  return type if context.nil? || context.return_type.undefined?
201
225
 
202
- type.self_to(context.return_type.tag)
226
+ type.self_to_type(context.return_type)
203
227
  end
204
228
 
205
229
  # @param type [ComplexType]
@@ -207,7 +231,7 @@ module Solargraph
207
231
  def maybe_nil type
208
232
  return type if type.undefined? || type.void? || type.nullable?
209
233
  return type unless nullable?
210
- ComplexType.try_parse("#{type}, nil")
234
+ ComplexType.new(type.items + [ComplexType::NIL])
211
235
  end
212
236
  end
213
237
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Solargraph
4
4
  class Source
5
- # Information about a position in a source, including the word located
6
- # there.
5
+ # Information about a single Position in a Source, including the
6
+ # word located there.
7
7
  #
8
8
  class Cursor
9
9
  # @return [Position]
@@ -103,6 +103,17 @@ module Solargraph
103
103
  @string ||= source.string_at?(position)
104
104
  end
105
105
 
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
+
106
117
  # Get a cursor pointing to the method that receives the current statement
107
118
  # as an argument.
108
119
  #