solargraph 0.54.0 → 0.54.5

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/lib/solargraph/api_map/cache.rb +10 -1
  4. data/lib/solargraph/api_map/index.rb +167 -0
  5. data/lib/solargraph/api_map/store.rb +72 -121
  6. data/lib/solargraph/api_map.rb +94 -36
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +17 -11
  9. data/lib/solargraph/complex_type/unique_type.rb +93 -10
  10. data/lib/solargraph/complex_type.rb +68 -18
  11. data/lib/solargraph/convention.rb +1 -0
  12. data/lib/solargraph/doc_map.rb +1 -0
  13. data/lib/solargraph/equality.rb +33 -0
  14. data/lib/solargraph/language_server/host/message_worker.rb +48 -5
  15. data/lib/solargraph/language_server/host.rb +12 -11
  16. data/lib/solargraph/language_server/message/base.rb +19 -12
  17. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  18. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  19. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  20. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  21. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  22. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  23. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  24. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  25. data/lib/solargraph/language_server/progress.rb +19 -2
  26. data/lib/solargraph/library.rb +31 -41
  27. data/lib/solargraph/location.rb +21 -1
  28. data/lib/solargraph/parser/node_methods.rb +1 -1
  29. data/lib/solargraph/parser/node_processor.rb +1 -0
  30. data/lib/solargraph/parser/parser_gem/class_methods.rb +2 -6
  31. data/lib/solargraph/parser/parser_gem/node_methods.rb +3 -3
  32. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  33. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  34. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  35. data/lib/solargraph/parser.rb +2 -5
  36. data/lib/solargraph/pin/base.rb +41 -17
  37. data/lib/solargraph/pin/base_variable.rb +4 -3
  38. data/lib/solargraph/pin/block.rb +6 -26
  39. data/lib/solargraph/pin/callable.rb +147 -0
  40. data/lib/solargraph/pin/closure.rb +8 -3
  41. data/lib/solargraph/pin/common.rb +2 -6
  42. data/lib/solargraph/pin/conversions.rb +3 -2
  43. data/lib/solargraph/pin/instance_variable.rb +2 -2
  44. data/lib/solargraph/pin/method.rb +57 -31
  45. data/lib/solargraph/pin/namespace.rb +5 -5
  46. data/lib/solargraph/pin/parameter.rb +11 -12
  47. data/lib/solargraph/pin/proxy_type.rb +1 -1
  48. data/lib/solargraph/pin/signature.rb +3 -129
  49. data/lib/solargraph/pin.rb +4 -1
  50. data/lib/solargraph/position.rb +7 -0
  51. data/lib/solargraph/range.rb +9 -4
  52. data/lib/solargraph/rbs_map/conversions.rb +77 -38
  53. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  54. data/lib/solargraph/rbs_map.rb +1 -0
  55. data/lib/solargraph/shell.rb +19 -2
  56. data/lib/solargraph/source/chain/array.rb +7 -6
  57. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  58. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  59. data/lib/solargraph/source/chain/call.rb +90 -55
  60. data/lib/solargraph/source/chain/hash.rb +5 -0
  61. data/lib/solargraph/source/chain/if.rb +5 -0
  62. data/lib/solargraph/source/chain/link.rb +26 -5
  63. data/lib/solargraph/source/chain/literal.rb +5 -0
  64. data/lib/solargraph/source/chain/or.rb +1 -1
  65. data/lib/solargraph/source/chain.rb +68 -30
  66. data/lib/solargraph/source/cursor.rb +3 -2
  67. data/lib/solargraph/source.rb +104 -86
  68. data/lib/solargraph/source_map/clip.rb +5 -5
  69. data/lib/solargraph/source_map/data.rb +30 -0
  70. data/lib/solargraph/source_map.rb +28 -16
  71. data/lib/solargraph/type_checker/rules.rb +6 -1
  72. data/lib/solargraph/type_checker.rb +15 -15
  73. data/lib/solargraph/version.rb +1 -1
  74. data/lib/solargraph/views/environment.erb +3 -5
  75. data/lib/solargraph/workspace/config.rb +7 -3
  76. data/lib/solargraph/workspace.rb +1 -1
  77. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  78. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  79. data/lib/solargraph/yard_map/mapper.rb +1 -0
  80. data/lib/solargraph/yardoc.rb +1 -1
  81. data/lib/solargraph.rb +1 -0
  82. data/solargraph.gemspec +5 -5
  83. metadata +29 -25
