rbi 0.2.4 → 0.3.2

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.
data/lib/rbi/parser.rb CHANGED
@@ -5,12 +5,10 @@ require "prism"
5
5
 
6
6
  module RBI
7
7
  class ParseError < Error
8
- extend T::Sig
9
-
10
- sig { returns(Loc) }
8
+ #: Loc
11
9
  attr_reader :location
12
10
 
13
- sig { params(message: String, location: Loc).void }
11
+ #: (String message, Loc location) -> void
14
12
  def initialize(message, location)
15
13
  super(message)
16
14
  @location = location
@@ -18,19 +16,17 @@ module RBI
18
16
  end
19
17
 
20
18
  class UnexpectedParserError < Error
21
- extend T::Sig
22
-
23
- sig { returns(Loc) }
19
+ #: Loc
24
20
  attr_reader :last_location
25
21
 
26
- sig { params(parent_exception: Exception, last_location: Loc).void }
22
+ #: (Exception parent_exception, Loc last_location) -> void
27
23
  def initialize(parent_exception, last_location)
28
24
  super(parent_exception)
29
25
  set_backtrace(parent_exception.backtrace)
30
26
  @last_location = last_location
31
27
  end
32
28
 
33
- sig { params(io: T.any(IO, StringIO)).void }
29
+ #: (?io: (IO | StringIO)) -> void
34
30
  def print_debug(io: $stderr)
35
31
  io.puts ""
36
32
  io.puts "##################################"
@@ -51,47 +47,43 @@ module RBI
51
47
  end
52
48
 
53
49
  class Parser
54
- extend T::Sig
55
-
56
50
  class << self
57
- extend T::Sig
58
-
59
- sig { params(string: String).returns(Tree) }
51
+ #: (String string) -> Tree
60
52
  def parse_string(string)
61
53
  Parser.new.parse_string(string)
62
54
  end
63
55
 
64
- sig { params(path: String).returns(Tree) }
56
+ #: (String path) -> Tree
65
57
  def parse_file(path)
66
58
  Parser.new.parse_file(path)
67
59
  end
68
60
 
69
- sig { params(paths: T::Array[String]).returns(T::Array[Tree]) }
61
+ #: (Array[String] paths) -> Array[Tree]
70
62
  def parse_files(paths)
71
63
  parser = Parser.new
72
64
  paths.map { |path| parser.parse_file(path) }
73
65
  end
74
66
 
75
- sig { params(strings: T::Array[String]).returns(T::Array[Tree]) }
67
+ #: (Array[String] strings) -> Array[Tree]
76
68
  def parse_strings(strings)
77
69
  parser = Parser.new
78
70
  strings.map { |string| parser.parse_string(string) }
79
71
  end
80
72
  end
81
73
 
82
- sig { params(string: String).returns(Tree) }
74
+ #: (String string) -> Tree
83
75
  def parse_string(string)
84
76
  parse(string, file: "-")
85
77
  end
86
78
 
87
- sig { params(path: String).returns(Tree) }
79
+ #: (String path) -> Tree
88
80
  def parse_file(path)
89
81
  parse(::File.read(path), file: path)
90
82
  end
91
83
 
92
84
  private
93
85
 
94
- sig { params(source: String, file: String).returns(Tree) }
86
+ #: (String source, file: String) -> Tree
95
87
  def parse(source, file:)
96
88
  result = Prism.parse(source)
97
89
  unless result.success?
@@ -120,9 +112,7 @@ module RBI
120
112
  end
121
113
 
122
114
  class Visitor < Prism::Visitor
123
- extend T::Sig
124
-
125
- sig { params(source: String, file: String).void }
115
+ #: (String source, file: String) -> void
126
116
  def initialize(source, file:)
127
117
  super()
128
118
 
@@ -132,46 +122,56 @@ module RBI
132
122
 
133
123
  private
134
124
 
135
- sig { params(node: Prism::Node).returns(Loc) }
125
+ #: (Prism::Node node) -> Loc
136
126
  def node_loc(node)
137
127
  Loc.from_prism(@file, node.location)
138
128
  end
139
129
 
140
- sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(String)) }
130
+ #: (Prism::Node? node) -> String?
141
131
  def node_string(node)
142
132
  return unless node
143
133
 
144
134
  node.slice
145
135
  end
146
136
 
147
- sig { params(node: Prism::Node).returns(String) }
137
+ #: (Prism::Node node) -> String
148
138
  def node_string!(node)
