steep 1.2.1 → 1.3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile.lock +4 -4
  4. data/Gemfile.steep +1 -1
  5. data/Gemfile.steep.lock +13 -3
  6. data/Steepfile +0 -1
  7. data/lib/steep/annotation_parser.rb +34 -28
  8. data/lib/steep/ast/annotation.rb +16 -5
  9. data/lib/steep/ast/node/type_application.rb +74 -0
  10. data/lib/steep/ast/node/type_assertion.rb +56 -0
  11. data/lib/steep/ast/types/factory.rb +5 -1
  12. data/lib/steep/diagnostic/helper.rb +2 -1
  13. data/lib/steep/diagnostic/lsp_formatter.rb +3 -1
  14. data/lib/steep/diagnostic/ruby.rb +70 -5
  15. data/lib/steep/diagnostic/signature.rb +21 -8
  16. data/lib/steep/drivers/check.rb +1 -1
  17. data/lib/steep/drivers/checkfile.rb +1 -1
  18. data/lib/steep/drivers/langserver.rb +2 -2
  19. data/lib/steep/drivers/stats.rb +1 -1
  20. data/lib/steep/drivers/watch.rb +1 -1
  21. data/lib/steep/drivers/worker.rb +0 -1
  22. data/lib/steep/server/lsp_formatter.rb +13 -3
  23. data/lib/steep/server/master.rb +4 -1
  24. data/lib/steep/server/worker_process.rb +86 -14
  25. data/lib/steep/services/hover_provider/rbs.rb +7 -7
  26. data/lib/steep/services/hover_provider/ruby.rb +19 -4
  27. data/lib/steep/services/signature_service.rb +7 -4
  28. data/lib/steep/signature/validator.rb +36 -13
  29. data/lib/steep/source.rb +189 -71
  30. data/lib/steep/type_construction.rb +232 -126
  31. data/lib/steep/type_inference/logic_type_interpreter.rb +3 -1
  32. data/lib/steep/version.rb +1 -1
  33. data/lib/steep.rb +2 -0
  34. data/rbs_collection.steep.lock.yaml +27 -10
  35. data/rbs_collection.steep.yaml +0 -1
  36. data/sig/shims/exception.rbs +4 -0
  37. data/sig/shims/parser/comment.rbs +33 -0
  38. data/sig/shims/parser.rbs +30 -2
  39. data/sig/steep/annotation_parser.rbs +59 -0
  40. data/sig/steep/ast/annotation.rbs +21 -26
  41. data/sig/steep/ast/node/type_application.rbs +31 -0
  42. data/sig/steep/ast/node/type_assertion.rbs +26 -0
  43. data/sig/steep/ast/types/factory.rbs +0 -2
  44. data/sig/steep/diagnostic/helper.rbs +9 -3
  45. data/sig/steep/diagnostic/lsp_formatter.rbs +12 -8
  46. data/sig/steep/diagnostic/ruby.rbs +62 -8
  47. data/sig/steep/diagnostic/signature.rbs +118 -85
  48. data/sig/steep/drivers/worker.rbs +11 -13
  49. data/sig/steep/range_extension.rbs +7 -0
  50. data/sig/steep/server/lsp_formatter.rbs +14 -7
  51. data/sig/steep/server/worker_process.rbs +74 -12
  52. data/sig/steep/services/hover_provider/rbs.rbs +27 -7
  53. data/sig/steep/services/hover_provider/ruby.rbs +18 -4
  54. data/sig/steep/services/hover_provider/singleton_methods.rbs +1 -1
  55. data/sig/steep/signature/validator.rbs +76 -0
  56. data/sig/steep/source.rbs +54 -30
  57. data/sig/steep/type_construction.rbs +85 -27
  58. data/sig/steep/type_inference/method_call.rbs +1 -1
  59. data/smoke/diagnostics-rbs/inherit-module.rbs +2 -0
  60. data/smoke/diagnostics-rbs/test_expectations.yml +12 -0
  61. data/steep.gemspec +1 -1
  62. metadata +16 -6
data/lib/steep/source.rb CHANGED
@@ -1,27 +1,11 @@
1
1
  module Steep
2
2
  class Source
3
- class LocatedAnnotation
4
- attr_reader :line
5
- attr_reader :annotation
6
- attr_reader :source
7
-
8
- def initialize(line:, source:, annotation:)
9
- @line = line
10
- @source = source
11
- @annotation = annotation
12
- end
13
-
14
- def ==(other)
15
- other.is_a?(LocatedAnnotation) &&
16
- other.line == line &&
17
- other.annotation == annotation
18
- end
19
- end
20
-
21
3
  attr_reader :path