@@ -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.
@@ -132,6 +132,7 @@ module Solargraph
132
132
  result = false
133
133
  filenames.each do |filename|
134
134
  detach filename
135
+ source_map_hash.delete(filename)
135
136
  result ||= workspace.remove(filename)
136
137
  end
137
138
  result
@@ -174,9 +175,9 @@ module Solargraph
174
175
  # @return [Array<Solargraph::Pin::Base>, nil]
175
176
  # @todo Take filename/position instead of filename/line/column
176
177
  def definitions_at filename, line, column
178
+ sync_catalog
177
179
  position = Position.new(line, column)
178
180
  cursor = Source::Cursor.new(read(filename), position)
179
- sync_catalog
180
181
  if cursor.comment?
181
182
  source = read(filename)
182
183
  offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column))
@@ -190,7 +191,10 @@ module Solargraph
190
191
  []
191
192
  end
192
193
  else
193
- mutex.synchronize { api_map.clip(cursor).define.map { |pin| pin.realize(api_map) } }
194
+ mutex.synchronize do
195
+ clip = api_map.clip(cursor)
196
+ clip.define.map { |pin| pin.realize(api_map) }
197
+ end
194
198
  end
195
199
  rescue FileNotFoundError => e
196
200
  handle_file_not_found(filename, e)
@@ -205,9 +209,9 @@ module Solargraph
205
209
  # @return [Array<Solargraph::Pin::Base>, nil]
206
210
  # @todo Take filename/position instead of filename/line/column
207
211
  def type_definitions_at filename, line, column
212
+ sync_catalog
208
213
  position = Position.new(line, column)
209
214
  cursor = Source::Cursor.new(read(filename), position)
210
- sync_catalog
211
215
  mutex.synchronize { api_map.clip(cursor).types }
212
216
  rescue FileNotFoundError => e
213
217
  handle_file_not_found filename, e
@@ -222,9 +226,9 @@ module Solargraph
222
226
  # @return [Array<Solargraph::Pin::Base>]
223
227
  # @todo Take filename/position instead of filename/line/column
224
228
  def signatures_at filename, line, column
229
+ sync_catalog
225
230
  position = Position.new(line, column)
226
231
  cursor = Source::Cursor.new(read(filename), position)
227
- sync_catalog
228
232
  mutex.synchronize { api_map.clip(cursor).signify }
229
233
  end
230
234
 
@@ -233,7 +237,7 @@ module Solargraph
233
237
  # @param column [Integer]
234
238
  # @param strip [Boolean] Strip special characters from variable names
235
239
  # @param only [Boolean] Search for references in the current file only
236
- # @return [Array<Solargraph::Range>]
240
+ # @return [Array<Solargraph::Location>]
237
241
  # @todo Take a Location instead of filename/line/column
238
242
  def references_from filename, line, column, strip: false, only: false
239
243
  sync_catalog
@@ -391,6 +395,8 @@ module Solargraph
391
395
  return [] unless open?(filename)
392
396
  result = []
393
397
  source = read(filename)
398
+
399
+ # @type [Hash{Class<Solargraph::Diagnostics::Base> => Array<String>}]
394
400
  repargs = {}
395
401
  workspace.config.reporters.each do |line|
396
402
  if line == 'all!'
@@ -416,20 +422,7 @@ module Solargraph
416
422
  #
417
423
  # @return [void]
418
424
  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/
