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
@@ -24,10 +24,8 @@ module Solargraph
24
24
  attr_writer :client_capabilities
25
25
 
26
26
  def initialize
27
- @cancel_semaphore = Mutex.new
28
27
  @buffer_semaphore = Mutex.new
29
28
  @request_mutex = Mutex.new
30
- @cancel = []
31
29
  @buffer = String.new
32
30
  @stopped = true
33
31
  @next_request_id = 1
@@ -65,7 +63,7 @@ module Solargraph
65
63
  # @param id [Integer]
66
64
  # @return [void]
67
65
  def cancel id
68
- @cancel_semaphore.synchronize { @cancel.push id }
66
+ cancelled.push id
69
67
  end
70
68
 
71
69
  # True if the host received a request to cancel the method with the
@@ -74,9 +72,7 @@ module Solargraph
74
72
  # @param id [Integer]
75
73
  # @return [Boolean]
76
74
  def cancel? id
77
- result = false
78
- @cancel_semaphore.synchronize { result = @cancel.include? id }
79
- result
75
+ cancelled.include? id
80
76
  end
81
77
 
82
78
  # Delete the specified ID from the list of cancelled IDs if it exists.
@@ -84,7 +80,7 @@ module Solargraph
84
80
  # @param id [Integer]
85
81
  # @return [void]
86
82
  def clear id
87
- @cancel_semaphore.synchronize { @cancel.delete id }
83
+ cancelled.delete id
88
84
  end
89
85
 
90
86
  # Called by adapter, to handle the request
@@ -101,11 +97,11 @@ module Solargraph
101
97
  # @return [Solargraph::LanguageServer::Message::Base, nil] The message handler.
102
98
  def receive request
103
99
  if request['method']
104
- logger.info "Server received #{request['method']}"
100
+ logger.info "Host received ##{request['id']} #{request['method']}"
105
101
  logger.debug request
106
102
  message = Message.select(request['method']).new(self, request)
107
103
  begin
108
- message.process
104
+ message.process unless cancel?(request['id'])
109
105
  rescue StandardError => e
110
106
  logger.warn "Error processing request: [#{e.class}] #{e.message}"
111
107
  logger.warn e.backtrace.join("\n")
@@ -381,7 +377,6 @@ module Solargraph
381
377
  envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
382
378
  queue envelope
383
379
  @next_request_id += 1
384
- logger.info "Server sent #{method}"
385
380
  logger.debug params
386
381
  end
387
382
  end
@@ -693,6 +688,11 @@ module Solargraph
693
688
 
694
689
  private
695
690
 
691
+ # @return [Array<Integer>]
692
+ def cancelled
693
+ @cancelled ||= []
694
+ end
695
+
696
696
  # @return [MessageWorker]
697
697
  def message_worker
698
698
  @message_worker ||= MessageWorker.new(self)
@@ -61,18 +61,11 @@ module Solargraph
61
61
  # @return [void]
62
62
  def send_response
63
63
  return if id.nil?
64
- if host.cancel?(id)
65
- # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
66
- # cancel should send response RequestCancelled
67
- Solargraph::Logging.logger.info "Cancelled response to #{method}"
68
- set_result nil
69
- set_error ErrorCodes::REQUEST_CANCELLED, "cancelled by client"
70
- else
71
- Solargraph::Logging.logger.info "Sending response to #{method}"
72
- end
64
+
65
+ accept_or_cancel
73
66
  response = {
74
- jsonrpc: "2.0",
75
- id: id,
67
+ jsonrpc: '2.0',
68
+ id: id
76
69
  }
77
70
  response[:result] = result unless result.nil?
78
71
  response[:error] = error unless error.nil?
@@ -83,6 +76,20 @@ module Solargraph
83
76
  host.queue envelope
84
77
  host.clear id
85
78
  end
79
+
80
+ private
81
+
82
+ def accept_or_cancel
83
+ if host.cancel?(id)
84
+ # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
85
+ # cancel should send response RequestCancelled
86
+ Solargraph::Logging.logger.info "Cancelled response to ##{id} #{method}"
87
+ set_result nil
88
+ set_error ErrorCodes::REQUEST_CANCELLED, 'Cancelled by client'
89
+ else
90
+ Solargraph::Logging.logger.info "Sending response to ##{id} #{method}"
91
+ end
92
+ end
86
93
  end
