solargraph 0.32.5 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +2 -11
  4. data/lib/solargraph.rb +1 -2
  5. data/lib/solargraph/api_map.rb +93 -63
  6. data/lib/solargraph/api_map/cache.rb +16 -1
  7. data/lib/solargraph/api_map/source_to_yard.rb +16 -7
  8. data/lib/solargraph/api_map/store.rb +55 -12
  9. data/lib/solargraph/complex_type.rb +58 -14
  10. data/lib/solargraph/complex_type/type_methods.rb +2 -2
  11. data/lib/solargraph/complex_type/unique_type.rb +33 -4
  12. data/lib/solargraph/core_fills.rb +40 -12
  13. data/lib/solargraph/diagnostics.rb +4 -3
  14. data/lib/solargraph/diagnostics/base.rb +6 -0
  15. data/lib/solargraph/diagnostics/require_not_found.rb +17 -10
  16. data/lib/solargraph/diagnostics/rubocop_helpers.rb +2 -0
  17. data/lib/solargraph/diagnostics/type_check.rb +51 -0
  18. data/lib/solargraph/diagnostics/update_errors.rb +1 -0
  19. data/lib/solargraph/language_server/host.rb +55 -25
  20. data/lib/solargraph/language_server/host/diagnoser.rb +1 -2
  21. data/lib/solargraph/language_server/host/dispatch.rb +4 -8
  22. data/lib/solargraph/language_server/host/sources.rb +1 -1
  23. data/lib/solargraph/language_server/message.rb +1 -0
  24. data/lib/solargraph/language_server/message/completion_item/resolve.rb +4 -2
  25. data/lib/solargraph/language_server/message/initialize.rb +9 -0
  26. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  27. data/lib/solargraph/language_server/message/text_document.rb +1 -0
  28. data/lib/solargraph/language_server/message/text_document/code_action.rb +15 -0
  29. data/lib/solargraph/language_server/message/text_document/completion.rb +1 -1
  30. data/lib/solargraph/language_server/message/text_document/definition.rb +25 -5
  31. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  32. data/lib/solargraph/language_server/message/text_document/signature_help.rb +4 -0
  33. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +8 -4
  34. data/lib/solargraph/language_server/transport/adapter.rb +12 -15
  35. data/lib/solargraph/library.rb +23 -6
  36. data/lib/solargraph/location.rb +4 -0
  37. data/lib/solargraph/pin.rb +7 -3
  38. data/lib/solargraph/pin/attribute.rb +14 -13
  39. data/lib/solargraph/pin/base.rb +56 -43
  40. data/lib/solargraph/pin/base_method.rb +41 -18
  41. data/lib/solargraph/pin/base_variable.rb +17 -15
  42. data/lib/solargraph/pin/block.rb +22 -4
  43. data/lib/solargraph/pin/closure.rb +28 -0
  44. data/lib/solargraph/pin/common.rb +59 -0
  45. data/lib/solargraph/pin/constant.rb +4 -4
  46. data/lib/solargraph/pin/conversions.rb +8 -8
  47. data/lib/solargraph/pin/duck_method.rb +3 -3
  48. data/lib/solargraph/pin/instance_variable.rb +30 -0
  49. data/lib/solargraph/pin/keyword.rb +1 -1
  50. data/lib/solargraph/pin/local_variable.rb +3 -3
  51. data/lib/solargraph/pin/localized.rb +9 -5
  52. data/lib/solargraph/pin/method.rb +26 -40
  53. data/lib/solargraph/pin/method_alias.rb +9 -6
  54. data/lib/solargraph/pin/namespace.rb +33 -10
  55. data/lib/solargraph/pin/parameter.rb +150 -0
  56. data/lib/solargraph/pin/proxy_type.rb +8 -8
  57. data/lib/solargraph/pin/reference.rb +1 -12
  58. data/lib/solargraph/pin/reference/override.rb +18 -0
  59. data/lib/solargraph/pin/reference/require.rb +2 -1
  60. data/lib/solargraph/pin/singleton.rb +9 -0
  61. data/lib/solargraph/pin/symbol.rb +9 -4
  62. data/lib/solargraph/pin/yard_pin/constant.rb +12 -3
  63. data/lib/solargraph/pin/yard_pin/method.rb +18 -6
  64. data/lib/solargraph/pin/yard_pin/namespace.rb +13 -1
  65. data/lib/solargraph/position.rb +1 -1
  66. data/lib/solargraph/range.rb +4 -0
  67. data/lib/solargraph/shell.rb +83 -4
  68. data/lib/solargraph/source.rb +32 -12
  69. data/lib/solargraph/source/chain.rb +48 -28
  70. data/lib/solargraph/source/chain/call.rb +37 -38
  71. data/lib/solargraph/source/chain/constant.rb +1 -1
  72. data/lib/solargraph/source/chain/head.rb +2 -8
  73. data/lib/solargraph/source/chain/instance_variable.rb +1 -1
  74. data/lib/solargraph/source/chain/link.rb +2 -0
  75. data/lib/solargraph/source/cursor.rb +59 -24
  76. data/lib/solargraph/source/node_chainer.rb +0 -2
  77. data/lib/solargraph/source/node_methods.rb +12 -6
  78. data/lib/solargraph/source/source_chainer.rb +38 -44
  79. data/lib/solargraph/source_map.rb +11 -18
  80. data/lib/solargraph/source_map/clip.rb +13 -15
  81. data/lib/solargraph/source_map/mapper.rb +37 -26
  82. data/lib/solargraph/source_map/node_processor.rb +13 -8
  83. data/lib/solargraph/source_map/node_processor/alias_node.rb +8 -8
  84. data/lib/solargraph/source_map/node_processor/args_node.rb +10 -16
  85. data/lib/solargraph/source_map/node_processor/base.rb +47 -4
  86. data/lib/solargraph/source_map/node_processor/block_node.rb +9 -4
  87. data/lib/solargraph/source_map/node_processor/casgn_node.rb +7 -2
  88. data/lib/solargraph/source_map/node_processor/cvasgn_node.rb +8 -3
  89. data/lib/solargraph/source_map/node_processor/def_node.rb +45 -38
  90. data/lib/solargraph/source_map/node_processor/defs_node.rb +16 -6
  91. data/lib/solargraph/source_map/node_processor/gvasgn_node.rb +8 -1
  92. data/lib/solargraph/source_map/node_processor/ivasgn_node.rb +20 -6
  93. data/lib/solargraph/source_map/node_processor/lvasgn_node.rb +10 -4
  94. data/lib/solargraph/source_map/node_processor/namespace_node.rb +18 -12
  95. data/lib/solargraph/source_map/node_processor/orasgn_node.rb +1 -1
  96. data/lib/solargraph/source_map/node_processor/resbody_node.rb +30 -0
  97. data/lib/solargraph/source_map/node_processor/sclass_node.rb +7 -1
  98. data/lib/solargraph/source_map/node_processor/send_node.rb +102 -52
  99. data/lib/solargraph/source_map/node_processor/sym_node.rb +4 -1
  100. data/lib/solargraph/source_map/region.rb +9 -8
  101. data/lib/solargraph/type_checker.rb +282 -0
  102. data/lib/solargraph/type_checker/param_def.rb +47 -0
  103. data/lib/solargraph/type_checker/problem.rb +25 -0
  104. data/lib/solargraph/version.rb +1 -1
  105. data/lib/solargraph/views/environment.erb +1 -1
  106. data/lib/solargraph/workspace.rb +2 -2
  107. data/lib/solargraph/workspace/config.rb +0 -8
  108. data/lib/solargraph/yard_map.rb +25 -69
  109. data/lib/solargraph/yard_map/core_docs.rb +8 -3
  110. data/lib/solargraph/yard_map/core_gen.rb +1 -3
  111. data/lib/solargraph/yard_map/mapper.rb +85 -0
  112. data/lib/yard-solargraph.rb +2 -0
  113. metadata +14 -14
  114. data/lib/solargraph/diagnostics/type_not_defined.rb +0 -108
  115. data/lib/solargraph/live_map.rb +0 -126
  116. data/lib/solargraph/live_map/cache.rb +0 -38
  117. data/lib/solargraph/pin/block_parameter.rb +0 -103
  118. data/lib/solargraph/pin/method_parameter.rb +0 -40
  119. data/lib/solargraph/pin/plugin/method.rb +0 -25
  120. data/lib/solargraph/plugin.rb +0 -8
  121. data/lib/solargraph/plugin/base.rb +0 -41
  122. data/lib/solargraph/plugin/canceler.rb +0 -11
  123. data/lib/solargraph/plugin/process.rb +0 -172
  124. data/lib/solargraph/plugin/runtime.rb +0 -134
  125. data/lib/yard-coregen.rb +0 -16
