solargraph 0.58.3 → 0.59.0.dev.1

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +41 -38
  5. data/.github/workflows/rspec.yml +45 -13
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.gitignore +0 -1
  8. data/.rubocop_todo.yml +27 -49
  9. data/CHANGELOG.md +1 -10
  10. data/README.md +3 -3
  11. data/Rakefile +1 -0
  12. data/lib/solargraph/api_map/cache.rb +3 -3
  13. data/lib/solargraph/api_map/constants.rb +13 -3
  14. data/lib/solargraph/api_map/index.rb +22 -11
  15. data/lib/solargraph/api_map/source_to_yard.rb +13 -1
  16. data/lib/solargraph/api_map/store.rb +11 -8
  17. data/lib/solargraph/api_map.rb +105 -50
  18. data/lib/solargraph/bench.rb +45 -45
  19. data/lib/solargraph/complex_type/conformance.rb +176 -0
  20. data/lib/solargraph/complex_type/type_methods.rb +16 -2
  21. data/lib/solargraph/complex_type/unique_type.rb +170 -20
  22. data/lib/solargraph/complex_type.rb +119 -14
  23. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  24. data/lib/solargraph/convention/data_definition/data_definition_node.rb +3 -1
  25. data/lib/solargraph/convention/data_definition.rb +4 -1
  26. data/lib/solargraph/convention/gemfile.rb +15 -15
  27. data/lib/solargraph/convention/gemspec.rb +23 -23
  28. data/lib/solargraph/convention/rakefile.rb +17 -17
  29. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -0
  30. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +1 -0
  31. data/lib/solargraph/convention/struct_definition.rb +5 -1
  32. data/lib/solargraph/convention.rb +78 -78
  33. data/lib/solargraph/converters/dd.rb +17 -17
  34. data/lib/solargraph/converters/dl.rb +15 -15
  35. data/lib/solargraph/converters/dt.rb +15 -15
  36. data/lib/solargraph/converters/misc.rb +1 -1
  37. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  38. data/lib/solargraph/diagnostics/rubocop.rb +1 -0
  39. data/lib/solargraph/diagnostics/rubocop_helpers.rb +2 -0
  40. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  41. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  42. data/lib/solargraph/doc_map.rb +134 -373
  43. data/lib/solargraph/equality.rb +1 -1
  44. data/lib/solargraph/gem_pins.rb +14 -15
  45. data/lib/solargraph/language_server/error_codes.rb +20 -20
  46. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  47. data/lib/solargraph/language_server/host/dispatch.rb +1 -0
  48. data/lib/solargraph/language_server/host/message_worker.rb +2 -1
  49. data/lib/solargraph/language_server/host/sources.rb +1 -0
  50. data/lib/solargraph/language_server/host.rb +6 -1
  51. data/lib/solargraph/language_server/message/base.rb +97 -97
  52. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  53. data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -60
  54. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -7
  55. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  56. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  57. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -19
  58. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  59. data/lib/solargraph/language_server/message/initialize.rb +191 -191
  60. data/lib/solargraph/language_server/message/text_document/completion.rb +2 -0
  61. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  62. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  63. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -0
  64. data/lib/solargraph/language_server/message/text_document/formatting.rb +2 -0
  65. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  66. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  67. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  68. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  69. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -0
  70. data/lib/solargraph/language_server/message/text_document/type_definition.rb +2 -0
  71. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -35
  72. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -40
  73. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +26 -26
  74. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -0
  75. data/lib/solargraph/language_server/message.rb +94 -94
  76. data/lib/solargraph/language_server/request.rb +27 -27
  77. data/lib/solargraph/language_server/transport/data_reader.rb +74 -74
  78. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  79. data/lib/solargraph/library.rb +59 -13
  80. data/lib/solargraph/location.rb +9 -4
  81. data/lib/solargraph/logging.rb +21 -1
  82. data/lib/solargraph/page.rb +92 -92
  83. data/lib/solargraph/parser/comment_ripper.rb +7 -0
  84. data/lib/solargraph/parser/flow_sensitive_typing.rb +330 -102
  85. data/lib/solargraph/parser/node_processor/base.rb +32 -2
  86. data/lib/solargraph/parser/node_processor.rb +7 -6
  87. data/lib/solargraph/parser/parser_gem/class_methods.rb +28 -10
  88. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  89. data/lib/solargraph/parser/parser_gem/node_chainer.rb +31 -6
  90. data/lib/solargraph/parser/parser_gem/node_methods.rb +27 -7
  91. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  92. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +2 -0
  93. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  94. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -11
  95. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +7 -0
  96. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +37 -37
  97. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  98. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +3 -2
  99. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  100. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  101. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +2 -2
  102. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  103. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  104. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  105. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +1 -0
  106. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +12 -7
  107. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  108. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  109. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +5 -1
  110. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  111. data/lib/solargraph/parser/parser_gem.rb +12 -12
  112. data/lib/solargraph/parser/region.rb +9 -3
  113. data/lib/solargraph/parser/snippet.rb +1 -1
  114. data/lib/solargraph/parser.rb +23 -23
  115. data/lib/solargraph/pin/base.rb +53 -21
  116. data/lib/solargraph/pin/base_variable.rb +312 -20
  117. data/lib/solargraph/pin/block.rb +26 -4
  118. data/lib/solargraph/pin/breakable.rb +5 -1
  119. data/lib/solargraph/pin/callable.rb +50 -3
  120. data/lib/solargraph/pin/closure.rb +2 -6
  121. data/lib/solargraph/pin/common.rb +20 -5
  122. data/lib/solargraph/pin/compound_statement.rb +55 -0
  123. data/lib/solargraph/pin/constant.rb +45 -45
  124. data/lib/solargraph/pin/conversions.rb +2 -1
  125. data/lib/solargraph/pin/delegated_method.rb +15 -4
  126. data/lib/solargraph/pin/documenting.rb +1 -0
  127. data/lib/solargraph/pin/instance_variable.rb +5 -1
  128. data/lib/solargraph/pin/keyword.rb +0 -4
  129. data/lib/solargraph/pin/local_variable.rb +13 -57
  130. data/lib/solargraph/pin/method.rb +90 -42
  131. data/lib/solargraph/pin/method_alias.rb +8 -0
  132. data/lib/solargraph/pin/namespace.rb +7 -1
  133. data/lib/solargraph/pin/parameter.rb +76 -13
  134. data/lib/solargraph/pin/proxy_type.rb +2 -1
  135. data/lib/solargraph/pin/reference/override.rb +1 -1
  136. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  137. data/lib/solargraph/pin/reference.rb +2 -0
  138. data/lib/solargraph/pin/search.rb +1 -0
  139. data/lib/solargraph/pin/signature.rb +8 -0
  140. data/lib/solargraph/pin/symbol.rb +1 -1
  141. data/lib/solargraph/pin/until.rb +1 -1
  142. data/lib/solargraph/pin/while.rb +1 -1
  143. data/lib/solargraph/pin.rb +2 -0
  144. data/lib/solargraph/pin_cache.rb +477 -57
  145. data/lib/solargraph/position.rb +12 -26
  146. data/lib/solargraph/range.rb +6 -6
  147. data/lib/solargraph/rbs_map/conversions.rb +33 -10
  148. data/lib/solargraph/rbs_map/core_fills.rb +84 -84
  149. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  150. data/lib/solargraph/rbs_map/stdlib_map.rb +34 -5
  151. data/lib/solargraph/rbs_map.rb +74 -20
  152. data/lib/solargraph/server_methods.rb +16 -16
  153. data/lib/solargraph/shell.rb +73 -39
  154. data/lib/solargraph/source/chain/array.rb +37 -37
  155. data/lib/solargraph/source/chain/call.rb +52 -17
  156. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  157. data/lib/solargraph/source/chain/constant.rb +2 -0
  158. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  159. data/lib/solargraph/source/chain/hash.rb +1 -0
  160. data/lib/solargraph/source/chain/if.rb +1 -0
  161. data/lib/solargraph/source/chain/instance_variable.rb +22 -1
  162. data/lib/solargraph/source/chain/link.rb +109 -109
  163. data/lib/solargraph/source/chain/literal.rb +5 -0
  164. data/lib/solargraph/source/chain/or.rb +9 -1
  165. data/lib/solargraph/source/chain/q_call.rb +11 -11
  166. data/lib/solargraph/source/chain/variable.rb +13 -13
  167. data/lib/solargraph/source/chain/z_super.rb +30 -30
  168. data/lib/solargraph/source/chain.rb +25 -22
  169. data/lib/solargraph/source/change.rb +9 -2
  170. data/lib/solargraph/source/cursor.rb +7 -1
  171. data/lib/solargraph/source/source_chainer.rb +13 -3
  172. data/lib/solargraph/source/updater.rb +4 -0
  173. data/lib/solargraph/source.rb +33 -7
  174. data/lib/solargraph/source_map/clip.rb +13 -2
  175. data/lib/solargraph/source_map/data.rb +4 -1
  176. data/lib/solargraph/source_map/mapper.rb +24 -1
  177. data/lib/solargraph/source_map.rb +14 -6
  178. data/lib/solargraph/type_checker/problem.rb +3 -1
  179. data/lib/solargraph/type_checker/rules.rb +75 -2
  180. data/lib/solargraph/type_checker.rb +111 -30
  181. data/lib/solargraph/version.rb +1 -1
  182. data/lib/solargraph/workspace/config.rb +3 -1
  183. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  184. data/lib/solargraph/workspace/require_paths.rb +1 -0
  185. data/lib/solargraph/workspace.rb +158 -16
  186. data/lib/solargraph/yard_map/helpers.rb +2 -1
  187. data/lib/solargraph/yard_map/mapper/to_method.rb +5 -1
  188. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  189. data/lib/solargraph/yard_map/mapper.rb +5 -0
  190. data/lib/solargraph/yard_tags.rb +20 -20
  191. data/lib/solargraph/yardoc.rb +33 -23
  192. data/lib/solargraph.rb +24 -3
  193. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  194. data/rbs/fills/tuple/tuple.rbs +28 -0
  195. data/rbs/shims/ast/0/node.rbs +1 -1
  196. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  197. data/solargraph.gemspec +2 -1
  198. metadata +12 -7
  199. data/lib/solargraph/type_checker/checks.rb +0 -124
  200. data/lib/solargraph/type_checker/param_def.rb +0 -37
  201. data/lib/solargraph/yard_map/to_method.rb +0 -89
