solargraph 0.54.0 → 0.54.2

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/solargraph/api_map/store.rb +8 -4
  4. data/lib/solargraph/api_map.rb +74 -23
  5. data/lib/solargraph/complex_type/type_methods.rb +17 -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 +45 -5
  9. data/lib/solargraph/language_server/host.rb +10 -10
  10. data/lib/solargraph/language_server/message/base.rb +18 -11
  11. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  12. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  13. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  14. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  15. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  16. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  17. data/lib/solargraph/language_server/progress.rb +19 -2
  18. data/lib/solargraph/library.rb +29 -39
  19. data/lib/solargraph/location.rb +14 -1
  20. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -7
  21. data/lib/solargraph/parser/parser_gem/node_methods.rb +1 -1
  22. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  23. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  24. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  25. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  26. data/lib/solargraph/parser.rb +2 -5
  27. data/lib/solargraph/pin/base.rb +16 -3
  28. data/lib/solargraph/pin/base_variable.rb +1 -1
  29. data/lib/solargraph/pin/block.rb +6 -26
  30. data/lib/solargraph/pin/callable.rb +147 -0
  31. data/lib/solargraph/pin/closure.rb +8 -3
  32. data/lib/solargraph/pin/common.rb +2 -6
  33. data/lib/solargraph/pin/conversions.rb +3 -2
  34. data/lib/solargraph/pin/instance_variable.rb +2 -2
  35. data/lib/solargraph/pin/method.rb +51 -31
  36. data/lib/solargraph/pin/namespace.rb +4 -4
  37. data/lib/solargraph/pin/parameter.rb +9 -11
  38. data/lib/solargraph/pin/proxy_type.rb +1 -1
  39. data/lib/solargraph/pin/signature.rb +3 -129
  40. data/lib/solargraph/pin.rb +4 -1
  41. data/lib/solargraph/range.rb +2 -4
  42. data/lib/solargraph/rbs_map/conversions.rb +76 -37
  43. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  44. data/lib/solargraph/shell.rb +17 -2
  45. data/lib/solargraph/source/chain/array.rb +6 -5
  46. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  47. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  48. data/lib/solargraph/source/chain/call.rb +81 -51
  49. data/lib/solargraph/source/chain/link.rb +9 -0
  50. data/lib/solargraph/source/chain/or.rb +1 -1
  51. data/lib/solargraph/source/chain.rb +41 -17
  52. data/lib/solargraph/source/cursor.rb +14 -2
  53. data/lib/solargraph/source.rb +102 -85
  54. data/lib/solargraph/source_map/clip.rb +4 -4
  55. data/lib/solargraph/source_map/data.rb +30 -0
  56. data/lib/solargraph/source_map.rb +28 -16
  57. data/lib/solargraph/type_checker/rules.rb +6 -1
  58. data/lib/solargraph/type_checker.rb +7 -7
  59. data/lib/solargraph/version.rb +1 -1
  60. data/lib/solargraph/views/environment.erb +3 -5
  61. data/solargraph.gemspec +4 -4
  62. metadata +20 -24
@@ -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
+ ns = context.namespace == '' ? '' : context.namespace_type.tag
46
+ api_map.get_method_stack(ns, 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,10 +80,13 @@ 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
+ # make sure we get types from up the method
85
+ # inheritance chain if we don't have them on this pin
86
+ ptype = param.typify api_map
82
87
  # @todo Weak type comparison
83
88
  # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
84
- 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
+ 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?
85
90
  match = false
86
91
  break
87
92
  end
@@ -89,11 +94,16 @@ module Solargraph
89
94
  if match
90
95
  if ol.block && with_block?
91
96
  block_atypes = ol.block.parameters.map(&:return_type)
92
- blocktype = block_call_type(api_map, context, block_atypes, locals)
97
+ if block.links.map(&:class) == [BlockSymbol]
98
+ # like the bar in foo(&:bar)
99
+ blocktype = block_symbol_call_type(api_map, name_pin.context, block_atypes, locals)
100
+ else
101
+ blocktype = block_call_type(api_map, name_pin, locals)
102
+ end
93
103
  end
94
104
  new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
95
105
  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?
106
+ 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
107
  type ||= ComplexType::UNDEFINED
98
108
  end
99
109
  break if type.defined?
@@ -101,20 +111,21 @@ module Solargraph
101
111
  p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
102
112
  next p.proxy(type) if type.defined?
103
113
  if !p.macros.empty?
104
- result = process_macro(p, api_map, context, locals)
114
+ result = process_macro(p, api_map, name_pin.context, locals)
105
115
  next result unless result.return_type.undefined?
106
116
  elsif !p.directives.empty?
107
- result = process_directive(p, api_map, context, locals)
117
+ result = process_directive(p, api_map, name_pin.context, locals)
108
118
  next result unless result.return_type.undefined?
109
119
  end
110
120
  p
111
121
  end
112
122
  result.map do |pin|
113
- if pin.path == 'Class#new' && context.tag != 'Class'
114
- pin.proxy(ComplexType.try_parse(context.namespace))
123
+ if pin.path == 'Class#new' && name_pin.context.tag != 'Class'
124
+ reduced_context = name_pin.context.reduce_class_type
125
+ pin.proxy(reduced_context)
115
126
  else
116
127
  next pin if pin.return_type.undefined?
117
- selfy = pin.return_type.self_to(context.tag)
128
+ selfy = pin.return_type.self_to_type(name_pin.context)
118
129
  selfy == pin.return_type ? pin : pin.proxy(selfy)
119
130
  end
120
131
  end
@@ -192,24 +203,24 @@ module Solargraph
192
203
  nil
193
204
  end
194
205
 
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
206
+ # @param name_pin [Pin::Base]
207
+ # @return [Pin::Method, nil]
208
+ def find_method_pin(name_pin)
209
+ method_pin = name_pin
210
+ until method_pin.is_a?(Pin::Method)
211
+ method_pin = method_pin.closure
212
+ return if method_pin.nil?
213
+ end
214
+ method_pin
206
215
  end
207
216
 
208
217
  # @param api_map [ApiMap]
209
218
  # @param name_pin [Pin::Base]
210
219
  # @return [::Array<Pin::Base>]
211
220
  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)