@@ -3,7 +3,8 @@ module Solargraph
3
3
  class Reference
4
4
  class Require < Reference
5
5
  def initialize location, name
6
- super(location, '', name)
6
+ # super(location, '', name)
7
+ super(location: location, name: name)
7
8
  end
8
9
 
9
10
  def kind
@@ -0,0 +1,9 @@
1
+ module Solargraph
2
+ module Pin
3
+ class Singleton < Closure
4
+ def initialize name: '', location: nil, closure: nil
5
+ super
6
+ end
7
+ end
8
+ end
9
+ end
@@ -4,8 +4,9 @@ module Solargraph
4
4
  # @param location [Solargraph::Location]
5
5
  # @param name [String]
6
6
  def initialize location, name
7
- @name = name
8
- @location = location
7
+ super(location: location, name: name)
8
+ # @name = name
9
+ # @location = location
9
10
  end
10
11
 
11
12
  def namespace
@@ -28,14 +29,18 @@ module Solargraph
28
29
  ''
29
30
  end
30
31
 
31
- def return_complex_type
32
- @return_complex_type ||= Solargraph::ComplexType::SYMBOL
32
+ def return_type
33
+ @return_type ||= Solargraph::ComplexType::SYMBOL
33
34
  end
34
35
 
35
36
  def directives