@@ -41,7 +41,7 @@ module Solargraph
41
41
  # @param type_location [Solargraph::Location, nil]
42
42
  # @param closure [Solargraph::Pin::Closure, nil]
43
43
  # @param name [String]
44
- # @param comments [String]
44
+ # @param comments [String, nil]
45
45
  # @param source [Symbol, nil]
46
46
  # @param docstring [YARD::Docstring, nil]
47
47
  # @param directives [::Array<YARD::Tags::Directive>, nil]
@@ -57,6 +57,9 @@ module Solargraph
57
57
  @docstring = docstring
58
58
  @directives = directives
59
59
  @combine_priority = combine_priority
60
+ # @type [ComplexType, ComplexType::UniqueType, nil]
61
+ @binder = nil
62
+
60
63
 
61
64
  assert_source_provided
62
65
  assert_location_provided
@@ -72,7 +75,6 @@ module Solargraph
72
75
  # @return [Pin::Closure, nil]
73
76
  def closure
74
77
  Solargraph.assert_or_log(:closure, "Closure not set on #{self.class} #{name.inspect} from #{source.inspect}") unless @closure
75
- # @type [Pin::Closure, nil]
76
78
  @closure
77
79
  end
78
80
 
@@ -81,7 +83,6 @@ module Solargraph
81
83
  #