87
94
  end
88
95
  end
@@ -12,9 +12,6 @@ module Solargraph
12
12
  col = params['position']['character']
13
13
  begin
14
14
  completion = host.completions_at(params['textDocument']['uri'], line, col)
15
- if host.cancel?(id)
16
- return set_result(empty_result) if host.cancel?(id)
17
- end
18
15
  items = []
19
16
  last_context = nil
20
17
  idx = -1
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
13
13
  def code_location
14
14
  suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column)
15
15
  return nil if suggestions.empty?
16
- suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin|
16
+ suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
17
17
  {
18
- uri: file_to_uri(pin.location.filename),
19
- range: pin.location.range.to_hash
18
+ uri: file_to_uri(pin.best_location.filename),
19
+ range: pin.best_location.range.to_hash
20
20
  }
21
21
  end
22
22
  end
@@ -6,15 +6,15 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
6
6
  def process
7
7
  pins = host.document_symbols params['textDocument']['uri']
8
8
  info = pins.map do |pin|
9
- next nil unless pin.location&.filename
9
+ next nil unless pin.best_location&.filename
10
10
 
11
11
  result = {
12
12
  name: pin.name,
13
13
  containerName: pin.namespace,
14
14
  kind: pin.symbol_kind,
15
15
  location: {
16
- uri: file_to_uri(pin.location.filename),
17
- range: pin.location.range.to_hash
16
+ uri: file_to_uri(pin.best_location.filename),
17
+ range: pin.best_location.range.to_hash
18
18
  },
19
19
  deprecated: pin.deprecated?
20
20
  }
@@ -21,7 +21,7 @@ module Solargraph
21
21
  parts.push pin.documentation unless pin.documentation.nil? || pin.documentation.empty?
22
22
  unless parts.empty?
23
23
  data = parts.join("\n\n")
24
- next if contents.last && contents.last.end_with?(data)
24
+ next if contents.last&.end_with?(data)
25
25
  contents.push data
26
26
  end
27
27
  last_link = this_link unless this_link.nil?
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
13
13
  def code_location
14
14
  suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column)
15
15
  return nil if suggestions.empty?
16
- suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin|
16
+ suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
17
17
  {
18
- uri: file_to_uri(pin.location.filename),
19
- range: pin.location.range.to_hash
18
+ uri: file_to_uri(pin.best_location.filename),
19
+ range: pin.best_location.range.to_hash
20
20
  }
21
21
  end
22
22
  end
@@ -6,14 +6,14 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra
6
6
  def process
7
7
  pins = host.query_symbols(params['query'])
8
8
  info = pins.map do |pin|
9
- uri = file_to_uri(pin.location.filename)
9
+ uri = file_to_uri(pin.best_location.filename)
10
10
  {
11
11
  name: pin.path,
12
12
  containerName: pin.namespace,
13
13
  kind: pin.symbol_kind,
14
14
  location: {
15
15
  uri: uri,
16
- range: pin.location.range.to_hash
16
+ range: pin.best_location.range.to_hash
17
17
  },
18
18
  deprecated: pin.deprecated?
19
19
  }
@@ -66,10 +66,10 @@ module Solargraph
66
66
  return unless host.client_supports_progress? && !finished?
67
67
 
68
68
  message = build
69
-
70
- create(host) unless created?
69
+ create(host)
71
70
  host.send_notification '$/progress', message
72
71
  @status = FINISHED if kind == 'end'
72
+ keep_alive host
73
73
  end
74
74
 
75
75
  def created?
@@ -113,6 +113,23 @@ module Solargraph
113
113
  raise "Invalid progress kind #{kind}"
114
114
  end
115
115
  end
116
+
117
+ # @param host [Host]
118
+ def keep_alive host
119
+ mutex.synchronize { @last = Time.now }
120
+ @keep_alive ||= Thread.new do
121
+ until finished?
122
+ sleep 10
123
+ break if finished?
124
+ next if mutex.synchronize { Time.now - @last < 10 }
125
+ send host
126
+ end
127
+ end
128
+ end
129
+
130
+ def mutex
131
+ @mutex ||= Mutex.new
132
+ end
116
133
  end