149
- T.must(node_string(node))
139
+ node_string(node) #: as !nil
140
+ end
141
+
142
+ #: (Prism::Node node) -> Prism::Location
143
+ def adjust_prism_location_for_heredoc(node)
144
+ visitor = HeredocLocationVisitor.new(
145
+ node.location.send(:source),
146
+ node.location.start_offset,
147
+ node.location.end_offset,
148
+ )
149
+ visitor.visit(node)
150
+ visitor.location
150
151
  end
151
152
  end
152
153
 
153
154
  class TreeBuilder < Visitor
154
- extend T::Sig
155
-
156
- sig { returns(Tree) }
155
+ #: Tree
157
156
  attr_reader :tree
158
157
 
159
- sig { returns(T.nilable(Prism::Node)) }
158
+ #: Prism::Node?
160
159
  attr_reader :last_node
161
160
 
162
- sig { params(source: String, comments: T::Array[Prism::Comment], file: String).void }
161
+ #: (String source, comments: Array[Prism::Comment], file: String) -> void
163
162
  def initialize(source, comments:, file:)
164
163
  super(source, file: file)
165
164
 
166
- @comments_by_line = T.let(comments.to_h { |c| [c.location.start_line, c] }, T::Hash[Integer, Prism::Comment])
167
- @tree = T.let(Tree.new, Tree)
165
+ @comments_by_line = comments.to_h { |c| [c.location.start_line, c] } #: Hash[Integer, Prism::Comment]
166
+ @tree = Tree.new #: Tree
168
167
 
169
- @scopes_stack = T.let([@tree], T::Array[Tree])
170
- @last_node = T.let(nil, T.nilable(Prism::Node))
171
- @last_sigs = T.let([], T::Array[RBI::Sig])
168
+ @scopes_stack = [@tree] #: Array[Tree]
169
+ @last_node = nil #: Prism::Node?
170
+ @last_sigs = [] #: Array[RBI::Sig]
172
171
  end
173
172
 
174
- sig { override.params(node: Prism::ClassNode).void }
173
+ # @override
174
+ #: (Prism::ClassNode node) -> void
175
175
  def visit_class_node(node)
176
176
  @last_node = node
177
177
  superclass_name = node_string(node.superclass)
@@ -206,54 +206,78 @@ module RBI
206
206
  @last_node = nil
207
207
  end
208
208
 
209
- sig { override.params(node: Prism::ConstantWriteNode).void }
209
+ # @override
210
+ #: (Prism::ConstantWriteNode node) -> void
210
211
  def visit_constant_write_node(node)
211
212
  @last_node = node
212
213
  visit_constant_assign(node)
213
214
  @last_node = nil
214
215
  end
215
216
 
216
- sig { override.params(node: Prism::ConstantPathWriteNode).void }
217
+ # @override
218
+ #: (Prism::ConstantPathWriteNode node) -> void
217
219
  def visit_constant_path_write_node(node)
218
220
  @last_node = node
219
221
  visit_constant_assign(node)
220
222
  @last_node = nil
221
223
  end
222
224
 
223
- sig { params(node: T.any(Prism::ConstantWriteNode, Prism::ConstantPathWriteNode)).void }
225
+ #: ((Prism::ConstantWriteNode | Prism::ConstantPathWriteNode) node) -> void
224
226
  def visit_constant_assign(node)
225
227
  struct = parse_struct(node)
226
228
 
227
229
  current_scope << if struct
228
230
  struct