82
84
  # @return [self]
83
85
  def combine_with(other, attrs={})
84
- raise "tried to combine #{other.class} with #{self.class}" unless other.class == self.class
85
86
  priority_choice = choose_priority(other)
86
87
  return priority_choice unless priority_choice.nil?
87
88
 
@@ -92,7 +93,7 @@ module Solargraph
92
93
  location: location,
93
94
  type_location: type_location,
94
95
  name: combined_name,
95
- closure: choose_pin_attr_with_same_name(other, :closure),
96
+ closure: combine_closure(other),
96
97
  comments: choose_longer(other, :comments),
97
98
  source: :combined,
98
99
  docstring: choose(other, :docstring),
@@ -140,14 +141,22 @@ module Solargraph
140
141
  end
141
142
 
142
143
  # @param other [self]
144
+ #
143
145
  # @return [::Array<YARD::Tags::Directive>, nil]
144
146
  def combine_directives(other)
145
147
  return self.directives if other.directives.empty?
146
148
  return other.directives if directives.empty?
147
- [directives + other.directives].uniq
149
+ (directives + other.directives).uniq
150
+ end
151
+
152
+ # @param other [self]
153
+ # @return [Pin::Closure, nil]
154
+ def combine_closure(other)
155
+ choose_pin_attr_with_same_name(other, :closure)
148
156
  end
149
157
 
150
158
  # @param other [self]
159
+ # @sg-ignore @type should override probed type
151
160
  # @return [String]
152
161
  def combine_name(other)
153
162
  if needs_consistent_name? || other.needs_consistent_name?
@@ -170,6 +179,9 @@ module Solargraph
170
179
  # Same with @directives, @macros, @maybe_directives, which
171
180
  # regenerate docstring
172
181
  @deprecated = nil
182
+ @context = nil
183
+ @binder = nil
184
+ @path = nil
173
185
  reset_conversions
174
186
  end
175
187
 
@@ -189,6 +201,10 @@ module Solargraph
189
201
  other.return_type
190
202
  elsif other.return_type.undefined?
191
203
  return_type
204
+ elsif return_type.erased_version_of?(other.return_type)
205
+ other.return_type
206
+ elsif other.return_type.erased_version_of?(return_type)
207
+ return_type
192
208
  elsif dodgy_return_type_source? && !other.dodgy_return_type_source?
193
209
  other.return_type
194
210
  elsif other.dodgy_return_type_source? && !dodgy_return_type_source?