36
37
  []
37
38
  end
38
39
 
40
+ def visibility
41
+ :public
42
+ end
43
+
39
44
  def deprecated?
40
45
  false
41
46
  end
@@ -4,11 +4,20 @@ module Solargraph
4
4
  class Constant < Pin::Constant
5
5
  include YardMixin
6
6
 
7
- def initialize code_object, location
8
- super(location, code_object.namespace.to_s, code_object.name.to_s, comments_from(code_object), nil, nil, nil, code_object.visibility)
7
+ def initialize code_object, location, closure = nil
8
+ @code_object = code_object
9
+ closure ||= Solargraph::Pin::Namespace.new(
10
+ name: code_object.namespace.to_s
11
+ )
12
+ super(
13
+ location: location,
14
+ closure: closure,
15
+ name: code_object.name.to_s,
16
+ comments: comments_from(code_object),
17
+ visibility: code_object.visibility
18
+ )
9
19
  end
10
20
  end
11
21
  end
12
22
  end
13
23
  end
14
- map = Solargraph::ApiMap.new
@@ -4,15 +4,27 @@ module Solargraph
4
4
  class Method < Pin::Method
5
5
  include YardMixin
6
6
 
7
- def initialize code_object, location, name = nil, scope = nil, visibility = nil
7
+ def initialize code_object, location, name = nil, scope = nil, visibility = nil, closure = nil
8
+ @code_object = code_object
8
9
  comments = (code_object.docstring ? code_object.docstring.all : nil)