117
134
  end
118
135
  end
@@ -27,11 +27,11 @@ module Solargraph
27
27
  def initialize workspace = Solargraph::Workspace.new, name = nil
28
28
  @workspace = workspace
29
29
  @name = name
30
- @threads = []
31
30
  # @type [Integer, nil]
32
31
  @total = nil
33
32
  # @type [Source, nil]
34
33
  @current = nil
34
+ @sync_count = 0
35
35
  end
36
36
 
37
37
  def inspect
@@ -44,7 +44,7 @@ module Solargraph
44
44
  #
45
45
  # @return [Boolean]
46
46
  def synchronized?
47
- !mutex.locked?
47
+ @sync_count < 2
48
48
  end
49
49
 
50
50
  # Attach a source to the library.
@@ -174,9 +174,9 @@ module Solargraph
174
174
  # @return [Array<Solargraph::Pin::Base>, nil]
175
175
  # @todo Take filename/position instead of filename/line/column
176
176
  def definitions_at filename, line, column
177
+ sync_catalog
177
178
  position = Position.new(line, column)
178
179
  cursor = Source::Cursor.new(read(filename), position)
179
- sync_catalog
180
180
  if cursor.comment?
181
181
  source = read(filename)
182
182
  offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column))
@@ -190,7 +190,14 @@ module Solargraph
190
190
  []
191
191
  end
192
192
  else
193
- mutex.synchronize { api_map.clip(cursor).define.map { |pin| pin.realize(api_map) } }
193
+ mutex.synchronize do
194
+ clip = api_map.clip(cursor)
195
+ if cursor.assign?
196
+ [Pin::ProxyType.new(name: cursor.word, return_type: clip.infer)]
197
+ else
198
+ clip.define.map { |pin| pin.realize(api_map) }
199
+ end
200
+ end
194
201
  end
195
202
  rescue FileNotFoundError => e
196
203
  handle_file_not_found(filename, e)
@@ -205,9 +212,9 @@ module Solargraph
205
212
  # @return [Array<Solargraph::Pin::Base>, nil]
206
213
  # @todo Take filename/position instead of filename/line/column
207
214
  def type_definitions_at filename, line, column
215
+ sync_catalog
208
216
  position = Position.new(line, column)
209
217
  cursor = Source::Cursor.new(read(filename), position)
210
- sync_catalog
211
218
  mutex.synchronize { api_map.clip(cursor).types }
212
219
  rescue FileNotFoundError => e
213
220
  handle_file_not_found filename, e
@@ -222,9 +229,9 @@ module Solargraph
222
229
  # @return [Array<Solargraph::Pin::Base>]
223
230
  # @todo Take filename/position instead of filename/line/column
224
231
  def signatures_at filename, line, column
232
+ sync_catalog
225
233
  position = Position.new(line, column)
226
234
  cursor = Source::Cursor.new(read(filename), position)
227
- sync_catalog
228
235
  mutex.synchronize { api_map.clip(cursor).signify }
229
236
  end
230
237
 
@@ -416,20 +423,7 @@ module Solargraph
416
423
  #
417
424
  # @return [void]
418
425
  def catalog
419
- @threads.delete_if(&:stop?)
420
- @threads.push(Thread.new do
421
- sleep 0.05 if RUBY_PLATFORM =~ /mingw/
422
- next unless @threads.last == Thread.current
423
-
424
- mutex.synchronize do
425
- logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
426
- api_map.catalog bench
427
- logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
428
- logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs"
429
- cache_next_gemspec
430
- end
431
- end)
432
- @threads.last.run if RUBY_PLATFORM =~ /mingw/
426
+ @sync_count += 1
433
427
  end
434
428
 
435
429
  # @return [Bench]
@@ -467,7 +461,6 @@ module Solargraph
467
461
  # @param source [Source]
468
462
  # @return [Boolean] True if the source was merged into the workspace.
469
463
  def merge source
470
- Logging.logger.debug "Merging source: #{source.filename}"
471
464
  result = workspace.merge(source)
472
465
  maybe_map source
473
466
  result
@@ -489,7 +482,6 @@ module Solargraph
489
482
  if src
490
483
  Logging.logger.debug "Mapping #{src.filename}"
491
484
  source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