@@ -203,9 +219,12 @@ module Solargraph
203
219
  end
204
220
  end
205
221
 
222
+ # @sg-ignore need boolish support for ? methods
206
223
  def dodgy_return_type_source?
207
224
  # uses a lot of 'Object' instead of 'self'
208
- location&.filename&.include?('core_ext/object/')
225
+ location&.filename&.include?('core_ext/object/') ||
226
+ # ditto
227
+ location&.filename&.include?('stdlib/date/0/date.rbs')
209
228
  end
210
229
 
211
230
  # when choices are arbitrary, make sure the choice is consistent
@@ -213,11 +232,13 @@ module Solargraph
213
232
  # @param other [Pin::Base]
214
233
  # @param attr [::Symbol]
215
234
  #
216
- # @return [Object, nil]
235
+ # @sg-ignore
236
+ # @return [undefined, nil]
217
237
  def choose(other, attr)
218
238
  results = [self, other].map(&attr).compact
219
239
  # true and false are different classes and can't be sorted
220
240
  return true if results.any? { |r| r == true || r == false }
241
+ return results.first if results.any? { |r| r.is_a? AST::Node }
221
242
  results.min
222
243
  rescue
223
244
  STDERR.puts("Problem handling #{attr} for \n#{self.inspect}\n and \n#{other.inspect}\n\n#{self.send(attr).inspect} vs #{other.send(attr).inspect}")
@@ -250,6 +271,7 @@ module Solargraph
250
271
  end
251
272
  end
252
273
 
274
+ # @sg-ignore need boolish support for ? methods
253
275
  def rbs_location?
254
276
  type_location&.rbs?
255
277
  end
@@ -309,7 +331,11 @@ module Solargraph
309
331
  # @sg-ignore
310
332
  # @return [undefined]
311
333
  def assert_same(other, attr)
312
- return false if other.nil?
334
+ if other.nil?
335
+ Solargraph.assert_or_log("combine_with_#{attr}_nil".to_sym,
336
+ "Other was passed in nil in assert_same on #{self}")
337
+ return send(attr)
338
+ end
313
339
  val1 = send(attr)
314
340
  val2 = other.send(attr)
315
341
  return val1 if val1 == val2
@@ -358,8 +384,11 @@ module Solargraph
358
384
  [
359
385
  # maximize number of gates, as types in other combined pins may
360
386
  # depend on those gates
387
+
388
+ # @sg-ignore Need better handling of #compact
361
389
  closure.gates.length,
362
390
  # use basename so that results don't vary system to system
391
+ # @sg-ignore Need better handling of #compact
363
392
  File.basename(closure.best_location.to_s)
364
393
  ]
365
394
  end
@@ -376,7 +405,7 @@ module Solargraph
376
405
  end
377
406
 
378
407
  # @param generics_to_resolve [Enumerable<String>]
379
- # @param return_type_context [ComplexType, nil]
408
+ # @param return_type_context [ComplexType, ComplexType::UniqueType, nil]
380
409
  # @param context [ComplexType]
381
410
  # @param resolved_generic_values [Hash{String => ComplexType}]
382
411
  # @return [self]
@@ -418,6 +447,7 @@ module Solargraph
418
447
  # @return [String, nil]
419
448
  def filename
420
449
  return nil if location.nil?
450
+ # @sg-ignore flow sensitive typing needs to handle attrs
421
451
  location.filename
422
452
  end
423
453
 
@@ -453,11 +483,16 @@ module Solargraph
453
483
  # @return [Boolean]
454
484
  def nearly? other
455
485
  self.class == other.class &&
486
+ # @sg-ignore Translate to something flow sensitive typing understands
456
487
  name == other.name &&
488
+ # @sg-ignore flow sensitive typing needs to handle attrs
457
489
  (closure == other.closure || (closure && closure.nearly?(other.closure))) &&
490
+ # @sg-ignore Translate to something flow sensitive typing understands
458
491
  (comments == other.comments ||
492
+ # @sg-ignore Translate to something flow sensitive typing understands
459
493
  (((maybe_directives? == false && other.maybe_directives? == false) || compare_directives(directives, other.directives)) &&
460
- compare_docstring_tags(docstring, other.docstring))
494
+ # @sg-ignore Translate to something flow sensitive typing understands
495
+ compare_docstring_tags(docstring, other.docstring))
461
496
  )
462
497
  end
463
498
 
@@ -484,6 +519,7 @@ module Solargraph
484
519
  @docstring ||= Solargraph::Source.parse_docstring('').to_docstring
485
520
  end
486
521
 
522
+ # @sg-ignore parse_comments will always set @directives
487
523
  # @return [::Array<YARD::Tags::Directive>]
488
524
  def directives
489
525
  parse_comments unless @directives
@@ -520,7 +556,7 @@ module Solargraph
520
556
  # provided ApiMap.