9
- super(location, code_object.namespace.to_s, name || code_object.name.to_s, comments, scope || code_object.scope, visibility || code_object.visibility, get_parameters(code_object), nil)
10
+ closure ||= Solargraph::Pin::Namespace.new(
11
+ name: code_object.namespace.to_s
12
+ )
13
+ super(
14
+ location: location,
15
+ closure: closure,
16
+ name: name || code_object.name.to_s,
17
+ comments: comments,
18
+ scope: scope || code_object.scope,
19
+ visibility: visibility || code_object.visibility,
20
+ args: get_parameters(code_object)
21
+ )
10
22
  end
11
23
 
12
- def return_complex_type
13
- @return_complex_type ||= Solargraph::ComplexType.try_parse(Solargraph::CoreFills::CUSTOM_RETURN_TYPES[path]) if Solargraph::CoreFills::CUSTOM_RETURN_TYPES.has_key?(path)
14
- super
15
- end
24
+ # def return_type
25
+ # @return_type ||= Solargraph::ComplexType.try_parse(Solargraph::CoreFills::CUSTOM_RETURN_TYPES[path]) if Solargraph::CoreFills::CUSTOM_RETURN_TYPES.has_key?(path)
26
+ # super
27
+ # end
16
28
 
17
29
  private
18
30
 
@@ -5,7 +5,19 @@ module Solargraph
5
5
  include YardMixin
6
6
 
7
7
  def initialize code_object, location
8
- super(location, code_object.namespace.to_s, code_object.name.to_s, comments_from(code_object), namespace_type(code_object), code_object.visibility)
8
+ @code_object = code_object
9
+ closure = Solargraph::Pin::Namespace.new(
10
+ name: code_object.namespace.to_s,
11
+ closure: Pin::ROOT_PIN
12
+ )
13
+ super(
14
+ location: location,
15
+ name: code_object.name.to_s,
16
+ comments: comments_from(code_object),
17
+ type: namespace_type(code_object),
18
+ visibility: code_object.visibility,
19
+ closure: closure
20
+ )
9
21
  end
10
22
 
11
23
  private
@@ -29,7 +29,7 @@ module Solargraph
29
29
  end
30
30
 
31
31
  def inspect
32
- "<Position #{line}, #{character}>"
32
+ "#<#{self.class} #{line}, #{character}>"
33
33
  end
34
34
 
35
35
  # Get a numeric offset for the specified text and position.
@@ -79,5 +79,9 @@ module Solargraph
79
79
  return false unless other.is_a?(Range)
80
80
  start == other.start && ending == other.ending
81
81
  end
82
+
83
+ def inspect
84
+ "#<#{self.class} #{start.inspect} to #{ending.inspect}>"
85
+ end
82
86
  end
83
87
  end
@@ -1,9 +1,6 @@
1
1
  require 'thor'
2
- require 'json'
3
- require 'fileutils'
4
- require 'rubygems/package'
5
- require 'zlib'
6
2
  require 'backport'
3
+ require 'benchmark'
7
4
 
8
5
  module Solargraph
9
6
  class Shell < Thor
@@ -77,6 +74,10 @@ module Solargraph
77
74
  ver = version || Solargraph::YardMap::CoreDocs.best_download
78
75
  puts "Downloading docs for #{ver}..."
79
76
  Solargraph::YardMap::CoreDocs.download ver
77
+ rescue ArgumentError => e
78
+ STDERR.puts "ERROR: #{e.message}"
79
+ STDERR.puts "Run `solargraph available-cores` for a list."
80
+ exit 1
80
81
  end
81
82
 
82
83
  desc 'list-cores', 'List the local documentation versions'
@@ -98,5 +99,83 @@ module Solargraph
98
99
  def reporters
99
100
  puts Solargraph::Diagnostics.reporters
100
101
  end