492
- find_external_requires(source_map_hash[src.filename])
493
485
  source_map_hash[src.filename]
494
486
  else
495
487
  false
@@ -500,7 +492,7 @@ module Solargraph
500
492
  def map!
501
493
  workspace.sources.each do |src|
502
494
  source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
503
- find_external_requires(source_map_hash[src.filename])
495
+ find_external_requires source_map_hash[src.filename]
504
496
  end
505
497
  self
506
498
  end
@@ -580,22 +572,10 @@ module Solargraph
580
572
  return unless source
581
573
  return unless @current == source || workspace.has_file?(source.filename)
582
574
  if source_map_hash.key?(source.filename)
583
- return if source_map_hash[source.filename].code == source.code &&
584
- source_map_hash[source.filename].source.synchronized? &&
585
- source.synchronized?
586
- if source.synchronized?
587
- new_map = Solargraph::SourceMap.map(source)
588
- unless source_map_hash[source.filename].try_merge!(new_map)
589
- source_map_hash[source.filename] = new_map
590
- find_external_requires(source_map_hash[source.filename])
591
- end
592
- else
593
- # @todo Smelly instance variable access
594
- source_map_hash[source.filename].instance_variable_set(:@source, source)
595
- end
575
+ new_map = Solargraph::SourceMap.map(source)
576
+ source_map_hash[source.filename] = new_map
596
577
  else
597
578
  source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
598
- find_external_requires(source_map_hash[source.filename])
599
579
  end
600
580
  end
601
581
 
@@ -625,6 +605,7 @@ module Solargraph
625
605
  ensure
626
606
  end_cache_progress
627
607
  catalog
608
+ sync_catalog
628
609
  end
629
610
  end
630
611
 
@@ -666,8 +647,17 @@ module Solargraph
666
647
  end
667
648
 
668
649
  def sync_catalog
669
- @threads.delete_if(&:stop?)
670
- .last&.join
650
+ return if @sync_count == 0
651
+
652
+ mutex.synchronize do
653
+ logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
654
+ api_map.catalog bench
655
+ source_map_hash.values.each { |map| find_external_requires(map) }
656
+ logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
657
+ logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs"
658
+ cache_next_gemspec
659
+ @sync_count = 0
660
+ end
671
661
  end
672
662
  end
673
663
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- # A section of text identified by its filename and range.
4
+ # A pointer to a section of source text identified by its filename
5
+ # and Range.
5
6
  #
6
7
  class Location
7
8
  # @return [String]
@@ -17,6 +18,11 @@ module Solargraph
17
18
  @range = range
18
19
  end
19
20
 
21
+ # @param location [self]
22
+ def contain? location
23
+ range.contain?(location.range.start) && range.contain?(location.range.ending) && filename == location.filename
24
+ end
25
+
20
26
  # @return [Hash]
21
27
  def to_hash
22
28
  {
@@ -25,6 +31,13 @@ module Solargraph
25
31
  }
26
32
  end
27
33
 
34
+ # @param node [Parser::AST::Node, nil]
35
+ def self.from_node(node)
36
+ return nil if node.nil? || node.loc.nil?
37
+ range = Range.from_node(node)
38
+ self.new(node.loc.expression.source_buffer.name, range)
39
+ end
40
+
28
41
  # @param other [BasicObject]
29
42
  def == other
30
43
  return false unless other.is_a?(Location)
@@ -89,15 +89,21 @@ module Solargraph
89
89
  elsif n.type == :const
90
90
  const = unpack_name(n)
91
91
  result.push Chain::Constant.new(const)
92
- elsif [:lvar, :lvasgn].include?(n.type)
92
+ elsif [:lvasgn, :ivasgn, :gvasgn, :cvasgn].include?(n.type)
93
+ result.concat generate_links(n.children[1])
94
+ elsif n.type == :lvar
93
95
  result.push Chain::Call.new(n.children[0].to_s)