521
557
  #
522
558
  # @param api_map [ApiMap]
523
- # @return [ComplexType]
559
+ # @return [ComplexType, ComplexType::UniqueType]
524
560
  def typify api_map
525
561
  return_type.qualify(api_map, *(closure&.gates || ['']))
526
562
  end
@@ -528,16 +564,16 @@ module Solargraph
528
564
  # Infer the pin's return type via static code analysis.
529
565
  #
530
566
  # @param api_map [ApiMap]
531
- # @return [ComplexType]
567
+ # @return [ComplexType, ComplexType::UniqueType]
532
568
  def probe api_map
533
569
  typify api_map
534
570
  end
535
571
 
536
572
  # @deprecated Use #typify and/or #probe instead
537
573
  # @param api_map [ApiMap]
538
- # @return [ComplexType]
574
+ # @return [ComplexType, ComplexType::UniqueType]
539
575
  def infer api_map
540
- Solargraph::Logging.logger.warn "WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead."
576
+ Solargraph.assert_or_log(:pin_infer, 'WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead.')
541
577
  type = typify(api_map)
542
578
  return type unless type.undefined?
543
579
  probe api_map
@@ -568,7 +604,7 @@ module Solargraph
568
604
  # the return type and the #proxied? setting, the proxy should be a clone
569
605
  # of the original.
570
606
  #
571
- # @param return_type [ComplexType]
607
+ # @param return_type [ComplexType, ComplexType::UniqueType, nil]
572
608
  # @return [self]
573
609
  def proxy return_type
574
610
  result = dup
@@ -618,7 +654,7 @@ module Solargraph
618
654
 
619
655
  # @return [String]
620
656
  def inner_desc
621
- closure_info = closure&.desc
657
+ closure_info = closure&.name.inspect
622
658
  binder_info = binder&.desc
623
659
  "name=#{name.inspect} return_type=#{type_desc}, context=#{context.rooted_tags}, closure=#{closure_info}, binder=#{binder_info}"
624
660
  end
@@ -646,10 +682,6 @@ module Solargraph
646
682
  end
647
683
  end
648
684
 
649
- # @return [void]
650
- def reset_generated!
651
- end
652
-
653
685
  protected
654
686
 
655
687
  # @return [Boolean]
@@ -658,7 +690,7 @@ module Solargraph
658
690
  # @return [Boolean]
659
691
  attr_writer :proxied
660
692
 
661
- # @return [ComplexType]
693
+ # @return [ComplexType, ComplexType::UniqueType, nil]
662
694
  attr_writer :return_type
663
695
 
664
696
  attr_writer :docstring
@@ -6,31 +6,139 @@ module Solargraph
6
6
  # include Solargraph::Source::NodeMethods
7
7
  include Solargraph::Parser::NodeMethods
8
8
 
9
- # @return [Parser::AST::Node, nil]
10
- attr_reader :assignment
9
+ # @return [Array<Parser::AST::Node>]
10
+ attr_reader :assignments
11
11
 
12
12
  attr_accessor :mass_assignment
13
13
 
14
+ # @return [Range, nil]
15
+ attr_reader :presence
16
+
14
17
  # @param return_type [ComplexType, nil]
18
+ # @param assignment [Parser::AST::Node, nil] First assignment
19
+ # that was made to this variable
20
+ # @param assignments [Array<Parser::AST::Node>] Possible
21
+ # assignments that may have been made to this variable
15
22
  # @param mass_assignment [::Array(Parser::AST::Node, Integer), nil]
16
- # @param assignment [Parser::AST::Node, nil]
17
- def initialize assignment: nil, return_type: nil, mass_assignment: nil, **splat
23
+ # @param assignment [Parser::AST::Node, nil] First assignment
24
+ # that was made to this variable
25
+ # @param assignments [Array<Parser::AST::Node>] Possible
26
+ # assignments that may have been made to this variable
27
+ # @param exclude_return_type [ComplexType, nil] Ensure any
28
+ # return type returned will never include any of these unique
29
+ # types in the unique types of its complex type.
30
+ #
31
+ # Example: If a return type is 'Float | Integer | nil' and the
32
+ # exclude_return_type is 'Integer', the resulting return
33
+ # type will be 'Float | nil' because Integer is excluded.
34
+ # @param intersection_return_type [ComplexType, nil] Ensure each unique
35
+ # return type is compatible with at least one element of this
36
+ # complex type. If a ComplexType used as a return type is an
37
+ # union type - we can return any of these - these are
38
+ # intersection types - everything we return needs to meet at least
39
+ # one of these unique types.
40
+ #
41
+ # Example: If a return type is 'Numeric | nil' and the
42
+ # intersection_return_type is 'Float | nil', the resulting return
43
+ # type will be 'Float | nil' because Float is compatible
44
+ # with Numeric and nil is compatible with nil.
45
+ # @see https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types
46
+ # @see https://en.wikipedia.org/wiki/Intersection_type#TypeScript_example
47
+ # @param presence [Range, nil]
48
+ # @param presence_certain [Boolean]
49
+ def initialize assignment: nil, assignments: [], mass_assignment: nil,
50
+ presence: nil, presence_certain: false, return_type: nil,
51
+ intersection_return_type: nil, exclude_return_type: nil,
52
+ **splat
18
53
  super(**splat)