102
+
103
+ desc 'typecheck [FILE]', 'Run the type checker'
104
+ long_desc %(
105
+ Perform type checking on one or more files is a workspace.
106
+ A normal check reports problems with type definitions in method
107
+ parameters and return values. A strict check performs static analysis of
108
+ code to validate return types and arguments in method calls.
109
+ )
110
+ option :strict, type: :boolean, aliases: :s, desc: 'Use strict typing', default: false
111
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
112
+ def typecheck *files
113
+ directory = File.realpath(options[:directory])
114
+ api_map = Solargraph::ApiMap.load(directory)
115
+ if files.empty?
116
+ files = api_map.source_maps.map(&:filename)
117
+ else
118
+ files.map! { |file| File.realpath(file) }
119
+ end
120
+ probcount = 0
121
+ filecount = 0
122
+ files.each do |file|
123
+ checker = TypeChecker.new(file, api_map: api_map)
124
+ problems = checker.param_type_problems + checker.return_type_problems
125
+ problems.concat checker.strict_type_problems if options[:strict]
126
+ next if problems.empty?
127
+ problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
128
+ puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
129
+ filecount += 1
130
+ probcount += problems.length
131
+ end
132
+ puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
133
+ end
134
+
135
+ desc 'scan', 'Test the workspace for problems'
136
+ long_desc %(
137
+ A scan loads the entire workspace to make sure that the ASTs and
138
+ maps do not raise errors during analysis. It does not perform any type
139
+ checking or validation; it only confirms that the analysis itself is
140
+ error-free.
141
+ )
142
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
143
+ option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
144
+ def scan
145
+ directory = File.realpath(options[:directory])
146
+ api_map = nil
147
+ time = Benchmark.measure {
148
+ api_map = Solargraph::ApiMap.load(directory)
149
+ api_map.pins.each do |pin|
150
+ begin
151
+ puts pin_description(pin) if options[:verbose]
152
+ pin.typify api_map
153
+ pin.probe api_map
154
+ rescue Exception => e
155
+ STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
156
+ STDERR.puts "[#{e.class}]: #{e.message}"
157
+ STDERR.puts e.backtrace.join("\n")
158
+ exit 1
159
+ end
160
+ end
161
+ }
162
+ puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
163
+ end
164
+
165
+ private
166
+
167
+ # @param pin [Solargraph::Pin::Base]
168
+ # @return [String]
169
+ def pin_description pin
170
+ if pin.path.nil? || pin.path.empty?
171
+ if pin.closure
172
+ "#{pin.closure.path} | #{pin.name}"
173
+ else
174
+ "#{pin.context.namespace} | #{pin.name}"
175
+ end
176
+ else
177
+ pin.path
178
+ end
179
+ end
101
180
  end
102
181
  end
@@ -56,8 +56,8 @@ module Solargraph
56
56
  @comments = []
57
57
  @parsed = false
58
58
  rescue Exception => e
59
- STDERR.puts e.message
60
- STDERR.puts e.backtrace
59
+ Solargraph.logger.warn "[#{e.class}] #{e.message}"
60
+ Solargraph.logger.warn e.backtrace.join("\n")
61
61
  raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
62
62
  ensure
63
63
  @code.freeze
@@ -191,8 +191,22 @@ module Solargraph
191
191
  # @param position [Position]
192
192
  # @return [Boolean]
193
193
  def string_at? position