221
+ method_pin = find_method_pin(name_pin)
222
+ return [] if method_pin.nil?
223
+ pins = api_map.get_method_stack(method_pin.namespace, method_pin.name, scope: method_pin.context.scope)
213
224
  pins.reject{|p| p.path == name_pin.path}
214
225
  end
215
226
 
@@ -217,10 +228,13 @@ module Solargraph
217
228
  # @param name_pin [Pin::Base]
218
229
  # @return [::Array<Pin::Base>]
219
230
  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?
231
+ method_pin = find_method_pin(name_pin)
232
+ return [] unless method_pin
222
233
 
223
- method_pin.signatures.map(&:block).compact
234
+ method_pin.signatures.map(&:block).compact.map do |signature_pin|
235
+ return_type = signature_pin.return_type.qualify(api_map, name_pin.namespace)
236
+ signature_pin.proxy(return_type)
237
+ end
224
238
  end
225
239
 
226
240
  # @param type [ComplexType]
@@ -228,7 +242,7 @@ module Solargraph
228
242
  # @return [ComplexType]
229
243
  def with_params type, context
230
244
  return type unless type.to_s.include?('$')
231
- ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:tag).join(', ')).gsub('<>', ''))
245
+ ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:rooted_tag).join(', ')).gsub('<>', ''))
232
246
  end
233
247
 
234
248
  # @return [void]
@@ -242,25 +256,41 @@ module Solargraph
242
256
  # @param block_parameter_types [::Array<ComplexType>]
243
257
  # @param locals [::Array<Pin::LocalVariable>]
244
258
  # @return [ComplexType, nil]
245
- def block_call_type(api_map, context, block_parameter_types, locals)
259
+ def block_symbol_call_type(api_map, context, block_parameter_types, locals)
260
+ # Ruby's shorthand for sending the passed in method name
261
+ # to the first yield parameter with no arguments
262
+ block_symbol_name = block.links.first.word
263
+ block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
264
+ callee = api_map.get_path_pins(block_symbol_call_path).first
265
+ return_type = callee&.return_type
266
+ # @todo: Figure out why we get unresolved generics at
267
+ # this point and need to assume method return types
268
+ # based on the generic type
269
+ return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
270
+ return_type || ComplexType::UNDEFINED
271
+ end
272
+
273
+ # @param api_map [ApiMap]
274
+ # @return [Pin::Block, nil]
275
+ def find_block_pin(api_map)
276
+ node_location = Solargraph::Location.from_node(block.node)
277
+ return if node_location.nil?
278
+ block_pins = api_map.get_block_pins
279
+ block_pins.find { |pin| pin.location.contain?(node_location) }
280
+ end
281
+
282
+ # @param api_map [ApiMap]
283
+ # @param name_pin [Pin::Base]
284
+ # @param block_parameter_types [::Array<ComplexType>]
285
+ # @param locals [::Array<Pin::LocalVariable>]
286
+ # @return [ComplexType, nil]
287
+ def block_call_type(api_map, name_pin, locals)
246
288
  return nil unless with_block?
247
289
 
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
290
+ block_context_pin = name_pin
291
+ block_pin = find_block_pin(api_map)
292
+ block_context_pin = block_pin.closure if block_pin
293
+ block.infer(api_map, block_context_pin, locals)
264
294
  end
265
295
  end
266
296
  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]
@@ -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,6 +104,17 @@ module Solargraph
103
104
  @string ||= source.string_at?(position)
104
105
  end
105
106
 
107
+
108
+ # True if the cursor's chain is an assignment to a variable.
109
+ #
110
+ # When the chain is an assignment, `Cursor#word` will contain the
111
+ # variable name.
112
+ #
113
+ # @return [Boolean]
114
+ def assign?
115
+ [:lvasgn, :ivasgn, :gvasgn, :cvasgn].include? chain&.node&.type
116
+ end
117
+
106
118
  # Get a cursor pointing to the method that receives the current statement
107
119
  # as an argument.
108
120
  #