19
- @assignment = assignment
54
+ @assignments = (assignment.nil? ? [] : [assignment]) + assignments
20
55
  # @type [nil, ::Array(Parser::AST::Node, Integer)]
21
- @mass_assignment = nil
56
+ @mass_assignment = mass_assignment
22
57
  @return_type = return_type
58
+ @intersection_return_type = intersection_return_type
59
+ @exclude_return_type = exclude_return_type
60
+ @presence = presence
61
+ @presence_certain = presence_certain
62
+ end
63
+
64
+ # @param presence [Range]
65
+ # @param exclude_return_type [ComplexType, nil]
66
+ # @param intersection_return_type [ComplexType, nil]
67
+ # @param source [::Symbol]
68
+ #
69
+ # @return [self]
70
+ def downcast presence:, exclude_return_type: nil, intersection_return_type: nil,
71
+ source: self.source
72
+ result = dup
73
+ result.exclude_return_type = exclude_return_type
74
+ result.intersection_return_type = intersection_return_type
75
+ result.source = source
76
+ result.presence = presence
77
+ result.reset_generated!
78
+ result
79
+ end
80
+
81
+ def reset_generated!
82
+ @assignment = nil
83
+ super
23
84
  end
24
85
 
25
86
  def combine_with(other, attrs={})
87
+ new_assignments = combine_assignments(other)
26
88
  new_attrs = attrs.merge({
27
- assignment: assert_same(other, :assignment),
28
- mass_assignment: assert_same(other, :mass_assignment),
89
+ # default values don't exist in RBS parameters; it just
90
+ # tells you if the arg is optional or not. Prefer a
91
+ # provided value if we have one here since we can't rely on
92
+ # it from RBS so we can infer from it and typecheck on it.
93
+ assignment: choose(other, :assignment),
94
+ assignments: new_assignments,
95
+ mass_assignment: combine_mass_assignment(other),
29
96
  return_type: combine_return_type(other),
97
+ intersection_return_type: combine_types(other, :intersection_return_type),
98
+ exclude_return_type: combine_types(other, :exclude_return_type),
99
+ presence: combine_presence(other),
100
+ presence_certain: combine_presence_certain(other)
30
101
  })
31
102
  super(other, new_attrs)
32
103
  end
33
104
 
105
+ # @param other [self]
106
+ #
107
+ # @return [Array(AST::Node, Integer), nil]
108
+ def combine_mass_assignment(other)
109
+ # @todo pick first non-nil arbitrarily - we don't yet support
110
+ # mass assignment merging
111
+ mass_assignment || other.mass_assignment
112
+ end
113
+
114
+ # If a certain pin is being combined with an uncertain pin, we
115
+ # end up with a certain result
116
+ #
117
+ # @param other [self]
118
+ #
119
+ # @return [Boolean]
120
+ def combine_presence_certain(other)
121
+ presence_certain? || other.presence_certain?
122
+ end
123
+
124
+ # @return [Parser::AST::Node, nil]
125
+ def assignment
126
+ @assignment ||= assignments.last
127
+ end
128
+
129
+ # @param other [self]
130
+ #
131
+ # @return [::Array<Parser::AST::Node>]
132
+ def combine_assignments(other)
133
+ (other.assignments + assignments).uniq
134
+ end
135
+
136
+ def inner_desc
137
+ super + ", presence=#{presence.inspect}, assignments=#{assignments}, " \
138
+ "intersection_return_type=#{intersection_return_type&.rooted_tags.inspect}, " \
139
+ "exclude_return_type=#{exclude_return_type&.rooted_tags.inspect}"
140
+ end
141
+
34
142
  def completion_item_kind
35
143
  Solargraph::LanguageServer::CompletionItemKinds::VARIABLE
36
144
  end
@@ -40,10 +148,6 @@ module Solargraph
40
148
  Solargraph::LanguageServer::SymbolKinds::VARIABLE
41
149
  end
42
150
 
43
- def return_type
44
- @return_type ||= generate_complex_type
45
- end
46
-
47
151
  def nil_assignment?
48
152
  # this will always be false - should it be return_type ==
49
153
  # ComplexType::NIL or somesuch?
@@ -67,10 +171,12 @@ module Solargraph
67
171
  rng = Range.from_node(node)