94
- elsif [:ivar, :ivasgn].include?(n.type)
95
- result.push Chain::InstanceVariable.new(n.children[0].to_s)
96
- elsif [:cvar, :cvasgn].include?(n.type)
97
- result.push Chain::ClassVariable.new(n.children[0].to_s)
98
- elsif [:gvar, :gvasgn].include?(n.type)
99
- result.push Chain::GlobalVariable.new(n.children[0].to_s)
96
+ elsif n.type == :ivar
97
+ result.push Chain::InstanceVariable.new(n.children[0].to_s)
98
+ elsif n.type == :cvar
99
+ result.push Chain::ClassVariable.new(n.children[0].to_s)
100
+ elsif n.type == :gvar
101
+ result.push Chain::GlobalVariable.new(n.children[0].to_s)
100
102
  elsif n.type == :or_asgn
103
+ # @todo: Need a new Link class here that evaluates the
104
+ # existing variable type with the RHS, and generates a
105
+ # union type of the LHS alone if never nil, or minus nil +
106
+ # RHS if it is nilable.
101
107
  result.concat generate_links n.children[1]
102
108
  elsif [:class, :module, :def, :defs].include?(n.type)
103
109
  # @todo Undefined or what?
@@ -311,7 +311,7 @@ module Solargraph
311
311
  # statements in value positions.
312
312
  module DeepInference
313
313
  class << self
314
- CONDITIONAL_ALL_BUT_FIRST = [:if, :unless, :or_asgn]
314
+ CONDITIONAL_ALL_BUT_FIRST = [:if, :unless]
315
315
  CONDITIONAL_ALL = [:or]
316
316
  ONLY_ONE_CHILD = [:return]
317
317
  FIRST_TWO_CHILDREN = [:rescue]
@@ -6,22 +6,25 @@ module Solargraph
6
6
  module NodeProcessors
7
7
  class ArgsNode < Parser::NodeProcessor::Base
8
8
  def process
9
- if node.type == :forward_args
10
- forward
11
- else
12
- node.children.each do |u|
13
- loc = get_node_location(u)
14
- locals.push Solargraph::Pin::Parameter.new(
15
- location: loc,
16
- closure: region.closure,
17
- comments: comments_for(node),
18
- name: u.children[0].to_s,
19
- assignment: u.children[1],
20
- asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil,
21
- presence: region.closure.location.range,
22
- decl: get_decl(u)
23
- )
24
- region.closure.parameters.push locals.last
9
+ callable = region.closure
10
+ if callable.is_a? Pin::Callable
11
+ if node.type == :forward_args
12
+ forward(callable)
13
+ else
14
+ node.children.each do |u|
15
+ loc = get_node_location(u)
16
+ locals.push Solargraph::Pin::Parameter.new(
17
+ location: loc,
18
+ closure: callable,
19
+ comments: comments_for(node),
20
+ name: u.children[0].to_s,
21
+ assignment: u.children[1],
22
+ asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil,
23
+ presence: callable.location.range,
24
+ decl: get_decl(u)
25
+ )
26
+ callable.parameters.push locals.last
27
+ end
25
28
  end
26
29
  end
27
30
  process_children
@@ -29,16 +32,17 @@ module Solargraph
29
32
 
30
33
  private
31
34
 
35
+ # @param callable [Pin::Callable]
32
36
  # @return [void]
33
- def forward
37
+ def forward(callable)
34
38
  loc = get_node_location(node)
35
39
  locals.push Solargraph::Pin::Parameter.new(
36
40
  location: loc,
37
- closure: region.closure,
41
+ closure: callable,
38
42
  presence: region.closure.location.range,
39
43
  decl: get_decl(node)
40
44
  )
41
- region.closure.parameters.push locals.last
45
+ callable.parameters.push locals.last
42
46
  end
43
47
 
44
48
  # @param node [AST::Node]
@@ -8,8 +8,8 @@ module Solargraph
8
8
  include ParserGem::NodeMethods
9
9
 
10
10
  def process
11
- here = get_node_start_position(node)
12
- presence = Range.new(here, region.closure.location.range.ending)
11
+ # variable not visible until next statement
12
+ presence = Range.new(get_node_end_position(node), region.closure.location.range.ending)
13
13
  loc = get_node_location(node)