194
- string_ranges.each do |range|
195
- return true if range.include?(position)
194
+ return false if Position.to_offset(code, position) >= code.length
195
+ string_nodes.each do |node|
196
+ range = Range.from_node(node)
197
+ next if range.ending.line < position.line
198
+ break if range.ending.line > position.line
199
+ return true if node.type == :str && range.include?(position) && range.start != position
200
+ if node.type == :dstr
201
+ inner = node_at(position.line, position.column)
202
+ next if inner.nil?
203
+ inner_range = Range.from_node(inner)
204
+ next unless range.include?(inner_range.ending)
205
+ return true if inner.type == :str
206
+ inner_code = at(Solargraph::Range.new(inner_range.start, position))
207
+ return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
208
+ (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
209
+ end
196
210
  break if range.ending.line > position.line
197
211
  end
198
212
  false
@@ -290,7 +304,7 @@ module Solargraph
290
304
  @associated_comments ||= begin
291
305
  result = {}
292
306
  Parser::Source::Comment.associate_locations(node, comments).each_pair do |loc, all|
293
- block = all #.select{ |l| l.document? || code.lines[l.loc.line].strip.start_with?('#')}
307
+ block = all.select { |l| l.loc.line < loc.line }
294
308
  next if block.empty?
295
309
  result[loc.line] ||= []
296
310
  result[loc.line].concat block
@@ -351,9 +365,9 @@ module Solargraph
351
365
  @stringified_comments ||= {}
352
366
  end
353
367
 
354
- # @return [Array<Range>]
355
- def string_ranges
356
- @string_ranges ||= string_ranges_in(@node)
368
+ # @return [Array<Parser::AST::Node>]
369
+ def string_nodes
370
+ @string_nodes ||= string_nodes_in(@node)
357
371
  end
358
372
 
359
373
  # @return [Array<Range>]
@@ -393,18 +407,24 @@ module Solargraph
393
407
  result
394
408
  end
395
409
 
396
- def string_ranges_in n
410
+ # @param n [Parser::AST::Node]
411
+ # @return [Array<Parser::AST::Node>]
412
+ def string_nodes_in n
397
413
  result = []
398
414
  if n.is_a?(Parser::AST::Node)
399
- if n.type == :str
400
- result.push Range.from_node(n)
415
+ if n.type == :str || n.type == :dstr
416
+ result.push n
401
417
  else
402
- n.children.each{ |c| result.concat string_ranges_in(c) }
418
+ n.children.each{ |c| result.concat string_nodes_in(c) }
403
419
  end
404
420
  end
405
421
  result
406
422
  end
407
423
 
424
+ # @param node [Parser::AST::Node]
425
+ # @param position [Position]
426
+ # @param stack [Array<Parser::AST::Node>]
427
+ # @return [void]
408
428
  def inner_tree_at node, position, stack
409
429
  return if node.nil?
410
430
  here = Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
@@ -17,9 +17,7 @@ module Solargraph
17
17
  autoload :Literal, 'solargraph/source/chain/literal'
18
18
  autoload :Head, 'solargraph/source/chain/head'
19
19
 
20
- # Chain#infer uses the inference stack to avoid recursing into itself.
21
- # See Chain#active_signature for more information.
22
- @@inference_stack = []
20
+ @@inference_depth = 0
23
21
 
24
22
  UNDEFINED_CALL = Chain::Call.new('<undefined>')
25
23
  UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
@@ -43,16 +41,18 @@ module Solargraph
43
41
  # @param locals [Array<Pin::Base>]
44
42
  # @return [Array<Pin::Base>]
45
43
  def define api_map, name_pin, locals
44
+ rebind_block name_pin, api_map, locals
46
45
  return [] if undefined?
47
46
  working_pin = name_pin
48
47
  links[0..-2].each do |link|
49
48
  pins = link.resolve(api_map, working_pin, locals)
50
49
  # Locals are only used when resolving the first link
51
50
  locals = []
52
- type = infer_first_defined(pins, api_map)
51
+ type = infer_first_defined(pins, working_pin, api_map)
53
52
  return [] if type.undefined?
54
53
  working_pin = Pin::ProxyType.anonymous(type)
55
54
  end
55
+ links.last.last_context = working_pin
56
56
  links.last.resolve(api_map, working_pin, locals)
57
57
  end
58
58
 
@@ -61,16 +61,10 @@ module Solargraph
61
61
  # @param locals [Array<Pin::Base>]
62
62
  # @return [ComplexType]
63
63
  def infer api_map, name_pin, locals
64
- return ComplexType::UNDEFINED if undefined? || @@inference_stack.include?(active_signature(name_pin))
65
- @@inference_stack.push active_signature(name_pin)
64
+ rebind_block name_pin, api_map, locals
66
65
  type = ComplexType::UNDEFINED
67
66
  pins = define(api_map, name_pin, locals)
68
- pins.each do |pin|
69
- type = pin.typify(api_map)
70
- break unless type.undefined?
71
- end
72
- type = pins.first.probe(api_map) unless type.defined? || pins.empty?
73
- @@inference_stack.pop
67
+ type = infer_first_defined(pins, links.last.last_context, api_map)
74
68
  type
75
69
  end
76
70
 
@@ -91,29 +85,55 @@ module Solargraph
91
85
 
92
86
  private
93
87
 
94
- # Get a signature for this chain that includes the current context
95
- # where it's being analyzed. Chain#infer uses this value to detect
96
- # recursive inference into the same chain, e.g., when two variables
97
- # reference each other in their assignments.
98
- #
99
- # @param pin [Pin::Base] The named pin context
100
- # @return [String]
101
- def active_signature(pin)
102
- "#{pin.path}|#{links.map(&:word).join('.')}"
103
- end
104
-
105
88
  # @param pins [Array<Pin::Base>]
106
89
  # @param api_map [ApiMap]
107
90
  # @return [ComplexType]
108
- def infer_first_defined pins, api_map
91
+ def infer_first_defined pins, context, api_map
109
92
  type = ComplexType::UNDEFINED
93
+ return type if @@inference_depth >= 3
94
+ @@inference_depth += 1
110
95
  pins.each do |pin|
111
- # type = pin.infer(api_map)
112
96
  type = pin.typify(api_map)
113
- break unless type.undefined?
97
+ break if type.defined?
98
+ end
99
+ if type.undefined?
100
+ pins.each do |pin|
101
+ type = pin.probe(api_map)
102
+ break if type.defined?
103
+ end
104
+ end
105
+ @@inference_depth -= 1
106
+ return type if context.nil? || context.return_type.undefined?
107
+ type.self_to(context.return_type.namespace)
108
+ end
109
+
110
+ def skippable_block_receivers api_map
111
+ @@skippable_block_receivers ||= (
112
+ api_map.get_methods('Array', deep: false).map(&:name) +
113
+ api_map.get_methods('Enumerable', deep: false).map(&:name) +
114
+ api_map.get_methods('Hash', deep: false).map(&:name) +
115
+ ['new']
116
+ ).to_set
117
+ end
118
+
119
+ def rebind_block pin, api_map, locals
120
+ return unless pin.is_a?(Pin::Block) && pin.receiver && !pin.rebound?
121
+ # This first rebind just sets the block pin's rebound state
122
+ pin.rebind ComplexType::UNDEFINED
123
+ chain = Solargraph::Source::NodeChainer.chain(pin.receiver, pin.location.filename)
124
+ return if skippable_block_receivers(api_map).include?(chain.links.last.word)
125
+ if ['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec'].include?(chain.links.last.word)
126
+ type = chain.base.infer(api_map, pin, locals)
127
+ pin.rebind type
128
+ else
129
+ receiver_pin = chain.define(api_map, pin, locals).first
130
+ return if receiver_pin.nil? || receiver_pin.docstring.nil?
131
+ ys = receiver_pin.docstring.tag(:yieldself)
132
+ unless ys.nil? || ys.types.nil? || ys.types.empty?
133
+ ysct = ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
134
+ pin.rebind ysct
135
+ end
114
136
  end
115
- type = pins.first.probe(api_map) unless type.defined? || pins.empty?
116
- type
117
137
  end
118
138
  end
119
139
  end