rbi 0.2.2 → 0.3.3

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,66 @@ 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
151
+ end
152
+
153
+ #: (Prism::Node? node) -> bool
154
+ def self?(node)
155
+ node.is_a?(Prism::SelfNode)
156
+ end
157
+
158
+ #: (Prism::Node? node) -> bool
159
+ def t_sig_without_runtime?(node)
160
+ !!(node.is_a?(Prism::ConstantPathNode) && node_string(node) =~ /(::)?T::Sig::WithoutRuntime/)
150
161
  end
151
162
  end
152
163
 
153
164
  class TreeBuilder < Visitor
154
- extend T::Sig
155
-
156
- sig { returns(Tree) }
165
+ #: Tree
157
166
  attr_reader :tree
158
167
 
159
- sig { returns(T.nilable(Prism::Node)) }
168
+ #: Prism::Node?
160
169
  attr_reader :last_node
161
170
 
162
- sig { params(source: String, comments: T::Array[Prism::Comment], file: String).void }
171
+ #: (String source, comments: Array[Prism::Comment], file: String) -> void
163
172
  def initialize(source, comments:, file:)
164
173
  super(source, file: file)
165
174
 
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)
175
+ @comments_by_line = comments.to_h { |c| [c.location.start_line, c] } #: Hash[Integer, Prism::Comment]
176
+ @tree = Tree.new #: Tree
168
177
 
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])
178
+ @scopes_stack = [@tree] #: Array[Tree]
179
+ @last_node = nil #: Prism::Node?
180
+ @last_sigs = [] #: Array[RBI::Sig]
172
181
  end
173
182
 
174
- sig { override.params(node: Prism::ClassNode).void }
183
+ # @override
184
+ #: (Prism::ClassNode node) -> void
175
185
  def visit_class_node(node)
176
186
  @last_node = node
177
187
  superclass_name = node_string(node.superclass)
@@ -206,54 +216,78 @@ module RBI
206
216
  @last_node = nil
207
217
  end
208
218
 
209
- sig { override.params(node: Prism::ConstantWriteNode).void }
219
+ # @override
220
+ #: (Prism::ConstantWriteNode node) -> void
210
221
  def visit_constant_write_node(node)
211
222
  @last_node = node
212
223
  visit_constant_assign(node)
213
224
  @last_node = nil
214
225
  end
215
226
 
216
- sig { override.params(node: Prism::ConstantPathWriteNode).void }
227
+ # @override
228
+ #: (Prism::ConstantPathWriteNode node) -> void
217
229
  def visit_constant_path_write_node(node)
218
230
  @last_node = node
219
231
  visit_constant_assign(node)
220
232
  @last_node = nil
221
233
  end
222
234
 
223
- sig { params(node: T.any(Prism::ConstantWriteNode, Prism::ConstantPathWriteNode)).void }
235
+ #: ((Prism::ConstantWriteNode | Prism::ConstantPathWriteNode) node) -> void
224
236
  def visit_constant_assign(node)
225
237
  struct = parse_struct(node)
226
238
 
227
239
  current_scope << if struct
228
240
  struct
229
- elsif type_variable_definition?(node.value)
230
- TypeMember.new(
241
+ elsif t_enum_value?(node)
242
+ TEnumValue.new(
231
243
  case node
232
244
  when Prism::ConstantWriteNode
233
245
  node.name.to_s
234
246
  when Prism::ConstantPathWriteNode
235
247
  node_string!(node.target)
236
248
  end,
237
- node_string!(node.value),
238
249
  loc: node_loc(node),
239
250
  comments: node_comments(node),
240
251
  )
241
252
  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),
253
+ adjusted_node_location = adjust_prism_location_for_heredoc(node)
254
+
255
+ adjusted_value_location = Prism::Location.new(
256
+ node.value.location.send(:source),
257
+ node.value.location.start_offset,
258
+ adjusted_node_location.end_offset - node.value.location.start_offset,
252
259
  )