14
14
  locals.push Solargraph::Pin::LocalVariable.new(
15
15
  location: loc,
@@ -30,11 +30,17 @@ module Solargraph
30
30
 
31
31
  lhs_arr.each_with_index do |lhs, i|
32
32
  location = get_node_location(lhs)
33
+ pin = if lhs.type == :lvasgn
34
+ # lvasgn is a local variable
35
+ locals.find { |l| l.location == location }
36
+ else
37
+ # e.g., ivasgn is an instance variable, etc
38
+ pins.find { |iv| iv.location == location && iv.is_a?(Pin::BaseVariable) }
39
+ end
33
40
  # @todo in line below, nothing in typechecking alerts
34
41
  # when a non-existant method is called on 'l'
35
- pin = locals.find { |l| l.location == location }
36
42
  if pin.nil?
37
- Solargraph.logger.debug "Could not find pin in location #{location}"
43
+ Solargraph.logger.debug { "Could not find local for masgn= value in location #{location.inspect} in #{lhs_arr} - masgn = #{masgn}, lhs.type = #{lhs.type}" }
38
44
  next
39
45
  end
40
46
  pin.mass_assignment = [mass_rhs, i]
@@ -92,7 +92,7 @@ module Solargraph
92
92
  pins.push method_pin
93
93
  method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
94
94
  if method_pin.return_type.defined?
95
- pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
95
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.items.map(&:rooted_tags), 'value')
96
96
  end
97
97
  end
98
98
  end
@@ -9,15 +9,12 @@ module Solargraph
9
9
  class SyntaxError < StandardError
10
10
  end
11
11
 
12
- def self.rubyvm?
13
- false
14
- end
15
-
16
12
  # @deprecated
17
13
  Legacy = ParserGem
18
14
 
19
15
  ClassMethods = ParserGem::ClassMethods
20
-
16
+ # @todo should be able to just 'extend ClassMethods' here and
17
+ # typecheck things off it in strict mode
21
18
  extend ParserGem::ClassMethods
22
19
 
23
20
  NodeMethods = ParserGem::NodeMethods
@@ -15,6 +15,9 @@ module Solargraph
15
15
  # @return [Solargraph::Location]
16
16
  attr_reader :location
17
17
 
18
+ # @return [Solargraph::Location]
19
+ attr_reader :type_location
20
+
18
21
  # @return [String]
19
22
  attr_reader :name
20
23
 
@@ -25,11 +28,13 @@ module Solargraph
25
28
  attr_accessor :source
26
29
 
27
30
  # @param location [Solargraph::Location, nil]
31
+ # @param type_location [Solargraph::Location, nil]
28
32
  # @param closure [Solargraph::Pin::Closure, nil]
29
33
  # @param name [String]
30
34
  # @param comments [String]
31
- def initialize location: nil, closure: nil, name: '', comments: ''
35
+ def initialize location: nil, type_location: nil, closure: nil, name: '', comments: ''
32
36
  @location = location
37
+ @type_location = type_location
33
38
  @closure = closure
34
39
  @name = name
35
40
  @comments = comments
@@ -66,8 +71,11 @@ module Solargraph
66
71
  # @param context_type [ComplexType] The receiver type
67
72
  # @return [self]
68
73
  def resolve_generics definitions, context_type
69
- transformed = transform_types { |t| t.resolve_generics(definitions, context_type) if t }
70
- transformed.erase_generics(definitions.generics)
74
+ transform_types { |t| t.resolve_generics(definitions, context_type) if t }
75
+ end
76
+
77
+ def all_rooted?
78
+ !return_type || return_type.all_rooted?
71
79
  end
72
80
 
73
81
  # @param generics_to_erase [Enumerable<String>]
@@ -102,6 +110,11 @@ module Solargraph
102
110
  false
103
111
  end
104
112
 
113
+ # @return [Location, nil]
114
+ def best_location
115
+ location || type_location
116
+ end
117
+
105
118
  # Pin equality is determined using the #nearly? method and also
106
119
  # requiring both pins to have the same location.
107
120
  #
@@ -57,7 +57,7 @@ module Solargraph
57
57
  # Use the return node for inference. The clip might infer from the
58
58
  # first node in a method call instead of the entire call.
59
59
  chain = Parser.chain(node, nil, nil)
60
- result = chain.infer(api_map, closure, clip.locals).self_to(closure.context.tag)
60
+ result = chain.infer(api_map, closure, clip.locals).self_to_type(closure.context)
61
61
  types.push result unless result.undefined?
62
62
  end
63
63
  end