68
172
  next if rng.nil?
69
173
  pos = rng.ending
174
+ # @sg-ignore Need to add nil check here
70
175
  clip = api_map.clip_at(location.filename, pos)
71
176
  # Use the return node for inference. The clip might infer from the
72
177
  # first node in a method call instead of the entire call.
73
178
  chain = Parser.chain(node, nil, nil)
179
+ # @sg-ignore Need to add nil check here
74
180
  result = chain.infer(api_map, closure, clip.locals).self_to_type(closure.context)
75
181
  types.push result unless result.undefined?
76
182
  end
@@ -79,13 +185,15 @@ module Solargraph
79
185
  end
80
186
 
81
187
  # @param api_map [ApiMap]
82
- # @return [ComplexType]
188
+ # @return [ComplexType, ComplexType::UniqueType]
83
189
  def probe api_map
84
- unless @assignment.nil?
85
- types = return_types_from_node(@assignment, api_map)
86
- return ComplexType.new(types.uniq) unless types.empty?
87
- end
190
+ assignment_types = assignments.flat_map { |node| return_types_from_node(node, api_map) }
191
+ type_from_assignment = ComplexType.new(assignment_types.flat_map(&:items).uniq) unless assignment_types.empty?
192
+ return adjust_type api_map, type_from_assignment unless type_from_assignment.nil?
88
193
 
194
+ # @todo should handle merging types from mass assignments as
195
+ # well so that we can do better flow sensitive typing with
196
+ # multiple assignments
89
197
  unless @mass_assignment.nil?
90
198
  mass_node, index = @mass_assignment
91
199
  types = return_types_from_node(mass_node, api_map)
@@ -96,7 +204,10 @@ module Solargraph
96
204
  type.all_params.first
97
205
  end
98
206
  end.compact!
99
- return ComplexType.new(types.uniq) unless types.empty?
207
+
208
+ return ComplexType::UNDEFINED if types.empty?
209
+
210
+ return adjust_type api_map, ComplexType.new(types.uniq).qualify(api_map, *gates)
100
211
  end
101
212
 
102
213
  ComplexType::UNDEFINED
@@ -113,13 +224,194 @@ module Solargraph
113
224
  "#{super} = #{assignment&.type.inspect}"
114
225
  end
115
226
 
227
+ # @return [ComplexType, nil]
228
+ def return_type
229
+ generate_complex_type || @return_type || intersection_return_type || ComplexType::UNDEFINED
230
+ end
231
+
232
+ def typify api_map
233
+ raw_return_type = super
234
+
235
+ adjust_type(api_map, raw_return_type)
236
+ end
237
+
238
+ # @sg-ignore need boolish support for ? methods
239
+ def presence_certain?
240
+ exclude_return_type || intersection_return_type
241
+ end
242
+
243
+ # @param other_loc [Location]
244
+ # @sg-ignore flow sensitive typing needs to handle attrs
245
+ def starts_at?(other_loc)
246
+ location&.filename == other_loc.filename &&
247
+ presence &&
248
+ # @sg-ignore flow sensitive typing needs to handle attrs
249
+ presence.start == other_loc.range.start
250
+ end
251
+
252
+ # Narrow the presence range to the intersection of both.
253
+ #
254
+ # @param other [self]
255
+ #
256
+ # @return [Range, nil]
257
+ def combine_presence(other)
258
+ return presence || other.presence if presence.nil? || other.presence.nil?
259
+
260
+ # @sg-ignore flow sensitive typing needs to handle attrs
261
+ Range.new([presence.start, other.presence.start].max, [presence.ending, other.presence.ending].min)
262
+ end
263
+
264
+ # @param other [self]
265
+ # @return [Pin::Closure, nil]
266
+ def combine_closure(other)
267
+ return closure if self.closure == other.closure
268
+
269
+ # choose first defined, as that establishes the scope of the variable
270
+ if closure.nil? || other.closure.nil?
271
+ Solargraph.assert_or_log(:varible_closure_missing) do
272
+ "One of the local variables being combined is missing a closure: " \
273
+ "#{self.inspect} vs #{other.inspect}"
274
+ end
275
+ return closure || other.closure
276
+ end
277
+
278
+ # @sg-ignore flow sensitive typing needs to handle attrs
279
+ if closure.location.nil? || other.closure.location.nil?
280
+ # @sg-ignore flow sensitive typing needs to handle attrs
281
+ return closure.location.nil? ? other.closure : closure
282
+ end
283
+
284
+ # if filenames are different, this will just pick one
285
+ # @sg-ignore flow sensitive typing needs to handle attrs
286
+ return closure if closure.location <= other.closure.location
287
+
288
+ other.closure
289
+ end
290
+
291
+ # @param other_closure [Pin::Closure]
292
+ # @param other_loc [Location]
293
+ def visible_at?(other_closure, other_loc)
294
+ # @sg-ignore flow sensitive typing needs to handle attrs
295
+ location.filename == other_loc.filename &&
296
+ # @sg-ignore flow sensitive typing needs to handle attrs
297
+ (!presence || presence.include?(other_loc.range.start)) &&
298
+ visible_in_closure?(other_closure)
299
+ end
300
+
301
+ def presence_certain?
302
+ @presence_certain
303
+ end
304
+
305
+ protected
306
+
307
+ attr_accessor :exclude_return_type, :intersection_return_type
308
+
309
+ # @return [Range]
310
+ attr_writer :presence
311
+
116
312
  private