260
+
261
+ if type_variable_definition?(node.value)
262
+ TypeMember.new(
263
+ case node
264
+ when Prism::ConstantWriteNode
265
+ node.name.to_s
266
+ when Prism::ConstantPathWriteNode
267
+ node_string!(node.target)
268
+ end,
269
+ adjusted_value_location.slice,
270
+ loc: Loc.from_prism(@file, adjusted_node_location),
271
+ comments: node_comments(node),
272
+ )
273
+ else
274
+ Const.new(
275
+ case node
276
+ when Prism::ConstantWriteNode
277
+ node.name.to_s
278
+ when Prism::ConstantPathWriteNode
279
+ node_string!(node.target)
280
+ end,
281
+ adjusted_value_location.slice,
282
+ loc: Loc.from_prism(@file, adjusted_node_location),
283
+ comments: node_comments(node),
284
+ )
285
+ end
253
286
  end
254
287
  end
255
288
 
256
- sig { override.params(node: Prism::DefNode).void }
289
+ # @override
290
+ #: (Prism::DefNode node) -> void
257
291
  def visit_def_node(node)
258
292
  @last_node = node
259
293
 
@@ -274,7 +308,8 @@ module RBI
274
308
  @last_node = nil
275
309
  end
276
310
 
277
- sig { override.params(node: Prism::ModuleNode).void }
311
+ # @override
312
+ #: (Prism::ModuleNode node) -> void
278
313
  def visit_module_node(node)
279
314
  @last_node = node
280
315
  scope = Module.new(
@@ -292,7 +327,8 @@ module RBI
292
327
  @last_node = nil
293
328
  end
294
329
 
295
- sig { override.params(node: Prism::ProgramNode).void }
330
+ # @override
331
+ #: (Prism::ProgramNode node) -> void
296
332
  def visit_program_node(node)
297
333
  @last_node = node
298
334
  super
@@ -303,7 +339,8 @@ module RBI
303
339
  @last_node = nil
304
340
  end
305
341
 
306
- sig { override.params(node: Prism::SingletonClassNode).void }
342
+ # @override
343
+ #: (Prism::SingletonClassNode node) -> void
307
344
  def visit_singleton_class_node(node)
308
345
  @last_node = node
309
346
  scope = SingletonClass.new(
@@ -320,7 +357,7 @@ module RBI
320
357
  @last_node = nil
321
358
  end
322
359
 
323
- sig { params(node: Prism::CallNode).void }
360
+ #: (Prism::CallNode node) -> void
324
361
  def visit_call_node(node)
325
362
  @last_node = node
326
363
  message = node.name.to_s
@@ -451,11 +488,6 @@ module RBI
451
488
  loc: node_loc(node),
452
489
  comments: node_comments(node),
453
490
  )
454
- else
455
- raise ParseError.new(
456
- "Unexpected token `#{node.message}` before `#{last_node&.string&.strip}`",
457
- node_loc(node),
458
- )
459
491
  end
460
492
  else
461
493
  current_scope << parse_visibility(node.name.to_s, node)
@@ -483,6 +515,10 @@ module RBI
483
515
  comments: node_comments(node),
484
516
  )
485
517
  when "sig"
518
+ unless node.receiver.nil? || self?(node.receiver) || t_sig_without_runtime?(node.receiver)
519
+ @last_node = nil
520
+ return
521
+ end
486
522
  @last_sigs << parse_sig(node)
487
523
  else