425
+ @sync_count += 1
433
426
  end
434
427
 
435
428
  # @return [Bench]
@@ -437,7 +430,8 @@ module Solargraph
437
430
  Bench.new(
438
431
  source_maps: source_map_hash.values,
439
432
  workspace: workspace,
440
- external_requires: external_requires
433
+ external_requires: external_requires,
434
+ live_map: @current ? source_map_hash[@current.filename] : nil
441
435
  )
442
436
  end
443
437
 
@@ -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,9 +1,12 @@
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
8
+ include Equality
9
+
7
10
  # @return [String]
8
11
  attr_reader :filename
9
12
 
@@ -17,6 +20,16 @@ module Solargraph
17
20
  @range = range
18
21
  end
19
22
 
23
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
24
+ protected def equality_fields
25
+ [filename, range]
26
+ end
27
+
28
+ # @param location [self]
29
+ def contain? location
30
+ range.contain?(location.range.start) && range.contain?(location.range.ending) && filename == location.filename
31
+ end
32
+
20
33
  # @return [Hash]
21
34
  def to_hash
22
35
  {
@@ -25,6 +38,13 @@ module Solargraph
25
38
  }
26
39
  end
27
40
 
41
+ # @param node [Parser::AST::Node, nil]
42
+ def self.from_node(node)
43
+ return nil if node.nil? || node.loc.nil?
44
+ range = Range.from_node(node)
45
+ self.new(node.loc.expression.source_buffer.name, range)
46
+ end
47
+
28
48
  # @param other [BasicObject]
29
49
  def == other
30
50
  return false unless other.is_a?(Location)
@@ -74,7 +74,7 @@ module Solargraph
74
74
 
75
75
  # @abstract
76
76
  # @param node [Parser::AST::Node]
77
- # @return [Hash{Parser::AST::Node => Chain}]
77
+ # @return [Hash{Parser::AST::Node => Source::Chain}]
78
78
  def convert_hash node
79
79
  raise NotImplementedError
80
80
  end
@@ -9,6 +9,7 @@ module Solargraph
9
9
  autoload :Base, 'solargraph/parser/node_processor/base'
10
10
 
11
11
  class << self
12
+ # @type [Hash<Symbol, Class<NodeProcessor::Base>>]
12
13
  @@processors ||= {}
13
14
 
14
15
  # Register a processor for a node type.
@@ -11,13 +11,9 @@ module Solargraph
11
11
  # @param filename [String, nil]
12
12
  # @return [Array(Parser::AST::Node, Hash{Integer => String})]
13
13
  def parse_with_comments code, filename = nil
14
- buffer = ::Parser::Source::Buffer.new(filename, 0)
15
- buffer.source = code
16
- node = parser.parse(buffer)
14
+ node = parse(code, filename)
17
15
  comments = CommentRipper.new(code, filename, 0).parse
18
16
  [node, comments]
19
- rescue ::Parser::SyntaxError => e
20
- raise Parser::SyntaxError, e.message
21
17
  end
22
18
 
23
19
  # @param code [String]
@@ -28,7 +24,7 @@ module Solargraph
28
24
  buffer = ::Parser::Source::Buffer.new(filename, line)
29
25
  buffer.source = code
30
26
  parser.parse(buffer)
31
- rescue ::Parser::SyntaxError => e
27
+ rescue ::Parser::SyntaxError, ::Parser::UnknownEncodingInMagicComment => e
32
28
  raise Parser::SyntaxError, e.message
33
29
  end
34
30
 
@@ -12,7 +12,7 @@ require 'ast'
12
12
  # class Node
13
13
  # # New children
14
14
  #
15
- # # @return [Array<AST::Node>]
15
+ # # @return [Array<self>]
16
16
  # attr_reader :children
17
17
  # end
18
18
  # end
@@ -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]
@@ -462,7 +462,7 @@ module Solargraph
462
462
  result
463
463
  end