117
313
 
118
- # @return [ComplexType]
314
+ # @param api_map [ApiMap]
315
+ # @param raw_return_type [ComplexType, ComplexType::UniqueType]
316
+ #
317
+ # @return [ComplexType, ComplexType::UniqueType]
318
+ def adjust_type(api_map, raw_return_type)
319
+ qualified_exclude = exclude_return_type&.qualify(api_map, *(closure&.gates || ['']))
320
+ minus_exclusions = raw_return_type.exclude qualified_exclude, api_map
321
+ qualified_intersection = intersection_return_type&.qualify(api_map, *(closure&.gates || ['']))
322
+ minus_exclusions.intersect_with qualified_intersection, api_map
323
+ end
324
+
325
+ # @param other [self]
326
+ # @return [Pin::Closure, nil]
327
+ def combine_closure(other)
328
+ return closure if self.closure == other.closure
329
+
330
+ # choose first defined, as that establishes the scope of the variable
331
+ if closure.nil? || other.closure.nil?
332
+ Solargraph.assert_or_log(:varible_closure_missing) do
333
+ "One of the local variables being combined is missing a closure: " \
334
+ "#{self.inspect} vs #{other.inspect}"
335
+ end
336
+ return closure || other.closure
337
+ end
338
+
339
+ # @sg-ignore Need to add nil check here
340
+ if closure.location.nil? || other.closure.location.nil?
341
+ # @sg-ignore Need to add nil check here
342
+ return closure.location.nil? ? other.closure : closure
343
+ end
344
+
345
+ # if filenames are different, this will just pick one
346
+ # @sg-ignore flow sensitive typing needs to handle attrs
347
+ return closure if closure.location <= other.closure.location
348
+
349
+ other.closure
350
+ end
351
+
352
+ # See if this variable is visible within 'viewing_closure'
353
+ #
354
+ # @param viewing_closure [Pin::Closure]
355
+ # @return [Boolean]
356
+ def visible_in_closure? viewing_closure
357
+ return false if closure.nil?
358
+
359
+ # if we're declared at top level, we can't be seen from within
360
+ # methods declared tere
361
+
362
+ # @sg-ignore Need to add nil check here
363
+ return false if viewing_closure.is_a?(Pin::Method) && closure.context.tags == 'Class<>'
364
+
365
+ # @sg-ignore Need to add nil check here
366
+ return true if viewing_closure.binder.namespace == closure.binder.namespace
367
+
368
+ # @sg-ignore Need to add nil check here
369
+ return true if viewing_closure.return_type == closure.context
370
+
371
+ # classes and modules can't see local variables declared
372
+ # in their parent closure, so stop here
373
+ return false if scope == :instance && viewing_closure.is_a?(Pin::Namespace)
374
+
375
+ parent_of_viewing_closure = viewing_closure.closure
376
+
377
+ return false if parent_of_viewing_closure.nil?
378
+
379
+ visible_in_closure?(parent_of_viewing_closure)
380
+ end
381
+
382
+ # @param other [self]
383
+ # @return [ComplexType, nil]
384
+ def combine_return_type(other)
385
+ combine_types(other, :return_type)
386
+ end
387
+
388
+ # @param other [self]
389
+ # @param attr [::Symbol]
390
+ #
391
+ # @return [ComplexType, nil]
392
+ def combine_types(other, attr)
393
+ # @type [ComplexType, nil]
394
+ type1 = send(attr)
395
+ # @type [ComplexType, nil]
396
+ type2 = other.send(attr)
397
+ if type1 && type2
398
+ types = (type1.items + type2.items).uniq
399
+ ComplexType.new(types)
400
+ else
401
+ type1 || type2
402
+ end
403
+ end
404
+
405
+ # @return [::Symbol]
406
+ def scope
407
+ :instance
408
+ end
409
+
410
+ # @return [ComplexType, nil]
119
411
  def generate_complex_type
120
412
  tag = docstring.tag(:type)
121
413
  return ComplexType.try_parse(*tag.types) unless tag.nil? || tag.types.nil? || tag.types.empty?
122
- ComplexType.new
414
+ nil
123
415
  end
124
416
  end
125
417
  end