488
524
  current_scope << Send.new(
@@ -499,7 +535,7 @@ module RBI
499
535
  private
500
536
 
501
537
  # Collect all the remaining comments within a node
502
- sig { params(node: Prism::Node).void }
538
+ #: (Prism::Node node) -> void
503
539
  def collect_dangling_comments(node)
504
540
  first_line = node.location.start_line
505
541
  last_line = node.location.end_line
@@ -517,9 +553,9 @@ module RBI
517
553
  end
518
554
 
519
555
  # Collect all the remaining comments after visiting the tree
520
- sig { void }
556
+ #: -> void
521
557
  def collect_orphan_comments
522
- last_line = T.let(nil, T.nilable(Integer))
558
+ last_line = nil #: Integer?
523
559
  last_node_end = @tree.nodes.last&.loc&.end_line
524
560
 
525
561
  @comments_by_line.each do |line, comment|
@@ -540,21 +576,21 @@ module RBI
540
576
  end
541
577
  end
542
578
 
543
- sig { returns(Tree) }
579
+ #: -> Tree
544
580
  def current_scope
545
- T.must(@scopes_stack.last) # Should never be nil since we create a Tree as the root
581
+ @scopes_stack.last #: as !nil # Should never be nil since we create a Tree as the root
546
582
  end
547
583
 
548
- sig { returns(T::Array[Sig]) }
584
+ #: -> Array[Sig]
549
585
  def current_sigs
550
586
  sigs = @last_sigs.dup
551
587
  @last_sigs.clear
552
588
  sigs
553
589
  end
554
590
 
555
- sig { params(sigs: T::Array[Sig]).returns(T::Array[Comment]) }
591
+ #: (Array[Sig] sigs) -> Array[Comment]
556
592
  def detach_comments_from_sigs(sigs)
557
- comments = T.let([], T::Array[Comment])
593
+ comments = [] #: Array[Comment]
558
594
 
559
595
  sigs.each do |sig|
560
596
  comments += sig.comments.dup
@@ -564,7 +600,7 @@ module RBI
564
600
  comments
565
601
  end
566
602
 
567
- sig { params(node: Prism::Node).returns(T::Array[Comment]) }
603
+ #: (Prism::Node node) -> Array[Comment]
568
604
  def node_comments(node)
569
605
  comments = []
570
606
 
@@ -582,14 +618,22 @@ module RBI
582
618
  comments
583
619
  end
584
620
 
585
- sig { params(node: Prism::Comment).returns(Comment) }
621
+ #: (Prism::Comment node) -> Comment
586
622
  def parse_comment(node)
587
- Comment.new(node.location.slice.gsub(/^# ?/, "").rstrip, loc: Loc.from_prism(@file, node.location))
623
+ loc = Loc.from_prism(@file, node.location)
624
+ string = node.location.slice
625
+ # We also ignore RDoc directives such as `:nodoc:`
626
+ # See https://ruby.github.io/rdoc/RDoc/MarkupReference.html#class-RDoc::MarkupReference-label-Directives
627
+ if string.start_with?("#:") && !(string =~ /^#:[a-z_]+:/)
628
+ RBSComment.new(string.gsub(/^#: ?/, "").rstrip, loc: loc)
629
+ else
630
+ Comment.new(string.gsub(/^# ?/, "").rstrip, loc: loc)
631
+ end
588
632
  end
589
633
 
590
- sig { params(node: T.nilable(Prism::Node)).returns(T::Array[Arg]) }
634
+ #: (Prism::Node? node) -> Array[Arg]
591
635
  def parse_send_args(node)
592
- args = T.let([], T::Array[Arg])
636
+ args = [] #: Array[Arg]
593
637
  return args unless node.is_a?(Prism::ArgumentsNode)
594
638
 
595
639
  node.arguments.each do |arg|
@@ -600,18 +644,20 @@ module RBI
600
644
 
601
645
  args << KwArg.new(
602
646
  node_string!(assoc.key).delete_suffix(":"),
603
- T.must(node_string(assoc.value)),
647
+ node_string(assoc.value), #: as !nil
604
648
  )
605
649
  end
606
650
  else
607
- args << Arg.new(T.must(node_string(arg)))
651
+ args << Arg.new(
652
+ node_string(arg), #: as !nil
653
+ )
608
654
  end
609
655
  end
610
656
 
611
657
  args
612
658
  end
613
659
 
614
- sig { params(node: T.nilable(Prism::Node)).returns(T::Array[Param]) }
660
+ #: (Prism::Node? node) -> Array[Param]
615
661
  def parse_params(node)
616
662
  params = []
617
663
  return params unless node.is_a?(Prism::ParametersNode)
@@ -685,7 +731,7 @@ module RBI
685
731
  params
686
732
  end
687
733
 
688
- sig { params(node: Prism::CallNode).returns(Sig) }
734
+ #: (Prism::CallNode node) -> Sig
689
735
  def parse_sig(node)
690
736
  builder = SigBuilder.new(@source, file: @file)
691
737
  builder.current.loc = node_loc(node)
@@ -694,7 +740,7 @@ module RBI
694
740
  builder.current
695
741
  end
696
742
 
697
- sig { params(node: T.any(Prism::ConstantWriteNode, Prism::ConstantPathWriteNode)).returns(T.nilable(Struct)) }
743
+ #: ((Prism::ConstantWriteNode | Prism::ConstantPathWriteNode) node) -> Struct?
698
744
  def parse_struct(node)
699
745
  send = node.value
700
746
  return unless send.is_a?(Prism::CallNode)
@@ -705,7 +751,7 @@ module RBI
705
751
  return unless node_string(recv) =~ /(::)?Struct/
706
752
 
707
753
  members = []
708
- keyword_init = T.let(false, T::Boolean)
754
+ keyword_init = false #: bool
709
755
 
710
756
  args = send.arguments
711
757
  if args.is_a?(Prism::ArgumentsNode)
@@ -722,8 +768,6 @@ module RBI
722
768
 
723
769
  keyword_init = val == "true" if key == "keyword_init:"
724
770
  end
725
- else
726
- raise ParseError.new("Unexpected node type `#{arg.class}`", node_loc(arg))
727
771
  end
728
772
  end
729
773
  end
@@ -744,7 +788,7 @@ module RBI
744
788
  struct
745
789
  end
746
790
 
747
- sig { params(send: Prism::CallNode).void }
791
+ #: (Prism::CallNode send) -> void
748
792
  def parse_tstruct_field(send)
749
793
  args = send.arguments
750
794
  return unless args.is_a?(Prism::ArgumentsNode)
@@ -757,7 +801,7 @@ module RBI
757
801
  type = node_string!(type_arg)
758
802
  loc = node_loc(send)
759
803
  comments = node_comments(send)
760
- default_value = T.let(nil, T.nilable(String))
804
+ default_value = nil #: String?
761
805
 
762
806
  rest.each do |arg|
763
807
  next unless arg.is_a?(Prism::KeywordHashNode)
@@ -781,7 +825,7 @@ module RBI
781
825
  end
782
826
  end
783
827
 
784
- sig { params(name: String, node: Prism::Node).returns(Visibility) }
828
+ #: (String name, Prism::Node node) -> Visibility
785
829
  def parse_visibility(name, node)
786
830
  case name
787
831
  when "public"
@@ -795,7 +839,7 @@ module RBI
795
839
  end
796
840
  end
797
841
 
798
- sig { void }
842
+ #: -> void
799
843
  def separate_header_comments
800
844
  current_scope.nodes.dup.each do |child_node|
801
845
  break unless child_node.is_a?(Comment) || child_node.is_a?(BlankLine)
@@ -805,7 +849,7 @@ module RBI
805
849
  end
806
850
  end
807
851
 
808
- sig { void }
852
+ #: -> void
809
853
  def set_root_tree_loc
810
854
  first_loc = tree.nodes.first&.loc
811
855
  last_loc = tree.nodes.last&.loc
@@ -819,29 +863,43 @@ module RBI
819
863
  )
820
864
  end
821
865
 
822
- sig { params(node: T.nilable(Prism::Node)).returns(T::Boolean) }
866
+ #: (Prism::Node? node) -> bool
823
867
  def type_variable_definition?(node)
824
868
  node.is_a?(Prism::CallNode) && (node.message == "type_member" || node.message == "type_template")
825
869
  end
870
+
871
+ #: (Prism::Node? node) -> bool
872
+ def t_enum_value?(node)
873
+ return false unless current_scope.is_a?(TEnumBlock)
874
+
875
+ return false unless node.is_a?(Prism::ConstantWriteNode)
876
+
877
+ value = node.value
878
+ return false unless value.is_a?(Prism::CallNode)
879
+ return false unless value.message == "new"
880
+
881
+ true
882
+ end
826
883
  end
827
884
 
828
885
  class SigBuilder < Visitor
829
- extend T::Sig
830
-
831
- sig { returns(Sig) }
886
+ #: Sig
832
887
  attr_reader :current
833
888
 
834
- sig { params(content: String, file: String).void }
889
+ #: (String content, file: String) -> void
835
890
  def initialize(content, file:)
836
891
  super
837
892
 
838
- @current = T.let(Sig.new, Sig)
893
+ @current = Sig.new #: Sig
839
894
  end
840
895
 
841
- sig { override.params(node: Prism::CallNode).void }
896
+ # @override
897
+ #: (Prism::CallNode node) -> void
842
898
  def visit_call_node(node)
843
899
  case node.message
844
900
  when "sig"
901
+ @current.without_runtime = t_sig_without_runtime?(node.receiver)
902
+
845
903
  args = node.arguments
846
904
  if args.is_a?(Prism::ArgumentsNode)
847
905
  args.arguments.each do |arg|
@@ -858,6 +916,22 @@ module RBI
858
916
  end
859
917
  when "override"
860
918
  @current.is_override = true
919
+
920
+ args = node.arguments&.arguments
921
+
922
+ keywords_hash = args
923
+ &.grep(Prism::KeywordHashNode)
924
+ &.first
925
+
926
+ allow_incompatible_override = keywords_hash
927
+ &.elements
928
+ &.any? do |assoc|
929
+ assoc.is_a?(Prism::AssocNode) &&
930
+ node_string(assoc.key) == "allow_incompatible:" &&
931
+ node_string(assoc.value) == "true"
932
+ end
933
+
934
+ @current.allow_incompatible_override = !!allow_incompatible_override
861
935
  when "overridable"
862
936
  @current.is_overridable = true
863
937
  when "params"
@@ -883,7 +957,8 @@ module RBI
883
957
  visit(node.block)
884
958
  end
885
959
 
886
- sig { override.params(node: Prism::AssocNode).void }
960
+ # @override
961
+ #: (Prism::AssocNode node) -> void
887
962
  def visit_assoc_node(node)
888
963
  @current.params << SigParam.new(
889
964
  node_string!(node.key).delete_suffix(":"),
@@ -891,5 +966,59 @@ module RBI
891
966
  )
892
967
  end
893
968
  end
969
+
970
+ class HeredocLocationVisitor < Prism::Visitor
971
+ #: (Prism::Source source, Integer begin_offset, Integer end_offset) -> void
972
+ def initialize(source, begin_offset, end_offset)
973
+ super()
974
+ @source = source
975
+ @begin_offset = begin_offset
976
+ @end_offset = end_offset
977
+ @offset_last_newline = false #: bool
978
+ end
979
+
980
+ # @override
981
+ #: (Prism::StringNode node) -> void
982
+ def visit_string_node(node)
983
+ return unless node.heredoc?
984
+
985
+ closing_loc = node.closing_loc
986
+ return unless closing_loc
987
+
988
+ handle_string_node(node)
989
+ end
990
+
991
+ # @override
992
+ #: (Prism::InterpolatedStringNode node) -> void
993
+ def visit_interpolated_string_node(node)
994
+ return super unless node.heredoc?
995
+
996
+ closing_loc = node.closing_loc
997
+ return super unless closing_loc
998
+
999
+ handle_string_node(node)
1000
+ end
1001
+
1002
+ #: -> Prism::Location
1003
+ def location
1004
+ Prism::Location.new(
1005
+ @source,
1006
+ @begin_offset,
1007
+ @end_offset - @begin_offset - (@offset_last_newline ? 1 : 0),
1008
+ )
1009
+ end
1010
+
1011
+ private
1012
+
1013
+ #: (Prism::StringNode | Prism::InterpolatedStringNode node) -> void
1014
+ def handle_string_node(node)
1015
+ closing_loc = node.closing_loc #: as !nil
1016
+
1017
+ if closing_loc.end_offset > @end_offset
1018
+ @end_offset = closing_loc.end_offset
1019
+ @offset_last_newline = true if node.closing_loc&.slice&.end_with?("\n")
1020
+ end
1021
+ end
1022
+ end
894
1023
  end
895
1024
  end