464
464
 
465
- # @param nodes [Enumerable<Parser::AST::Node, BaseObject>]
465
+ # @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
466
466
  # @return [Array<Parser::AST::Node, nil>]
467
467
  def reduce_to_value_nodes nodes
468
468
  result = []
@@ -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]
@@ -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,11 +71,14 @@ 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
- # @param generics_to_erase [Enumerable<String>]
81
+ # @param generics_to_erase [::Array<String>]
74
82
  # @return [self]
75
83
  def erase_generics(generics_to_erase)
76
84
  return self if generics_to_erase.empty?
@@ -94,7 +102,7 @@ module Solargraph
94
102
  end
95
103
 
96
104
  def to_s
97
- to_rbs
105
+ desc
98
106
  end
99
107
 
100
108
  # @return [Boolean]
@@ -102,12 +110,9 @@ module Solargraph
102
110
  false
103
111
  end
104
112
 
105
- # Pin equality is determined using the #nearly? method and also
106
- # requiring both pins to have the same location.
107
- #
108
- def == other
109
- return false unless nearly? other
110
- comments == other.comments and location == other.location
113
+ # @return [Location, nil]
114
+ def best_location
115
+ location || type_location
111
116
  end
112
117
 
113
118
  # True if the specified pin is a near match to this one. A near match
@@ -126,6 +131,14 @@ module Solargraph
126
131
  )
127
132
  end
128
133
 
134
+ # Pin equality is determined using the #nearly? method and also
135
+ # requiring both pins to have the same location.
136
+ #
137
+ def == other
138
+ return false unless nearly? other
139
+ comments == other.comments && location == other.location
140
+ end
141
+
129
142
  # The pin's return type.
130
143
  #
131
144
  # @return [ComplexType]
@@ -252,9 +265,10 @@ module Solargraph
252
265
  result
253
266
  end
254
267
 
268
+ # @deprecated
255
269
  # @return [String]
256
270
  def identity
257
- @identity ||= "#{closure.path}|#{name}"
271
+ @identity ||= "#{closure&.path}|#{name}"
258
272
  end
259
273
 
260
274
  # @return [String, nil]
@@ -262,19 +276,29 @@ module Solargraph
262
276
  return_type.to_rbs
263
277
  end
264
278
 
265
- # @return [String, nil]
266
- def desc
279
+ # @return [String]
280
+ def type_desc
281
+ rbs = to_rbs
282
+ # RBS doesn't have a way to represent a Class<x> type
283
+ rbs = return_type.rooted_tags if return_type.name == 'Class'
267
284
  if path
268
- if to_rbs
269
- path + ' ' + to_rbs
285
+ if rbs
286
+ path + ' ' + rbs
270
287
  else
271
288
  path
272
289
  end
273
290
  else
274
- to_rbs
291
+ rbs
275
292
  end
276
293
  end
277
294
 
295
+ # @return [String]
296
+ def desc
297
+ closure_info = closure&.desc
298
+ binder_info = binder&.desc
299
+ "[#{type_desc}, closure=#{closure_info}, binder=#{binder}"
300
+ end
301
+
278
302
  def inspect
279
303
  "#<#{self.class} `#{self.desc}` at #{self.location.inspect}>"
280
304
  end
@@ -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
@@ -93,6 +93,7 @@ module Solargraph
93
93
  assignment == other.assignment
94
94
  end
95
95
 
96
+ # @param pin [self]
96
97
  def try_merge! pin
97
98
  return false unless super
98
99
  @assignment = pin.assignment
@@ -100,8 +101,8 @@ module Solargraph
100
101
  true
101
102
  end
102
103
 
103
- def desc
104
- "#{to_rbs} = #{assignment&.type.inspect}"
104
+ def type_desc
105
+ "#{super} = #{assignment&.type.inspect}"
105
106
  end
106
107
 
107
108
  private
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Solargraph
4
4
  module Pin