229
- elsif type_variable_definition?(node.value)
230
- TypeMember.new(
231
+ elsif t_enum_value?(node)
232
+ TEnumValue.new(
231
233
  case node
232
234
  when Prism::ConstantWriteNode
233
235
  node.name.to_s
234
236
  when Prism::ConstantPathWriteNode
235
237
  node_string!(node.target)
236
238
  end,
237
- node_string!(node.value),
238
239
  loc: node_loc(node),
239
240
  comments: node_comments(node),
240
241
  )
241
242
  else
242
- Const.new(
243
- case node
244
- when Prism::ConstantWriteNode
245
- node.name.to_s
246
- when Prism::ConstantPathWriteNode
247
- node_string!(node.target)
248
- end,
249
- node_string!(node.value),
250
- loc: node_loc(node),
251
- comments: node_comments(node),
243
+ adjusted_node_location = adjust_prism_location_for_heredoc(node)
244
+
245
+ adjusted_value_location = Prism::Location.new(
246
+ node.value.location.send(:source),
247
+ node.value.location.start_offset,
248
+ adjusted_node_location.end_offset - node.value.location.start_offset,
252
249
  )
250
+
251
+ if type_variable_definition?(node.value)
252
+ TypeMember.new(
253
+ case node
254
+ when Prism::ConstantWriteNode
255
+ node.name.to_s
256
+ when Prism::ConstantPathWriteNode
257
+ node_string!(node.target)
258
+ end,
259
+ adjusted_value_location.slice,
260
+ loc: Loc.from_prism(@file, adjusted_node_location),
261
+ comments: node_comments(node),
262
+ )
263
+ else
264
+ Const.new(
265
+ case node
266
+ when Prism::ConstantWriteNode
267
+ node.name.to_s
268
+ when Prism::ConstantPathWriteNode
269
+ node_string!(node.target)
270
+ end,
271
+ adjusted_value_location.slice,
272
+ loc: Loc.from_prism(@file, adjusted_node_location),
273
+ comments: node_comments(node),
274
+ )
275
+ end
253
276
  end
254
277
  end
255
278
 
256
- sig { override.params(node: Prism::DefNode).void }
279
+ # @override
280
+ #: (Prism::DefNode node) -> void
257
281
  def visit_def_node(node)
258
282
  @last_node = node
259
283
 
@@ -274,7 +298,8 @@ module RBI
274
298
  @last_node = nil
275
299
  end
276
300
 
277
- sig { override.params(node: Prism::ModuleNode).void }
301
+ # @override
302
+ #: (Prism::ModuleNode node) -> void
278
303
  def visit_module_node(node)
279
304
  @last_node = node
280
305
  scope = Module.new(
@@ -292,7 +317,8 @@ module RBI
292
317
  @last_node = nil
293
318
  end
294
319
 
295
- sig { override.params(node: Prism::ProgramNode).void }
320
+ # @override
321
+ #: (Prism::ProgramNode node) -> void
296
322
  def visit_program_node(node)
297
323
  @last_node = node
298
324
  super
@@ -303,7 +329,8 @@ module RBI
303
329
  @last_node = nil
304
330
  end
305
331
 
306
- sig { override.params(node: Prism::SingletonClassNode).void }
332
+ # @override
333
+ #: (Prism::SingletonClassNode node) -> void
307
334
  def visit_singleton_class_node(node)
308
335
  @last_node = node
309
336
  scope = SingletonClass.new(
@@ -320,7 +347,7 @@ module RBI
320
347
  @last_node = nil
321
348
  end
322
349
 
323
- sig { params(node: Prism::CallNode).void }
350
+ #: (Prism::CallNode node) -> void
324
351
  def visit_call_node(node)
325
352
  @last_node = node
326
353
  message = node.name.to_s
@@ -494,7 +521,7 @@ module RBI
494
521
  private
495
522
 
496
523
  # Collect all the remaining comments within a node
497
- sig { params(node: Prism::Node).void }
524
+ #: (Prism::Node node) -> void
498
525
  def collect_dangling_comments(node)
499
526
  first_line = node.location.start_line
500
527
  last_line = node.location.end_line
@@ -512,9 +539,9 @@ module RBI
512
539
  end
513
540
 
514
541
  # Collect all the remaining comments after visiting the tree
515
- sig { void }
542
+ #: -> void
516
543
  def collect_orphan_comments
517
- last_line = T.let(nil, T.nilable(Integer))
544
+ last_line = nil #: Integer?
518
545
  last_node_end = @tree.nodes.last&.loc&.end_line
519
546
 
520
547
  @comments_by_line.each do |line, comment|
@@ -535,21 +562,21 @@ module RBI
535
562
  end
536
563
  end
537
564
 
538
- sig { returns(Tree) }
565
+ #: -> Tree
539
566
  def current_scope
540
- T.must(@scopes_stack.last) # Should never be nil since we create a Tree as the root
567
+ @scopes_stack.last #: as !nil # Should never be nil since we create a Tree as the root
541
568
  end
542
569
 
543
- sig { returns(T::Array[Sig]) }
570
+ #: -> Array[Sig]
544
571
  def current_sigs
545
572
  sigs = @last_sigs.dup
546
573
  @last_sigs.clear
547
574
  sigs
548
575
  end
549
576
 
550
- sig { params(sigs: T::Array[Sig]).returns(T::Array[Comment]) }
577
+ #: (Array[Sig] sigs) -> Array[Comment]
551
578
  def detach_comments_from_sigs(sigs)
552
- comments = T.let([], T::Array[Comment])
579
+ comments = [] #: Array[Comment]
553
580
 
554
581
  sigs.each do |sig|
555
582
  comments += sig.comments.dup
@@ -559,7 +586,7 @@ module RBI
559
586
  comments
560
587
  end
561
588
 
562
- sig { params(node: Prism::Node).returns(T::Array[Comment]) }
589
+ #: (Prism::Node node) -> Array[Comment]
563
590
  def node_comments(node)
564
591
  comments = []
565
592
 
@@ -577,14 +604,22 @@ module RBI
577
604
  comments
578
605
  end
579
606
 
580
- sig { params(node: Prism::Comment).returns(Comment) }
607
+ #: (Prism::Comment node) -> Comment
581
608
  def parse_comment(node)
582
- Comment.new(node.location.slice.gsub(/^# ?/, "").rstrip, loc: Loc.from_prism(@file, node.location))
609
+ loc = Loc.from_prism(@file, node.location)
610
+ string = node.location.slice
611
+ # We also ignore RDoc directives such as `:nodoc:`
612
+ # See https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Directives
613
+ if string.start_with?("#:") && !(string =~ /^#:[a-z_]+:/)
614
+ RBSComment.new(string.gsub(/^#: ?/, "").rstrip, loc: loc)
615
+ else
616
+ Comment.new(string.gsub(/^# ?/, "").rstrip, loc: loc)
617
+ end
583
618
  end
584
619
 
585
- sig { params(node: T.nilable(Prism::Node)).returns(T::Array[Arg]) }
620
+ #: (Prism::Node? node) -> Array[Arg]
586
621
  def parse_send_args(node)
587
- args = T.let([], T::Array[Arg])
622
+ args = [] #: Array[Arg]
588
623
  return args unless node.is_a?(Prism::ArgumentsNode)
589
624
 
590
625
  node.arguments.each do |arg|
@@ -595,18 +630,20 @@ module RBI
595
630
 
596
631
  args << KwArg.new(
597
632
  node_string!(assoc.key).delete_suffix(":"),
598
- T.must(node_string(assoc.value)),
633
+ node_string(assoc.value), #: as !nil
599
634
  )
600
635
  end
601
636
  else
602
- args << Arg.new(T.must(node_string(arg)))
637
+ args << Arg.new(
638
+ node_string(arg), #: as !nil
639
+ )
603
640
  end
604
641
  end
605
642
 
606
643
  args
607
644
  end
608
645
 
609
- sig { params(node: T.nilable(Prism::Node)).returns(T::Array[Param]) }
646
+ #: (Prism::Node? node) -> Array[Param]
610
647
  def parse_params(node)
611
648
  params = []
612
649
  return params unless node.is_a?(Prism::ParametersNode)
@@ -680,7 +717,7 @@ module RBI
680
717
  params
681
718
  end
682
719
 
683
- sig { params(node: Prism::CallNode).returns(Sig) }
720
+ #: (Prism::CallNode node) -> Sig
684
721
  def parse_sig(node)
685
722
  builder = SigBuilder.new(@source, file: @file)
686
723
  builder.current.loc = node_loc(node)
@@ -689,7 +726,7 @@ module RBI
689
726
  builder.current
690
727
  end
691
728
 
692
- sig { params(node: T.any(Prism::ConstantWriteNode, Prism::ConstantPathWriteNode)).returns(T.nilable(Struct)) }
729
+ #: ((Prism::ConstantWriteNode | Prism::ConstantPathWriteNode) node) -> Struct?
693
730
  def parse_struct(node)
694
731
  send = node.value
695
732
  return unless send.is_a?(Prism::CallNode)
@@ -700,7 +737,7 @@ module RBI
700
737
  return unless node_string(recv) =~ /(::)?Struct/
701
738
 
702
739
  members = []
703
- keyword_init = T.let(false, T::Boolean)
740
+ keyword_init = false #: bool
704
741
 
705
742
  args = send.arguments
706
743
  if args.is_a?(Prism::ArgumentsNode)
@@ -737,7 +774,7 @@ module RBI
737
774
  struct
738
775
  end
739
776
 
740
- sig { params(send: Prism::CallNode).void }
777
+ #: (Prism::CallNode send) -> void
741
778
  def parse_tstruct_field(send)
742
779
  args = send.arguments
743
780
  return unless args.is_a?(Prism::ArgumentsNode)
@@ -750,7 +787,7 @@ module RBI
750
787
  type = node_string!(type_arg)
751
788
  loc = node_loc(send)
752
789
  comments = node_comments(send)
753
- default_value = T.let(nil, T.nilable(String))
790
+ default_value = nil #: String?
754
791
 
755
792
  rest.each do |arg|
756
793
  next unless arg.is_a?(Prism::KeywordHashNode)
@@ -774,7 +811,7 @@ module RBI
774
811
  end
775
812
  end
776
813
 
777
- sig { params(name: String, node: Prism::Node).returns(Visibility) }
814
+ #: (String name, Prism::Node node) -> Visibility
778
815
  def parse_visibility(name, node)
779
816
  case name
780
817
  when "public"
@@ -788,7 +825,7 @@ module RBI
788
825
  end
789
826
  end
790
827
 
791
- sig { void }
828
+ #: -> void
792
829
  def separate_header_comments
793
830
  current_scope.nodes.dup.each do |child_node|
794
831
  break unless child_node.is_a?(Comment) || child_node.is_a?(BlankLine)
@@ -798,7 +835,7 @@ module RBI
798
835
  end
799
836
  end
800
837
 
801
- sig { void }
838
+ #: -> void
802
839
  def set_root_tree_loc
803
840
  first_loc = tree.nodes.first&.loc
804
841
  last_loc = tree.nodes.last&.loc
@@ -812,26 +849,38 @@ module RBI
812
849
  )
813
850
  end
814
851
 
815
- sig { params(node: T.nilable(Prism::Node)).returns(T::Boolean) }
852
+ #: (Prism::Node? node) -> bool
816
853
  def type_variable_definition?(node)
817
854
  node.is_a?(Prism::CallNode) && (node.message == "type_member" || node.message == "type_template")
818
855
  end
856
+
857
+ #: (Prism::Node? node) -> bool
858
+ def t_enum_value?(node)
859
+ return false unless current_scope.is_a?(TEnumBlock)
860
+
861
+ return false unless node.is_a?(Prism::ConstantWriteNode)
862
+
863
+ value = node.value
864
+ return false unless value.is_a?(Prism::CallNode)
865
+ return false unless value.message == "new"
866
+
867
+ true
868
+ end
819
869
  end
820
870
 
821
871
  class SigBuilder < Visitor
822
- extend T::Sig
823
-
824
- sig { returns(Sig) }
872
+ #: Sig
825
873
  attr_reader :current
826
874
 
827
- sig { params(content: String, file: String).void }
875
+ #: (String content, file: String) -> void
828
876
  def initialize(content, file:)
829
877
  super
830
878
 
831
- @current = T.let(Sig.new, Sig)
879
+ @current = Sig.new #: Sig
832
880
  end
833
881
 
834
- sig { override.params(node: Prism::CallNode).void }
882
+ # @override
883
+ #: (Prism::CallNode node) -> void
835
884
  def visit_call_node(node)
836
885
  case node.message
837
886
  when "sig"
@@ -892,7 +941,8 @@ module RBI
892
941
  visit(node.block)
893
942
  end
894
943
 
895
- sig { override.params(node: Prism::AssocNode).void }
944
+ # @override
945
+ #: (Prism::AssocNode node) -> void
896
946
  def visit_assoc_node(node)
897
947
  @current.params << SigParam.new(
898
948
  node_string!(node.key).delete_suffix(":"),
@@ -900,5 +950,57 @@ module RBI
900
950
  )
901
951
  end
902
952
  end
953
+
954
+ class HeredocLocationVisitor < Prism::Visitor
955
+ #: (Prism::Source source, Integer begin_offset, Integer end_offset) -> void
956
+ def initialize(source, begin_offset, end_offset)
957
+ super()
958
+ @source = source
959
+ @begin_offset = begin_offset
960
+ @end_offset = end_offset
961
+ @offset_last_newline = false #: bool
962
+ end
963
+
964
+ #: (Prism::StringNode node) -> void
965
+ def visit_string_node(node)
966
+ return unless node.heredoc?
967
+
968
+ closing_loc = node.closing_loc
969
+ return unless closing_loc
970
+
971
+ handle_string_node(node)
972
+ end
973
+
974
+ #: (Prism::InterpolatedStringNode node) -> void
975
+ def visit_interpolated_string_node(node)
976
+ return super unless node.heredoc?
977
+
978
+ closing_loc = node.closing_loc
979
+ return super unless closing_loc
980
+
981
+ handle_string_node(node)
982
+ end
983
+
984
+ #: -> Prism::Location
985
+ def location
986
+ Prism::Location.new(
987
+ @source,
988
+ @begin_offset,
989
+ @end_offset - @begin_offset - (@offset_last_newline ? 1 : 0),
990
+ )
991
+ end
992
+
993
+ private
994
+
995
+ #: (Prism::StringNode | Prism::InterpolatedStringNode node) -> void
996
+ def handle_string_node(node)
997
+ closing_loc = node.closing_loc #: as !nil
998
+
999
+ if closing_loc.end_offset > @end_offset
1000
+ @end_offset = closing_loc.end_offset
1001
+ @offset_last_newline = true if node.closing_loc&.slice&.end_with?("\n")
1002
+ end
1003
+ end
1004
+ end
903
1005
  end
904
1006
  end