22
4
  attr_reader :node
23
5
  attr_reader :mapping
24
6
 
7
+ extend NodeHelper
8
+
25
9
  def initialize(path:, node:, mapping:)
26
10
  @path = path
27
11
  @node = node
@@ -47,46 +31,61 @@ module Steep
47
31
 
48
32
  def self.parse(source_code, path:, factory:)
49
33
  buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
50
- node = new_parser().parse(buffer)
34
+ node, comments = new_parser().parse_with_comments(buffer)
51
35
 
36
+ # @type var annotations: Array[AST::Annotation::t]
52
37
  annotations = []
53
-
54
- _, comments, _ = yield_self do
55
- buffer = ::Parser::Source::Buffer.new(path.to_s, 1, source: source_code)
56
- new_parser().tokenize(buffer)
57
- end
38
+ # @type var type_comments: Hash[Integer, type_comment]
39
+ type_comments = {}
58
40
 
59
41
  buffer = RBS::Buffer.new(name: path, content: source_code)
42
+ annotation_parser = AnnotationParser.new(factory: factory)
60
43
 
61
44
  comments.each do |comment|
62
- src = comment.text.gsub(/\A#\s*/, '')
63
- location = RBS::Location.new(buffer: buffer,
64
- start_pos: comment.location.expression.begin_pos + 1,
65
- end_pos: comment.location.expression.end_pos)
66
- annotation = AnnotationParser.new(factory: factory).parse(src, location: location)
67
- if annotation
68
- annotations << LocatedAnnotation.new(line: comment.location.line, source: src, annotation: annotation)
45
+ if comment.inline?
46
+ content = comment.text.delete_prefix('#')
47
+ content.lstrip!
48
+ prefix = comment.text.size - content.size
49
+ content.rstrip!
50
+ suffix = comment.text.size - content.size - prefix
51
+
52
+ location = RBS::Location.new(
53
+ buffer: buffer,
54
+ start_pos: comment.location.expression.begin_pos + prefix,
55
+ end_pos: comment.location.expression.end_pos - suffix
56
+ )
57
+
58
+ case
59
+ when annotation = annotation_parser.parse(content, location: location)
60
+ annotations << annotation
61
+ when assertion = AST::Node::TypeAssertion.parse(location)
62
+ type_comments[assertion.line] = assertion
63
+ when tapp = AST::Node::TypeApplication.parse(location)
64
+ type_comments[tapp.line] = tapp
65
+ end
69
66
  end
70
67
  end
71
68
 
72
- mapping = {}.compare_by_identity
69
+ map = {}
70
+ map.compare_by_identity
73
71
 
74
72
  if node
75
- construct_mapping(node: node, annotations: annotations, mapping: mapping)
73
+ node = insert_type_node(node, type_comments)
74
+ construct_mapping(node: node, annotations: annotations, mapping: map)
76
75
  end
77
76
 
78
77
  annotations.each do |annot|
79
- mapping[node] ||= []
80
- mapping[node] << annot
78
+ map[node] ||= []
79
+ map[node] << annot
81
80
  end
82
81
 
83
- new(path: path, node: node, mapping: mapping)
82
+ new(path: path, node: node, mapping: map)
84
83
  end
85
84
 
86
85
  def self.construct_mapping(node:, annotations:, mapping:, line_range: nil)
87
86
  case node.type
88
87
  when :if
89
- if node.loc.is_a?(::Parser::Source::Map::Ternary)
88
+ if node.loc.respond_to?(:question)
90
89
  # Skip ternary operator
91
90
  each_child_node node do |child|
92
91
  construct_mapping(node: child, annotations: annotations, mapping: mapping, line_range: nil)
@@ -200,7 +199,7 @@ module Steep
200
199
  last_cond = node.children[-2]
201
200
  body = node.children.last
202
201
 
203
- node.children.take(node.children.size-1) do |child|
202
+ node.children.take(node.children.size-1).each do |child|
204
203
  construct_mapping(node: child, annotations: annotations, mapping: mapping, line_range: nil)
205
204
  end
206
205
 
@@ -235,7 +234,7 @@ module Steep
235
234
  end
236
235
  end
237
236
 
238
- associated_annotations = annotations.select do |annot|
237
+ associated_annotations, other_annotations = annotations.partition do |annot|
239
238
  case node.type
240
239
  when :def, :module, :class, :block, :ensure, :defs
241
240
  loc = node.loc
@@ -257,33 +256,34 @@ module Steep
257
256
  associated_annotations.each do |annot|
258
257
  mapping[node] ||= []
259
258
  mapping[node] << annot
260
- annotations.delete annot
261
259
  end
262
- end
263
260
 
264
- def self.each_child_node(node)
265
- node.children.each do |child|
266
- if child.is_a?(::AST::Node)
267
- yield child
268
- end
269
- end
261
+ annotations.replace(other_annotations)
270
262
  end
271
263
 
272
- def self.map_child_nodes(node)
264
+ def self.map_child_node(node, type = nil, skip: nil)
273
265
  children = node.children.map do |child|
274
- if child.is_a?(::AST::Node)
275
- yield child
266
+ if child.is_a?(Parser::AST::Node)
267
+ if skip
268
+ if skip.member?(child)
269
+ child
270
+ else
271
+ yield child
272
+ end
273
+ else
274
+ yield child
275
+ end
276
276
  else
277
277
  child
278
278
  end
279
279
  end
280
280
 
281
- node.updated(nil, children)
281
+ node.updated(type, children)
282
282
  end
283
283
 
284
284
  def annotations(block:, factory:, context:)
285
285
  AST::Annotation::Collection.new(
286
- annotations: (mapping[block] || []).map(&:annotation),
286
+ annotations: (mapping[block] || []),
287
287
  factory: factory,
288
288
  context: context
289
289
  )
@@ -292,7 +292,7 @@ module Steep
292
292
  def each_annotation(&block)
293
293
  if block_given?
294
294
  mapping.each do |node, annots|
295
- yield node, annots.map(&:annotation)
295
+ yield [node, annots]
296
296
  end
297
297
  else
298
298
  enum_for :each_annotation
@@ -301,9 +301,11 @@ module Steep
301
301
 
302
302
  def each_heredoc_node(node = self.node, parents = [], &block)
303
303
  if block
304
+ return unless node
305
+
304
306
  case node.type
305
307
  when :dstr, :str
306
- if node.location.is_a?(Parser::Source::Map::Heredoc)
308
+ if node.location.respond_to?(:heredoc_body)
307
309
  yield [node, *parents]
308
310
  end
309
311
  end
@@ -344,7 +346,9 @@ module Steep
344
346
  parents.unshift node
345
347
 
346
348
  Source.each_child_node(node) do |child|
347
- ns = find_nodes_loc(child, position, parents) and return ns
349
+ if ns = find_nodes_loc(child, position, parents)
350
+ return ns
351
+ end
348
352
  end
349
353
 
350
354
  parents
@@ -359,12 +363,14 @@ module Steep
359
363
  node.location.expression.source_buffer.source_line(i+1).size + 1
360
364
  end + column
361
365
 
362
- if nodes = find_heredoc_nodes(line, column, position)
363
- Source.each_child_node(nodes[0]) do |child|
364
- find_nodes_loc(child, position, nodes) and break
366
+ if heredoc_nodes = find_heredoc_nodes(line, column, position)
367
+ Source.each_child_node(heredoc_nodes[0]) do |child|
368
+ if nodes = find_nodes_loc(child, position, heredoc_nodes)
369
+ return nodes
370
+ end
365
371
  end
366
372
 
367
- nodes
373
+ return heredoc_nodes
368
374
  else
369
375
  find_nodes_loc(node, position, [])
370
376
  end
@@ -385,7 +391,7 @@ module Steep
385
391
  delete_defs(node.children[0], allow_list)
386
392
  end
387
393
  else
388
- map_child_nodes(node) do |child|
394
+ map_child_node(node) do |child|
389
395
  delete_defs(child, allow_list)
390
396
  end
391
397
  end
@@ -398,7 +404,9 @@ module Steep
398
404
 
399
405
  node_ = Source.delete_defs(node, defs)
400
406
 
401
- mapping = {}.compare_by_identity
407
+ # @type var mapping: Hash[Parser::AST::Node, Array[AST::Annotation::t]]
408
+ mapping = {}
409
+ mapping.compare_by_identity
402
410
 
403
411
  annotations = self.mapping.values.flatten
404
412
  Source.construct_mapping(node: node_, annotations: annotations, mapping: mapping)
@@ -414,19 +422,129 @@ module Steep
414
422
  end
415
423
  end
416
424
 
417
- def compact_siblings(node)
418
- case node
419
- when :def
420
- node.updated(:nil, [])
421
- when :defs
422
- node.children[0]
423
- when :class
424
- node.updated(:class, [node.children[0], node.children[1], nil])
425
- when :module
426
- node.updated(:module, [node.children[0], nil])
425
+ def self.insert_type_node(node, comments)
426
+ if node.location.expression
427
+ first_line = node.location.expression.first_line
428
+ last_line = node.location.expression.last_line
429
+ last_comment = comments[last_line]
430
+
431
+ if (first_line..last_line).none? {|l| comments.key?(l) }
432
+ return node
433
+ end
434
+
435
+ case
436
+ when last_comment.is_a?(AST::Node::TypeAssertion)
437
+ case node.type
438
+ when :lvasgn, :ivasgn, :gvasgn, :cvasgn, :casgn
439
+ # Skip
440
+ when :masgn
441
+ lhs, rhs = node.children
442
+ node = node.updated(nil, [lhs, insert_type_node(rhs, comments)])
443
+ return adjust_location(node)
444
+ when :return, :break, :next
445
+ # Skip
446
+ when :begin
447
+ if node.loc.begin
448
+ # paren
449
+ child_assertions = comments.except(last_line)
450
+ node = map_child_node(node) {|child| insert_type_node(child, child_assertions) }
451
+ node = adjust_location(node)
452
+ return assertion_node(node, last_comment)
453
+ end
454
+ else
455
+ child_assertions = comments.except(last_line)
456
+ node = map_child_node(node) {|child| insert_type_node(child, child_assertions) }
457
+ node = adjust_location(node)
458
+ return assertion_node(node, last_comment)
459
+ end
460
+ when selector_line = sendish_node?(node)
461
+ if (comment = comments[selector_line]).is_a?(AST::Node::TypeApplication)
462
+ child_assertions = comments.except(selector_line)
463
+ case node.type
464
+ when :block
465
+ send, *children = node.children
466
+ node = node.updated(
467
+ nil,
468
+ [
469
+ map_child_node(send) {|child| insert_type_node(child, child_assertions) },
470
+ *children.map {|child| insert_type_node(child, child_assertions) }
471
+ ]
472
+ )
473
+ when :numblock
474
+ send, size, body = node.children
475
+ node = node.updated(
476
+ nil,
477
+ [
478
+ map_child_node(send) {|child| insert_type_node(child, child_assertions) },
479
+ size,
480
+ insert_type_node(body, child_assertions)
481
+ ]
482
+ )
483
+ else
484
+ node = map_child_node(node) {|child| insert_type_node(child, child_assertions) }
485
+ end
486
+ node = adjust_location(node)
487
+ return type_application_node(node, comment)
488
+ end
489
+ end
490
+ end
491
+
492
+ adjust_location(
493
+ map_child_node(node, nil) {|child| insert_type_node(child, comments) }
494
+ )
495
+ end
496
+
497
+ def self.sendish_node?(node)
498
+ send_node =
499
+ case node.type
500
+ when :send, :csend
501
+ node
502
+ when :block, :numblock
503
+ send = node.children[0]
504
+ case send.type
505
+ when :send, :csend
506
+ send
507
+ end
508
+ end
509
+
510
+ if send_node
511
+ if send_node.location.dot
512
+ send_node.location.selector.line
513
+ end
514
+ end
515
+ end
516
+
517
+ def self.adjust_location(node)
518
+ if end_pos = node.location.expression&.end_pos
519
+ if last_pos = each_child_node(node).map {|node| node.location.expression&.end_pos }.compact.max
520
+ if last_pos > end_pos
521
+ props = { location: node.location.with_expression(node.location.expression.with(end_pos: last_pos)) }
522
+ end
523
+ end
524
+ end
525
+
526
+ if props
527
+ node.updated(nil, nil, props)
427
528
  else
428
529
  node
429
530
  end
430
531
  end
532
+
533
+ def self.assertion_node(node, type)
534
+ map = Parser::Source::Map.new(node.location.expression.with(end_pos: type.location.end_pos))
535
+ Parser::AST::Node.new(:assertion, [node, type], { location: map })
536
+ end
537
+
538
+ def self.type_application_node(node, tapp)
539
+ if node.location.expression.end_pos > tapp.location.end_pos
540
+ map = Parser::Source::Map.new(node.location.expression)
541
+ else
542
+ map = Parser::Source::Map.new(node.location.expression.with(end_pos: tapp.location.end_pos))
543
+ end
544
+
545
+ node = Parser::AST::Node.new(:tapp, [node, tapp], { location: map })
546
+ tapp.set_node(node)
547
+ node
548
+ end
431
549
  end
432
550
  end