5
- class Block < Closure
5
+ class Block < Callable
6
6
  # @return [Parser::AST::Node]
7
7
  attr_reader :receiver
8
8
 
@@ -14,10 +14,9 @@ module Solargraph
14
14
  # @param context [ComplexType, nil]
15
15
  # @param args [::Array<Parameter>]
16
16
  def initialize receiver: nil, args: [], context: nil, node: nil, **splat
17
- super(**splat)
17
+ super(**splat, parameters: args)
18
18
  @receiver = receiver
19
19
  @context = context
20
- @parameters = args
21
20
  @return_type = ComplexType.parse('::Proc')
22
21
  @node = node
23
22
  end
@@ -32,38 +31,19 @@ module Solargraph
32
31
  @rebind&.defined? ? @rebind : closure.binder
33
32
  end
34
33
 
35
- # @return [::Array<Parameter>]
36
- def parameters
37
- @parameters ||= []
38
- end
39
-
40
- # @return [::Array<String>]
41
- def parameter_names
42
- @parameter_names ||= parameters.map(&:name)
43
- end
44
-
45
34
  # @param yield_types [::Array<ComplexType>]
46
35
  # @param parameters [::Array<Parameter>]
47
36
  #
48
37
  # @return [::Array<ComplexType>]
49
38
  def destructure_yield_types(yield_types, parameters)
50
- return yield_types if yield_types.length == parameters.length
51
-
52
39
  # yielding a tuple into a block will destructure the tuple
53
40
  if yield_types.length == 1
54
41
  yield_type = yield_types.first
55
42
  return yield_type.all_params if yield_type.tuple? && yield_type.all_params.length == parameters.length
56
43
  end
57
- parameters.map { ComplexType::UNDEFINED }
44
+ parameters.map.with_index { |_, idx| yield_types[idx] || ComplexType::UNDEFINED }
58
45
  end
59
46
 
60
- # @todo the next step with parameters, arguments, destructuring,
61
- # kwargs, etc logic is probably either creating a Parameters
62
- # or Callable pin that encapsulates and shares the logic
63
- # between methods, blocks and signatures. It could live in
64
- # Signature if Method didn't also own potentially different
65
- # set of parameters, generics and return types.
66
-
67
47
  # @param api_map [ApiMap]
68
48
  # @return [::Array<ComplexType>]
69
49
  def typify_parameters(api_map)
@@ -87,7 +67,7 @@ module Solargraph
87
67
  namespace_pin = api_map.get_namespace_pins(meth.namespace, closure.namespace).first
88
68
  arg_type.resolve_generics(namespace_pin, param_type)
89
69
  else
90
- arg_type.self_to(chain.base.infer(api_map, self, locals).namespace).qualify(api_map, meth.context.namespace)
70
+ arg_type.self_to_type(chain.base.infer(api_map, self, locals)).qualify(api_map, meth.context.namespace)
91
71
  end
92
72
  end
93
73
  end
@@ -105,7 +85,7 @@ module Solargraph
105
85
 
106
86
  chain = Parser.chain(receiver, location.filename)
107
87
  locals = api_map.source_map(location.filename).locals_at(location)
108
- receiver_pin = chain.define(api_map, self, locals).first
88
+ receiver_pin = chain.define(api_map, closure, locals).first
109
89
  return ComplexType::UNDEFINED unless receiver_pin
110
90
 
111
91
  types = receiver_pin.docstring.tag(:yieldreceiver)&.types
@@ -114,7 +94,7 @@ module Solargraph
114
94
  target = chain.base.infer(api_map, receiver_pin, locals)
115
95
  target = full_context unless target.defined?
116
96
 
117
- ComplexType.try_parse(*types).qualify(api_map, receiver_pin.context.namespace).self_to(target.to_s)
97
+ ComplexType.try_parse(*types).qualify(api_map, receiver_pin.context.namespace).self_to_type(target)
118
98
  end
119
99
  end
120
100
  end