solargraph 0.32.5 → 0.33.0

Sign up to get free protection for your applications and to get access to all the features.
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