prism 1.8.1 → 2.0.0.rc1

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -2
  3. data/Makefile +41 -12
  4. data/README.md +35 -97
  5. data/config.yml +174 -163
  6. data/docs/build_system.md +2 -2
  7. data/docs/configuration.md +5 -4
  8. data/docs/encoding.md +2 -2
  9. data/docs/fuzzing.md +4 -11
  10. data/docs/javascript.md +19 -4
  11. data/docs/mapping.md +1 -1
  12. data/docs/releasing.md +77 -5
  13. data/docs/relocation.md +2 -4
  14. data/docs/ripper_translation.md +8 -17
  15. data/docs/serialization.md +21 -24
  16. data/docs/testing.md +46 -29
  17. data/ext/prism/api_node.c +1461 -1401
  18. data/ext/prism/extconf.rb +3 -4
  19. data/ext/prism/extension.c +778 -422
  20. data/ext/prism/extension.h +3 -3
  21. data/include/prism/arena.h +37 -0
  22. data/include/prism/ast.h +2729 -612
  23. data/include/prism/buffer.h +52 -0
  24. data/include/prism/comments.h +55 -0
  25. data/include/prism/compiler/accel.h +24 -0
  26. data/include/prism/compiler/align.h +36 -0
  27. data/include/prism/compiler/assume.h +25 -0
  28. data/include/prism/compiler/exported.h +24 -0
  29. data/include/prism/compiler/fallthrough.h +22 -0
  30. data/include/prism/compiler/filesystem.h +32 -0
  31. data/include/prism/compiler/flex_array.h +19 -0
  32. data/include/prism/compiler/force_inline.h +21 -0
  33. data/include/prism/compiler/format.h +25 -0
  34. data/include/prism/compiler/inline.h +17 -0
  35. data/include/prism/compiler/nodiscard.h +22 -0
  36. data/include/prism/compiler/nonnull.h +18 -0
  37. data/include/prism/compiler/unused.h +18 -0
  38. data/include/prism/constant_pool.h +81 -0
  39. data/include/prism/diagnostic.h +38 -405
  40. data/include/prism/errors_format.h +53 -0
  41. data/include/prism/excludes.h +29 -0
  42. data/include/prism/integer.h +41 -0
  43. data/include/prism/internal/allocator.h +68 -0
  44. data/include/prism/internal/allocator_debug.h +88 -0
  45. data/include/prism/internal/arena.h +108 -0
  46. data/include/prism/internal/bit.h +42 -0
  47. data/include/prism/internal/buffer.h +91 -0
  48. data/include/prism/internal/char.h +145 -0
  49. data/include/prism/internal/comments.h +53 -0
  50. data/include/prism/internal/constant_pool.h +152 -0
  51. data/include/prism/internal/diagnostic.h +389 -0
  52. data/include/prism/{encoding.h → internal/encoding.h} +27 -68
  53. data/include/prism/internal/integer.h +68 -0
  54. data/include/prism/internal/isinf.h +16 -0
  55. data/include/prism/internal/line_offset_list.h +34 -0
  56. data/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  57. data/include/prism/internal/magic_comments.h +23 -0
  58. data/include/prism/internal/memchr.h +15 -0
  59. data/include/prism/internal/node.h +38 -0
  60. data/include/prism/internal/options.h +232 -0
  61. data/include/prism/internal/parser.h +984 -0
  62. data/include/prism/internal/regexp.h +26 -0
  63. data/include/prism/internal/serialize.h +39 -0
  64. data/include/prism/internal/source.h +72 -0
  65. data/include/prism/{static_literals.h → internal/static_literals.h} +26 -47
  66. data/include/prism/internal/stringy.h +35 -0
  67. data/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  68. data/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  69. data/include/prism/internal/tokens.h +11 -0
  70. data/include/prism/json.h +32 -0
  71. data/include/prism/line_offset_list.h +61 -0
  72. data/include/prism/magic_comments.h +35 -0
  73. data/include/prism/node.h +18 -53
  74. data/include/prism/options.h +146 -280
  75. data/include/prism/parser.h +336 -868
  76. data/include/prism/prettyprint.h +6 -9
  77. data/include/prism/serialize.h +115 -0
  78. data/include/prism/source.h +155 -0
  79. data/include/prism/stream.h +28 -0
  80. data/include/prism/string_query.h +63 -0
  81. data/include/prism/stringy.h +72 -0
  82. data/include/prism/version.h +13 -4
  83. data/include/prism.h +43 -320
  84. data/lib/prism/compiler.rb +319 -305
  85. data/lib/prism/desugar_compiler.rb +105 -34
  86. data/lib/prism/dispatcher.rb +646 -473
  87. data/lib/prism/dot_visitor.rb +592 -511
  88. data/lib/prism/dsl.rb +391 -25
  89. data/lib/prism/ffi.rb +252 -90
  90. data/lib/prism/inspect_visitor.rb +357 -325
  91. data/lib/prism/lex_compat.rb +311 -228
  92. data/lib/prism/mutation_compiler.rb +313 -305
  93. data/lib/prism/node.rb +20284 -7038
  94. data/lib/prism/node_ext.rb +180 -187
  95. data/lib/prism/node_find.rb +215 -0
  96. data/lib/prism/parse_result/errors.rb +7 -1
  97. data/lib/prism/parse_result/newlines.rb +276 -8
  98. data/lib/prism/parse_result.rb +462 -139
  99. data/lib/prism/pattern.rb +65 -20
  100. data/lib/prism/reflection.rb +23 -12
  101. data/lib/prism/relocation.rb +149 -87
  102. data/lib/prism/serialize.rb +3442 -536
  103. data/lib/prism/string_query.rb +16 -1
  104. data/lib/prism/translation/parser/builder.rb +10 -2
  105. data/lib/prism/translation/parser/compiler.rb +44 -52
  106. data/lib/prism/translation/parser/lexer.rb +41 -92
  107. data/lib/prism/translation/parser.rb +1 -1
  108. data/lib/prism/translation/parser_current.rb +1 -1
  109. data/lib/prism/translation/ripper/filter.rb +53 -0
  110. data/lib/prism/translation/ripper/lexer.rb +92 -5
  111. data/lib/prism/translation/ripper/sexp.rb +2 -10
  112. data/lib/prism/translation/ripper/shim.rb +2 -0
  113. data/lib/prism/translation/ripper.rb +1005 -224
  114. data/lib/prism/translation/ruby_parser.rb +73 -316
  115. data/lib/prism/translation.rb +2 -0
  116. data/lib/prism/visitor.rb +632 -5
  117. data/lib/prism.rb +79 -22
  118. data/prism.gemspec +123 -61
  119. data/rbi/generated/prism/compiler.rbi +489 -0
  120. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  121. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  122. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  123. data/rbi/generated/prism/dsl.rbi +751 -0
  124. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  125. data/rbi/generated/prism/lex_compat.rbi +162 -0
  126. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  127. data/rbi/generated/prism/node.rbi +13681 -0
  128. data/rbi/generated/prism/node_ext.rbi +259 -0
  129. data/rbi/generated/prism/node_find.rbi +79 -0
  130. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  131. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  132. data/rbi/generated/prism/parse_result.rbi +764 -0
  133. data/rbi/generated/prism/pattern.rbi +134 -0
  134. data/rbi/generated/prism/reflection.rbi +89 -0
  135. data/rbi/generated/prism/relocation.rbi +329 -0
  136. data/rbi/generated/prism/serialize.rbi +181 -0
  137. data/rbi/generated/prism/string_query.rbi +36 -0
  138. data/rbi/generated/prism/translation.rbi +7 -0
  139. data/rbi/generated/prism/visitor.rbi +650 -0
  140. data/rbi/generated/prism.rbi +86 -0
  141. data/rbi/prism/translation/parser_versions.rbi +5 -0
  142. data/rbi/rubyvm/node_find.rbi +14 -0
  143. data/sig/generated/prism/compiler.rbs +492 -0
  144. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  145. data/sig/generated/prism/dispatcher.rbs +1292 -0
  146. data/sig/generated/prism/dot_visitor.rbs +635 -0
  147. data/sig/generated/prism/dsl.rbs +928 -0
  148. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  149. data/sig/generated/prism/lex_compat.rbs +166 -0
  150. data/sig/{prism → generated/prism}/mutation_compiler.rbs +309 -4
  151. data/sig/generated/prism/node.rbs +20160 -0
  152. data/sig/generated/prism/node_ext.rbs +290 -0
  153. data/sig/generated/prism/node_find.rbs +90 -0
  154. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  155. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  156. data/sig/generated/prism/parse_result.rbs +859 -0
  157. data/sig/generated/prism/pattern.rbs +156 -0
  158. data/sig/generated/prism/reflection.rbs +101 -0
  159. data/sig/generated/prism/relocation.rbs +428 -0
  160. data/sig/generated/prism/serialize.rbs +195 -0
  161. data/sig/generated/prism/string_query.rbs +36 -0
  162. data/sig/generated/prism/translation.rbs +8 -0
  163. data/sig/generated/prism/visitor.rbs +1111 -0
  164. data/sig/generated/prism.rbs +84 -0
  165. data/sig/manifest.yaml +2 -0
  166. data/src/arena.c +117 -0
  167. data/src/{util/pm_buffer.c → buffer.c} +44 -21
  168. data/src/{util/pm_char.c → char.c} +28 -57
  169. data/src/comments.c +454 -0
  170. data/src/constant_pool.c +406 -0
  171. data/src/diagnostic.c +93 -65
  172. data/src/encoding.c +8 -3
  173. data/src/errors_format.c +519 -0
  174. data/src/{util/pm_integer.c → integer.c} +33 -22
  175. data/src/json.c +5733 -0
  176. data/src/line_offset_list.c +100 -0
  177. data/src/list.c +24 -0
  178. data/src/{util/pm_memchr.c → memchr.c} +8 -6
  179. data/src/node.c +3504 -7674
  180. data/src/options.c +162 -49
  181. data/src/parser.c +349 -0
  182. data/src/prettyprint.c +421 -354
  183. data/src/prism.c +8035 -7019
  184. data/src/regexp.c +961 -45
  185. data/src/serialize.c +459 -424
  186. data/src/source.c +518 -0
  187. data/src/static_literals.c +111 -43
  188. data/src/string_query.c +166 -0
  189. data/src/stringy.c +91 -0
  190. data/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  191. data/src/strpbrk.c +447 -0
  192. data/src/{token_type.c → tokens.c} +39 -18
  193. metadata +123 -62
  194. data/ext/prism/api_pack.c +0 -276
  195. data/include/prism/defines.h +0 -260
  196. data/include/prism/pack.h +0 -163
  197. data/include/prism/regexp.h +0 -43
  198. data/include/prism/util/pm_buffer.h +0 -236
  199. data/include/prism/util/pm_char.h +0 -204
  200. data/include/prism/util/pm_constant_pool.h +0 -218
  201. data/include/prism/util/pm_integer.h +0 -130
  202. data/include/prism/util/pm_memchr.h +0 -29
  203. data/include/prism/util/pm_newline_list.h +0 -113
  204. data/include/prism/util/pm_string.h +0 -200
  205. data/lib/prism/lex_ripper.rb +0 -64
  206. data/lib/prism/pack.rb +0 -230
  207. data/lib/prism/parse_result/comments.rb +0 -188
  208. data/rbi/prism/compiler.rbi +0 -12
  209. data/rbi/prism/dsl.rbi +0 -524
  210. data/rbi/prism/inspect_visitor.rbi +0 -12
  211. data/rbi/prism/node.rbi +0 -8747
  212. data/rbi/prism/node_ext.rbi +0 -107
  213. data/rbi/prism/parse_result.rbi +0 -404
  214. data/rbi/prism/reflection.rbi +0 -58
  215. data/rbi/prism/string_query.rbi +0 -12
  216. data/rbi/prism/visitor.rbi +0 -473
  217. data/rbi/prism.rbi +0 -66
  218. data/sig/prism/compiler.rbs +0 -9
  219. data/sig/prism/dispatcher.rbs +0 -19
  220. data/sig/prism/dot_visitor.rbs +0 -6
  221. data/sig/prism/dsl.rbs +0 -351
  222. data/sig/prism/inspect_visitor.rbs +0 -22
  223. data/sig/prism/lex_compat.rbs +0 -10
  224. data/sig/prism/node.rbs +0 -4039
  225. data/sig/prism/node_ext.rbs +0 -149
  226. data/sig/prism/pack.rbs +0 -43
  227. data/sig/prism/parse_result/comments.rbs +0 -38
  228. data/sig/prism/parse_result.rbs +0 -196
  229. data/sig/prism/pattern.rbs +0 -13
  230. data/sig/prism/reflection.rbs +0 -50
  231. data/sig/prism/relocation.rbs +0 -185
  232. data/sig/prism/serialize.rbs +0 -8
  233. data/sig/prism/string_query.rbs +0 -11
  234. data/sig/prism/visitor.rbs +0 -169
  235. data/sig/prism.rbs +0 -258
  236. data/src/pack.c +0 -509
  237. data/src/util/pm_constant_pool.c +0 -342
  238. data/src/util/pm_list.c +0 -49
  239. data/src/util/pm_newline_list.c +0 -125
  240. data/src/util/pm_string.c +0 -381
  241. data/src/util/pm_strpbrk.c +0 -206
@@ -22,22 +22,10 @@ module Prism
22
22
  # - on_comma
23
23
  # - on_ignored_nl
24
24
  # - on_ignored_sp
25
- # - on_kw
26
- # - on_label_end
27
- # - on_lbrace
28
- # - on_lbracket
29
- # - on_lparen
30
25
  # - on_nl
31
- # - on_op
32
26
  # - on_operator_ambiguous
33
- # - on_rbrace
34
- # - on_rbracket
35
- # - on_rparen
36
27
  # - on_semicolon
37
28
  # - on_sp
38
- # - on_symbeg
39
- # - on_tstring_beg
40
- # - on_tstring_end
41
29
  #
42
30
  class Ripper < Compiler
43
31
  # Parses the given Ruby program read from +src+.
@@ -69,7 +57,8 @@ module Prism
69
57
  # [[1, 13], :on_kw, "end", END ]]
70
58
  #
71
59
  def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
72
- result = Prism.lex_compat(src, filepath: filename, line: lineno, version: "current")
60
+ coerced = coerce_source(src)
61
+ result = Prism.lex_compat(coerced, filepath: filename, line: lineno, version: "current", encoding: coerced.encoding)
73
62
 
74
63
  if result.failure? && raise_errors
75
64
  raise SyntaxError, result.errors.first.message
@@ -78,6 +67,34 @@ module Prism
78
67
  end
79
68
  end
80
69
 
70
+ # Tokenizes the Ruby program and returns an array of strings.
71
+ # The +filename+ and +lineno+ arguments are mostly ignored, since the
72
+ # return value is just the tokenized input.
73
+ # By default, this method does not handle syntax errors in +src+,
74
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
75
+ #
76
+ # p Ripper.tokenize("def m(a) nil end")
77
+ # # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"]
78
+ #
79
+ def self.tokenize(...)
80
+ lex(...).map { |token| token[2] }
81
+ end
82
+
83
+ # Mirros the various lex_types that ripper supports
84
+ def self.coerce_source(source) # :nodoc:
85
+ if source.is_a?(IO)
86
+ source.read
87
+ elsif source.respond_to?(:gets)
88
+ src = +""
89
+ while line = source.gets
90
+ src << line
91
+ end
92
+ src
93
+ else
94
+ source.to_str
95
+ end
96
+ end
97
+
81
98
  # This contains a table of all of the parser events and their
82
99
  # corresponding arity.
83
100
  PARSER_EVENT_TABLE = {
@@ -330,7 +347,7 @@ module Prism
330
347
  "__ENCODING__",
331
348
  "__FILE__",
332
349
  "__LINE__"
333
- ]
350
+ ].to_set
334
351
 
335
352
  # A list of all of the Ruby binary operators.
336
353
  BINARY_OPERATORS = [
@@ -355,7 +372,7 @@ module Prism
355
372
  :/,
356
373
  :*,
357
374
  :**
358
- ]
375
+ ].to_set
359
376
 
360
377
  private_constant :KEYWORDS, :BINARY_OPERATORS
361
378
 
@@ -424,10 +441,69 @@ module Prism
424
441
  end
425
442
  end
426
443
 
444
+ autoload :Filter, "prism/translation/ripper/filter"
427
445
  autoload :Lexer, "prism/translation/ripper/lexer"
428
446
  autoload :SexpBuilder, "prism/translation/ripper/sexp"
429
447
  autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
430
448
 
449
+ # Provides optimized access to line and column information.
450
+ # Ripper bounds are mostly accessed in a linear fashion, so
451
+ # we can try a linear scan first and fall back to binary search.
452
+ class LineAndColumnCache # :nodoc:
453
+ # How many should it look ahead/behind before falling back to binary searching.
454
+ WINDOW = 8
455
+ private_constant :WINDOW
456
+
457
+ #: (Source source) -> void
458
+ def initialize(source)
459
+ @source = source
460
+ @offsets = source.offsets
461
+ @hint = 0
462
+ end
463
+
464
+ #: (Integer byte_offset) -> [Integer, Integer]
465
+ def line_and_column(byte_offset)
466
+ @hint = new_hint(byte_offset) || @source.find_line(byte_offset)
467
+ return [@hint + @source.start_line, byte_offset - @offsets[@hint]]
468
+ end
469
+
470
+ private
471
+
472
+ def new_hint(byte_offset)
473
+ if @offsets[@hint] <= byte_offset
474
+ # Same line?
475
+ if (@hint + 1 >= @offsets.size || @offsets[@hint + 1] > byte_offset)
476
+ return @hint
477
+ end
478
+
479
+ # Scan forwards
480
+ limit = [@hint + WINDOW + 1, @offsets.size].min
481
+ idx = @hint + 1
482
+ while idx < limit
483
+ if @offsets[idx] > byte_offset
484
+ return idx - 1
485
+ end
486
+ if @offsets[idx] == byte_offset
487
+ return idx
488
+ end
489
+ idx += 1
490
+ end
491
+ else
492
+ # Scan backwards
493
+ limit = @hint > WINDOW ? @hint - WINDOW : 0
494
+ idx = @hint
495
+ while idx >= limit + 1
496
+ if @offsets[idx - 1] <= byte_offset
497
+ return idx - 1
498
+ end
499
+ idx -= 1
500
+ end
501
+ end
502
+
503
+ nil
504
+ end
505
+ end
506
+
431
507
  # :stopdoc:
432
508
  # This is not part of the public API but used by some gems.
433
509
 
@@ -461,16 +537,17 @@ module Prism
461
537
  # The current line number of the parser.
462
538
  attr_reader :lineno
463
539
 
464
- # The current column number of the parser.
540
+ # The current column in bytes of the parser.
465
541
  attr_reader :column
466
542
 
467
543
  # Create a new Translation::Ripper object with the given source.
468
544
  def initialize(source, filename = "(ripper)", lineno = 1)
469
- @source = source
545
+ @source = Ripper.coerce_source(source)
470
546
  @filename = filename
471
547
  @lineno = lineno
472
548
  @column = 0
473
549
  @result = nil
550
+ @line_and_column_cache = nil
474
551
  end
475
552
 
476
553
  ##########################################################################
@@ -489,7 +566,12 @@ module Prism
489
566
  bounds(location)
490
567
 
491
568
  if comment.is_a?(InlineComment)
492
- on_comment(comment.slice)
569
+ # Inline comments always contain a newline if the line itself contains it
570
+ if result.source.source.bytesize > comment.location.end_offset
571
+ on_comment("#{comment.slice}\n")
572
+ else
573
+ on_comment(comment.slice)
574
+ end
493
575
  else
494
576
  offset = location.start_offset
495
577
  lines = comment.slice.lines
@@ -566,13 +648,28 @@ module Prism
566
648
  end
567
649
  end
568
650
 
651
+ # Return encoding of the source.
652
+ def encoding
653
+ result.source.encoding
654
+ end
655
+
656
+ # Return true if parsed source ended by `__END__`.
657
+ def end_seen?
658
+ !!result.data_loc
659
+ end
660
+
569
661
  ##########################################################################
570
662
  # Visitor methods
571
663
  ##########################################################################
572
664
 
665
+ # :stopdoc:
666
+
573
667
  # alias foo bar
574
668
  # ^^^^^^^^^^^^^
575
669
  def visit_alias_method_node(node)
670
+ bounds(node.keyword_loc)
671
+ on_kw("alias")
672
+
576
673
  new_name = visit(node.new_name)
577
674
  old_name = visit(node.old_name)
578
675
 
@@ -583,6 +680,9 @@ module Prism
583
680
  # alias $foo $bar
584
681
  # ^^^^^^^^^^^^^^^
585
682
  def visit_alias_global_variable_node(node)
683
+ bounds(node.keyword_loc)
684
+ on_kw("alias")
685
+
586
686
  new_name = visit_alias_global_variable_node_value(node.new_name)
587
687
  old_name = visit_alias_global_variable_node_value(node.old_name)
588
688
 
@@ -608,6 +708,10 @@ module Prism
608
708
  # ^^^^^^^^^
609
709
  def visit_alternation_pattern_node(node)
610
710
  left = visit_pattern_node(node.left)
711
+
712
+ bounds(node.operator_loc)
713
+ on_op("|")
714
+
611
715
  right = visit_pattern_node(node.right)
612
716
 
613
717
  bounds(node.location)
@@ -618,7 +722,13 @@ module Prism
618
722
  # parenthesis node that can be used to wrap patterns.
619
723
  private def visit_pattern_node(node)
620
724
  if node.is_a?(ParenthesesNode)
621
- visit(node.body)
725
+ bounds(node.opening_loc)
726
+ on_lparen("(")
727
+ result = visit(node.body)
728
+ bounds(node.closing_loc)
729
+ on_rparen(")")
730
+
731
+ result
622
732
  else
623
733
  visit(node)
624
734
  end
@@ -628,6 +738,14 @@ module Prism
628
738
  # ^^^^^^^
629
739
  def visit_and_node(node)
630
740
  left = visit(node.left)
741
+
742
+ bounds(node.operator_loc)
743
+ if node.operator == "and"
744
+ on_kw("and")
745
+ else
746
+ on_op("&&")
747
+ end
748
+
631
749
  right = visit(node.right)
632
750
 
633
751
  bounds(node.location)
@@ -655,6 +773,8 @@ module Prism
655
773
  previous = element
656
774
  end
657
775
 
776
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
777
+
658
778
  bounds(node.closing_loc)
659
779
  on_tstring_end(node.closing)
660
780
  when /^%i/
@@ -669,11 +789,13 @@ module Prism
669
789
  visit_words_sep(opening_loc, previous, element)
670
790
 
671
791
  bounds(element.location)
672
- elements = on_qsymbols_add(elements, on_tstring_content(element.value))
792
+ elements = on_qsymbols_add(elements, on_tstring_content(element.content))
673
793
 
674
794
  previous = element
675
795
  end
676
796
 
797
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
798
+
677
799
  bounds(node.closing_loc)
678
800
  on_tstring_end(node.closing)
679
801
  when /^%W/
@@ -711,6 +833,8 @@ module Prism
711
833
  previous = element
712
834
  end
713
835
 
836
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
837
+
714
838
  bounds(node.closing_loc)
715
839
  on_tstring_end(node.closing)
716
840
  when /^%I/
@@ -729,7 +853,7 @@ module Prism
729
853
  on_symbols_add(
730
854
  elements,
731
855
  if element.is_a?(SymbolNode)
732
- on_word_add(on_word_new, on_tstring_content(element.value))
856
+ on_word_add(on_word_new, on_tstring_content(element.content))
733
857
  else
734
858
  element.parts.inject(on_word_new) do |word, part|
735
859
  word_part =
@@ -748,6 +872,8 @@ module Prism
748
872
  previous = element
749
873
  end
750
874
 
875
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
876
+
751
877
  bounds(node.closing_loc)
752
878
  on_tstring_end(node.closing)
753
879
  else
@@ -764,15 +890,21 @@ module Prism
764
890
  on_array(elements)
765
891
  end
766
892
 
767
- # Dispatch a words_sep event that contains the space between the elements
893
+ # Dispatch words_sep events that contains the whitespace between the elements
768
894
  # of list literals.
769
895
  private def visit_words_sep(opening_loc, previous, current)
770
- end_offset = (previous.nil? ? opening_loc : previous.location).end_offset
771
- start_offset = current.location.start_offset
772
-
773
- if end_offset != start_offset
774
- bounds(current.location.copy(start_offset: end_offset))
775
- on_words_sep(source.byteslice(end_offset...start_offset))
896
+ start_offset = (previous.nil? ? opening_loc : previous.location).end_offset
897
+ end_offset = current.start_offset
898
+ length = end_offset - start_offset
899
+
900
+ if length > 0
901
+ whitespace = source.byteslice(start_offset, length)
902
+ current_offset = start_offset
903
+ whitespace.each_line do |part|
904
+ bounds(opening_loc.copy(start_offset: current_offset, length: part.bytesize))
905
+ on_words_sep(part)
906
+ current_offset += part.bytesize
907
+ end
776
908
  end
777
909
  end
778
910
 
@@ -798,9 +930,18 @@ module Prism
798
930
  # ^^^^^
799
931
  def visit_array_pattern_node(node)
800
932
  constant = visit(node.constant)
933
+
934
+ if node.opening_loc
935
+ bounds(node.opening_loc)
936
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
937
+ end
938
+
801
939
  requireds = visit_all(node.requireds) if node.requireds.any?
802
940
  rest =
803
941
  if (rest_node = node.rest).is_a?(SplatNode)
942
+ bounds(rest_node.operator_loc)
943
+ on_op("*")
944
+
804
945
  if rest_node.expression.nil?
805
946
  bounds(rest_node.location)
806
947
  on_var_field(nil)
@@ -811,6 +952,10 @@ module Prism
811
952
 
812
953
  posts = visit_all(node.posts) if node.posts.any?
813
954
 
955
+ if node.closing_loc
956
+ bounds(node.closing_loc)
957
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
958
+ end
814
959
  bounds(node.location)
815
960
  on_aryptn(constant, requireds, rest, posts)
816
961
  end
@@ -826,6 +971,12 @@ module Prism
826
971
  # ^^^^
827
972
  def visit_assoc_node(node)
828
973
  key = visit(node.key)
974
+
975
+ if node.operator_loc
976
+ bounds(node.operator_loc)
977
+ on_op("=>")
978
+ end
979
+
829
980
  value = visit(node.value)
830
981
 
831
982
  bounds(node.location)
@@ -838,6 +989,9 @@ module Prism
838
989
  # { **foo }
839
990
  # ^^^^^
840
991
  def visit_assoc_splat_node(node)
992
+ bounds(node.operator_loc)
993
+ on_op("**")
994
+
841
995
  value = visit(node.value)
842
996
 
843
997
  bounds(node.location)
@@ -854,8 +1008,18 @@ module Prism
854
1008
  # begin end
855
1009
  # ^^^^^^^^^
856
1010
  def visit_begin_node(node)
1011
+ if node.begin_keyword_loc
1012
+ bounds(node.begin_keyword_loc)
1013
+ on_kw("begin")
1014
+ end
1015
+
857
1016
  clauses = visit_begin_node_clauses(node.begin_keyword_loc, node, false)
858
1017
 
1018
+ if node.end_keyword_loc
1019
+ bounds(node.end_keyword_loc)
1020
+ on_kw("end")
1021
+ end
1022
+
859
1023
  bounds(node.location)
860
1024
  on_begin(clauses)
861
1025
  end
@@ -867,7 +1031,7 @@ module Prism
867
1031
  on_stmts_add(on_stmts_new, on_void_stmt)
868
1032
  else
869
1033
  body = node.statements.body
870
- body.unshift(nil) if void_stmt?(location, node.statements.body[0].location, allow_newline)
1034
+ body = [nil, *body] if void_stmt?(location, node.statements.body[0].location, allow_newline)
871
1035
 
872
1036
  bounds(node.statements.location)
873
1037
  visit_statements_node_body(body)
@@ -876,12 +1040,15 @@ module Prism
876
1040
  rescue_clause = visit(node.rescue_clause)
877
1041
  else_clause =
878
1042
  unless (else_clause_node = node.else_clause).nil?
1043
+ bounds(else_clause_node.else_keyword_loc)
1044
+ on_kw("else")
1045
+
879
1046
  else_statements =
880
1047
  if else_clause_node.statements.nil?
881
1048
  [nil]
882
1049
  else
883
1050
  body = else_clause_node.statements.body
884
- body.unshift(nil) if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
1051
+ body = [nil, *body] if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
885
1052
  body
886
1053
  end
887
1054
 
@@ -903,7 +1070,7 @@ module Prism
903
1070
  on_bodystmt(visit_statements_node_body([nil]), nil, nil, nil)
904
1071
  when StatementsNode
905
1072
  body = [*node.body]
906
- body.unshift(nil) if void_stmt?(location, body[0].location, allow_newline)
1073
+ body = [nil, *body] if void_stmt?(location, body[0].location, allow_newline)
907
1074
  stmts = visit_statements_node_body(body)
908
1075
 
909
1076
  bounds(node.body.first.location)
@@ -918,6 +1085,8 @@ module Prism
918
1085
  # foo(&bar)
919
1086
  # ^^^^
920
1087
  def visit_block_argument_node(node)
1088
+ bounds(node.operator_loc)
1089
+ on_op("&")
921
1090
  visit(node.expression)
922
1091
  end
923
1092
 
@@ -931,6 +1100,13 @@ module Prism
931
1100
  # Visit a BlockNode.
932
1101
  def visit_block_node(node)
933
1102
  braces = node.opening == "{"
1103
+ bounds(node.opening_loc)
1104
+ if braces
1105
+ on_lbrace("{")
1106
+ else
1107
+ on_kw("do")
1108
+ end
1109
+
934
1110
  parameters = visit(node.parameters)
935
1111
 
936
1112
  body =
@@ -943,7 +1119,7 @@ module Prism
943
1119
  braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
944
1120
  when StatementsNode
945
1121
  stmts = node.body.body
946
- stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
1122
+ stmts = [nil, *stmts] if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
947
1123
  stmts = visit_statements_node_body(stmts)
948
1124
 
949
1125
  bounds(node.body.location)
@@ -954,6 +1130,14 @@ module Prism
954
1130
  raise
955
1131
  end
956
1132
 
1133
+ if braces
1134
+ bounds(node.closing_loc)
1135
+ on_rbrace("}")
1136
+ else
1137
+ bounds(node.closing_loc)
1138
+ on_kw("end")
1139
+ end
1140
+
957
1141
  if braces
958
1142
  bounds(node.location)
959
1143
  on_brace_block(parameters, body)
@@ -966,12 +1150,15 @@ module Prism
966
1150
  # def foo(&bar); end
967
1151
  # ^^^^
968
1152
  def visit_block_parameter_node(node)
1153
+ bounds(node.operator_loc)
1154
+ on_op("&")
1155
+
969
1156
  if node.name_loc.nil?
970
1157
  bounds(node.location)
971
1158
  on_blockarg(nil)
972
1159
  else
973
1160
  bounds(node.name_loc)
974
- name = visit_token(node.name.to_s)
1161
+ name = on_ident(node.name.to_s)
975
1162
 
976
1163
  bounds(node.location)
977
1164
  on_blockarg(name)
@@ -980,6 +1167,9 @@ module Prism
980
1167
 
981
1168
  # A block's parameters.
982
1169
  def visit_block_parameters_node(node)
1170
+ bounds(node.opening_loc)
1171
+ on_op("|")
1172
+
983
1173
  parameters =
984
1174
  if node.parameters.nil?
985
1175
  on_params(nil, nil, nil, nil, nil, nil, nil)
@@ -994,6 +1184,9 @@ module Prism
994
1184
  false
995
1185
  end
996
1186
 
1187
+ bounds(node.closing_loc)
1188
+ on_op("|")
1189
+
997
1190
  bounds(node.location)
998
1191
  on_block_var(parameters, locals)
999
1192
  end
@@ -1004,6 +1197,9 @@ module Prism
1004
1197
  # break foo
1005
1198
  # ^^^^^^^^^
1006
1199
  def visit_break_node(node)
1200
+ bounds(node.keyword_loc)
1201
+ on_kw("break")
1202
+
1007
1203
  if node.arguments.nil?
1008
1204
  bounds(node.location)
1009
1205
  on_break(on_args_new)
@@ -1028,20 +1224,32 @@ module Prism
1028
1224
  case node.name
1029
1225
  when :[]
1030
1226
  receiver = visit(node.receiver)
1031
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1227
+
1228
+ bounds(node.opening_loc)
1229
+ on_lbracket("[")
1230
+
1231
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1232
+
1233
+ bounds(node.closing_loc)
1234
+ on_rbracket("]")
1235
+
1236
+ block = visit(block_node)
1032
1237
 
1033
1238
  bounds(node.location)
1034
1239
  call = on_aref(receiver, arguments)
1035
1240
 
1036
- if block.nil?
1037
- call
1038
- else
1241
+ if block_node
1039
1242
  bounds(node.location)
1040
1243
  on_method_add_block(call, block)
1244
+ else
1245
+ call
1041
1246
  end
1042
1247
  when :[]=
1043
1248
  receiver = visit(node.receiver)
1044
1249
 
1250
+ bounds(node.opening_loc)
1251
+ on_lbracket("[")
1252
+
1045
1253
  *arguments, last_argument = node.arguments.arguments
1046
1254
  arguments << node.block if !node.block.nil?
1047
1255
 
@@ -1057,6 +1265,11 @@ module Prism
1057
1265
  end
1058
1266
  end
1059
1267
 
1268
+ bounds(node.closing_loc)
1269
+ on_rbracket("]")
1270
+ bounds(node.equal_loc)
1271
+ on_op("=")
1272
+
1060
1273
  bounds(node.location)
1061
1274
  call = on_aref_field(receiver, arguments)
1062
1275
  value = visit_write_value(last_argument)
@@ -1064,27 +1277,54 @@ module Prism
1064
1277
  bounds(last_argument.location)
1065
1278
  on_assign(call, value)
1066
1279
  when :-@, :+@, :~
1067
- receiver = visit(node.receiver)
1280
+ bounds(node.message_loc)
1281
+ on_op(node.message)
1068
1282
 
1283
+ receiver = visit(node.receiver)
1069
1284
  bounds(node.location)
1070
1285
  on_unary(node.name, receiver)
1071
1286
  when :!
1287
+ bounds(node.message_loc)
1072
1288
  if node.message == "not"
1289
+ on_kw("not")
1290
+
1291
+ if node.opening_loc
1292
+ bounds(node.opening_loc)
1293
+ on_lparen("(")
1294
+ end
1295
+
1073
1296
  receiver =
1074
- if !node.receiver.is_a?(ParenthesesNode) || !node.receiver.body.nil?
1297
+ if node.receiver.is_a?(ParenthesesNode) && node.receiver.body.nil?
1298
+ # The parens in `not()` just emit parens and nothing else.
1299
+ bounds(node.receiver.opening_loc)
1300
+ on_lparen("(")
1301
+ bounds(node.receiver.closing_loc)
1302
+ on_rparen(")")
1303
+ nil
1304
+ else
1075
1305
  visit(node.receiver)
1076
1306
  end
1077
1307
 
1308
+ if node.closing_loc
1309
+ bounds(node.closing_loc)
1310
+ on_rparen(")")
1311
+ end
1078
1312
  bounds(node.location)
1079
1313
  on_unary(:not, receiver)
1080
1314
  else
1315
+ on_op("!")
1316
+
1081
1317
  receiver = visit(node.receiver)
1082
1318
 
1083
1319
  bounds(node.location)
1084
1320
  on_unary(:!, receiver)
1085
1321
  end
1086
- when *BINARY_OPERATORS
1322
+ when BINARY_OPERATORS
1087
1323
  receiver = visit(node.receiver)
1324
+
1325
+ bounds(node.message_loc)
1326
+ on_op(node.message)
1327
+
1088
1328
  value = visit(node.arguments.arguments.first)
1089
1329
 
1090
1330
  bounds(node.location)
@@ -1096,9 +1336,21 @@ module Prism
1096
1336
  if node.variable_call?
1097
1337
  on_vcall(message)
1098
1338
  else
1099
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1339
+ if node.opening_loc
1340
+ bounds(node.opening_loc)
1341
+ on_lparen("(")
1342
+ end
1343
+
1344
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1345
+
1346
+ if node.closing_loc
1347
+ bounds(node.closing_loc)
1348
+ on_rparen(")")
1349
+ end
1350
+
1351
+ block = visit(block_node)
1100
1352
  call =
1101
- if node.opening_loc.nil? && arguments&.any?
1353
+ if node.opening_loc.nil? && get_arguments_and_block(node.arguments, node.block).first.any?
1102
1354
  bounds(node.location)
1103
1355
  on_command(message, arguments)
1104
1356
  elsif !node.opening_loc.nil?
@@ -1109,11 +1361,11 @@ module Prism
1109
1361
  on_method_add_arg(on_fcall(message), on_args_new)
1110
1362
  end
1111
1363
 
1112
- if block.nil?
1113
- call
1114
- else
1364
+ if block_node
1115
1365
  bounds(node.block.location)
1116
1366
  on_method_add_block(call, block)
1367
+ else
1368
+ call
1117
1369
  end
1118
1370
  end
1119
1371
  end
@@ -1121,7 +1373,7 @@ module Prism
1121
1373
  receiver = visit(node.receiver)
1122
1374
 
1123
1375
  bounds(node.call_operator_loc)
1124
- call_operator = visit_token(node.call_operator)
1376
+ call_operator = visit_call_operator(node.call_operator)
1125
1377
 
1126
1378
  message =
1127
1379
  if node.message_loc.nil?
@@ -1131,13 +1383,30 @@ module Prism
1131
1383
  visit_token(node.message, false)
1132
1384
  end
1133
1385
 
1386
+ if node.equal_loc
1387
+ bounds(node.equal_loc)
1388
+ on_op("=")
1389
+ end
1390
+
1134
1391
  if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil?
1135
1392
  value = visit_write_value(node.arguments.arguments.first)
1136
1393
 
1137
1394
  bounds(node.location)
1138
1395
  on_assign(on_field(receiver, call_operator, message), value)
1139
1396
  else
1140
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1397
+ if node.opening_loc
1398
+ bounds(node.opening_loc)
1399
+ on_lparen("(")
1400
+ end
1401
+
1402
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1403
+
1404
+ if node.closing_loc
1405
+ bounds(node.closing_loc)
1406
+ on_rparen(")")
1407
+ end
1408
+
1409
+ block = visit(block_node)
1141
1410
  call =
1142
1411
  if node.opening_loc.nil?
1143
1412
  bounds(node.location)
@@ -1155,27 +1424,35 @@ module Prism
1155
1424
  on_method_add_arg(on_call(receiver, call_operator, message), arguments)
1156
1425
  end
1157
1426
 
1158
- if block.nil?
1159
- call
1160
- else
1427
+ if block_node
1161
1428
  bounds(node.block.location)
1162
1429
  on_method_add_block(call, block)
1430
+ else
1431
+ call
1163
1432
  end
1164
1433
  end
1165
1434
  end
1166
1435
  end
1167
1436
 
1168
- # Visit the arguments and block of a call node and return the arguments
1169
- # and block as they should be used.
1170
- private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1437
+ # Extract the arguments and block Ripper-style, which means if the block
1438
+ # is like `&b` then it's moved to arguments.
1439
+ private def get_arguments_and_block(arguments_node, block_node)
1171
1440
  arguments = arguments_node&.arguments || []
1172
1441
  block = block_node
1173
1442
 
1174
1443
  if block.is_a?(BlockArgumentNode)
1175
- arguments << block
1444
+ arguments += [block]
1176
1445
  block = nil
1177
1446
  end
1178
1447
 
1448
+ [arguments, block]
1449
+ end
1450
+
1451
+ # Visit the arguments and block of a call node and return the arguments
1452
+ # and block as they should be used.
1453
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1454
+ arguments, block = get_arguments_and_block(arguments_node, block_node)
1455
+
1179
1456
  [
1180
1457
  if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
1181
1458
  visit(arguments.first)
@@ -1189,7 +1466,7 @@ module Prism
1189
1466
  on_args_add_block(args, false)
1190
1467
  end
1191
1468
  end,
1192
- visit(block)
1469
+ block,
1193
1470
  ]
1194
1471
  end
1195
1472
 
@@ -1207,7 +1484,7 @@ module Prism
1207
1484
  receiver = visit(node.receiver)
1208
1485
 
1209
1486
  bounds(node.call_operator_loc)
1210
- call_operator = visit_token(node.call_operator)
1487
+ call_operator = visit_call_operator(node.call_operator)
1211
1488
 
1212
1489
  bounds(node.message_loc)
1213
1490
  message = visit_token(node.message)
@@ -1229,7 +1506,7 @@ module Prism
1229
1506
  receiver = visit(node.receiver)
1230
1507
 
1231
1508
  bounds(node.call_operator_loc)
1232
- call_operator = visit_token(node.call_operator)
1509
+ call_operator = visit_call_operator(node.call_operator)
1233
1510
 
1234
1511
  bounds(node.message_loc)
1235
1512
  message = visit_token(node.message)
@@ -1251,7 +1528,7 @@ module Prism
1251
1528
  receiver = visit(node.receiver)
1252
1529
 
1253
1530
  bounds(node.call_operator_loc)
1254
- call_operator = visit_token(node.call_operator)
1531
+ call_operator = visit_call_operator(node.call_operator)
1255
1532
 
1256
1533
  bounds(node.message_loc)
1257
1534
  message = visit_token(node.message)
@@ -1273,6 +1550,9 @@ module Prism
1273
1550
  if node.call_operator == "::"
1274
1551
  receiver = visit(node.receiver)
1275
1552
 
1553
+ bounds(node.call_operator_loc)
1554
+ on_op("::")
1555
+
1276
1556
  bounds(node.message_loc)
1277
1557
  message = visit_token(node.message)
1278
1558
 
@@ -1282,7 +1562,7 @@ module Prism
1282
1562
  receiver = visit(node.receiver)
1283
1563
 
1284
1564
  bounds(node.call_operator_loc)
1285
- call_operator = visit_token(node.call_operator)
1565
+ call_operator = visit_call_operator(node.call_operator)
1286
1566
 
1287
1567
  bounds(node.message_loc)
1288
1568
  message = visit_token(node.message)
@@ -1296,6 +1576,10 @@ module Prism
1296
1576
  # ^^^^^^^^^^
1297
1577
  def visit_capture_pattern_node(node)
1298
1578
  value = visit(node.value)
1579
+
1580
+ bounds(node.operator_loc)
1581
+ on_op("=>")
1582
+
1299
1583
  target = visit(node.target)
1300
1584
 
1301
1585
  bounds(node.location)
@@ -1305,10 +1589,21 @@ module Prism
1305
1589
  # case foo; when bar; end
1306
1590
  # ^^^^^^^^^^^^^^^^^^^^^^^
1307
1591
  def visit_case_node(node)
1592
+ bounds(node.case_keyword_loc)
1593
+ on_kw("case")
1594
+
1308
1595
  predicate = visit(node.predicate)
1596
+ visited_conditions = node.conditions.map { |condition| visit(condition) }
1597
+ visited_else_clause = visit(node.else_clause)
1598
+
1599
+ if !node.else_clause
1600
+ bounds(node.end_keyword_loc)
1601
+ on_kw("end")
1602
+ end
1603
+
1309
1604
  clauses =
1310
- node.conditions.reverse_each.inject(visit(node.else_clause)) do |current, condition|
1311
- on_when(*visit(condition), current)
1605
+ visited_conditions.reverse_each.inject(visited_else_clause) do |current, condition|
1606
+ on_when(*condition, current)
1312
1607
  end
1313
1608
 
1314
1609
  bounds(node.location)
@@ -1318,10 +1613,23 @@ module Prism
1318
1613
  # case foo; in bar; end
1319
1614
  # ^^^^^^^^^^^^^^^^^^^^^
1320
1615
  def visit_case_match_node(node)
1616
+ bounds(node.case_keyword_loc)
1617
+ on_kw("case")
1618
+
1321
1619
  predicate = visit(node.predicate)
1620
+ visited_conditions = node.conditions.map do | condition|
1621
+ visit(condition)
1622
+ end
1623
+ visited_else_clause = visit(node.else_clause)
1624
+
1625
+ if !node.else_clause
1626
+ bounds(node.end_keyword_loc)
1627
+ on_kw("end")
1628
+ end
1629
+
1322
1630
  clauses =
1323
- node.conditions.reverse_each.inject(visit(node.else_clause)) do |current, condition|
1324
- on_in(*visit(condition), current)
1631
+ visited_conditions.reverse_each.inject(visited_else_clause) do |current, condition|
1632
+ on_in(*condition, current)
1325
1633
  end
1326
1634
 
1327
1635
  bounds(node.location)
@@ -1331,6 +1639,9 @@ module Prism
1331
1639
  # class Foo; end
1332
1640
  # ^^^^^^^^^^^^^^
1333
1641
  def visit_class_node(node)
1642
+ bounds(node.class_keyword_loc)
1643
+ on_kw("class")
1644
+
1334
1645
  constant_path =
1335
1646
  if node.constant_path.is_a?(ConstantReadNode)
1336
1647
  bounds(node.constant_path.location)
@@ -1339,9 +1650,17 @@ module Prism
1339
1650
  visit(node.constant_path)
1340
1651
  end
1341
1652
 
1653
+ if node.inheritance_operator_loc
1654
+ bounds(node.inheritance_operator_loc)
1655
+ on_op("<")
1656
+ end
1657
+
1342
1658
  superclass = visit(node.superclass)
1343
1659
  bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?)
1344
1660
 
1661
+ bounds(node.end_keyword_loc)
1662
+ on_kw("end")
1663
+
1345
1664
  bounds(node.location)
1346
1665
  on_class(constant_path, superclass, bodystmt)
1347
1666
  end
@@ -1355,12 +1674,13 @@ module Prism
1355
1674
 
1356
1675
  # @@foo = 1
1357
1676
  # ^^^^^^^^^
1358
- #
1359
- # @@foo, @@bar = 1
1360
- # ^^^^^ ^^^^^
1361
1677
  def visit_class_variable_write_node(node)
1362
1678
  bounds(node.name_loc)
1363
1679
  target = on_var_field(on_cvar(node.name.to_s))
1680
+
1681
+ bounds(node.operator_loc)
1682
+ on_op("=")
1683
+
1364
1684
  value = visit_write_value(node.value)
1365
1685
 
1366
1686
  bounds(node.location)
@@ -1425,12 +1745,13 @@ module Prism
1425
1745
 
1426
1746
  # Foo = 1
1427
1747
  # ^^^^^^^
1428
- #
1429
- # Foo, Bar = 1
1430
- # ^^^ ^^^
1431
1748
  def visit_constant_write_node(node)
1432
1749
  bounds(node.name_loc)
1433
1750
  target = on_var_field(on_const(node.name.to_s))
1751
+
1752
+ bounds(node.operator_loc)
1753
+ on_op("=")
1754
+
1434
1755
  value = visit_write_value(node.value)
1435
1756
 
1436
1757
  bounds(node.location)
@@ -1490,6 +1811,11 @@ module Prism
1490
1811
  # ^^^^^^^^
1491
1812
  def visit_constant_path_node(node)
1492
1813
  if node.parent.nil?
1814
+ if node.delimiter_loc
1815
+ bounds(node.delimiter_loc)
1816
+ on_op("::")
1817
+ end
1818
+
1493
1819
  bounds(node.name_loc)
1494
1820
  child = on_const(node.name.to_s)
1495
1821
 
@@ -1498,6 +1824,9 @@ module Prism
1498
1824
  else
1499
1825
  parent = visit(node.parent)
1500
1826
 
1827
+ bounds(node.delimiter_loc)
1828
+ on_op("::")
1829
+
1501
1830
  bounds(node.name_loc)
1502
1831
  child = on_const(node.name.to_s)
1503
1832
 
@@ -1508,11 +1837,12 @@ module Prism
1508
1837
 
1509
1838
  # Foo::Bar = 1
1510
1839
  # ^^^^^^^^^^^^
1511
- #
1512
- # Foo::Foo, Bar::Bar = 1
1513
- # ^^^^^^^^ ^^^^^^^^
1514
1840
  def visit_constant_path_write_node(node)
1515
1841
  target = visit_constant_path_write_node_target(node.target)
1842
+
1843
+ bounds(node.operator_loc)
1844
+ on_op("=")
1845
+
1516
1846
  value = visit_write_value(node.value)
1517
1847
 
1518
1848
  bounds(node.location)
@@ -1522,6 +1852,11 @@ module Prism
1522
1852
  # Visit a constant path that is part of a write node.
1523
1853
  private def visit_constant_path_write_node_target(node)
1524
1854
  if node.parent.nil?
1855
+ if node.delimiter_loc
1856
+ bounds(node.delimiter_loc)
1857
+ on_op("::")
1858
+ end
1859
+
1525
1860
  bounds(node.name_loc)
1526
1861
  child = on_const(node.name.to_s)
1527
1862
 
@@ -1530,6 +1865,9 @@ module Prism
1530
1865
  else
1531
1866
  parent = visit(node.parent)
1532
1867
 
1868
+ bounds(node.delimiter_loc)
1869
+ on_op("::")
1870
+
1533
1871
  bounds(node.name_loc)
1534
1872
  child = on_const(node.name.to_s)
1535
1873
 
@@ -1542,7 +1880,6 @@ module Prism
1542
1880
  # ^^^^^^^^^^^^^^^
1543
1881
  def visit_constant_path_operator_write_node(node)
1544
1882
  target = visit_constant_path_write_node_target(node.target)
1545
- value = visit(node.value)
1546
1883
 
1547
1884
  bounds(node.binary_operator_loc)
1548
1885
  operator = on_op("#{node.binary_operator}=")
@@ -1556,7 +1893,6 @@ module Prism
1556
1893
  # ^^^^^^^^^^^^^^^^
1557
1894
  def visit_constant_path_and_write_node(node)
1558
1895
  target = visit_constant_path_write_node_target(node.target)
1559
- value = visit(node.value)
1560
1896
 
1561
1897
  bounds(node.operator_loc)
1562
1898
  operator = on_op("&&=")
@@ -1570,7 +1906,6 @@ module Prism
1570
1906
  # ^^^^^^^^^^^^^^^^
1571
1907
  def visit_constant_path_or_write_node(node)
1572
1908
  target = visit_constant_path_write_node_target(node.target)
1573
- value = visit(node.value)
1574
1909
 
1575
1910
  bounds(node.operator_loc)
1576
1911
  operator = on_op("||=")
@@ -1592,16 +1927,24 @@ module Prism
1592
1927
  # def self.foo; end
1593
1928
  # ^^^^^^^^^^^^^^^^^
1594
1929
  def visit_def_node(node)
1930
+ bounds(node.def_keyword_loc)
1931
+ on_kw("def")
1932
+
1595
1933
  receiver = visit(node.receiver)
1596
1934
  operator =
1597
1935
  if !node.operator_loc.nil?
1598
1936
  bounds(node.operator_loc)
1599
- visit_token(node.operator)
1937
+ node.operator == "." ? on_period(".") : on_op("::")
1600
1938
  end
1601
1939
 
1602
1940
  bounds(node.name_loc)
1603
1941
  name = visit_token(node.name_loc.slice)
1604
1942
 
1943
+ if node.lparen_loc
1944
+ bounds(node.lparen_loc)
1945
+ on_lparen("(")
1946
+ end
1947
+
1605
1948
  parameters =
1606
1949
  if node.parameters.nil?
1607
1950
  bounds(node.location)
@@ -1611,10 +1954,17 @@ module Prism
1611
1954
  end
1612
1955
 
1613
1956
  if !node.lparen_loc.nil?
1957
+ bounds(node.rparen_loc)
1958
+ on_rparen(")")
1614
1959
  bounds(node.lparen_loc)
1615
1960
  parameters = on_paren(parameters)
1616
1961
  end
1617
1962
 
1963
+ if node.equal_loc
1964
+ bounds(node.equal_loc)
1965
+ on_op("=")
1966
+ end
1967
+
1618
1968
  bodystmt =
1619
1969
  if node.equal_loc.nil?
1620
1970
  visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body)
@@ -1625,11 +1975,16 @@ module Prism
1625
1975
  on_bodystmt(body, nil, nil, nil)
1626
1976
  end
1627
1977
 
1978
+ if node.end_keyword_loc
1979
+ bounds(node.end_keyword_loc)
1980
+ on_kw("end")
1981
+ end
1982
+
1628
1983
  bounds(node.location)
1629
- if receiver.nil?
1630
- on_def(name, parameters, bodystmt)
1631
- else
1984
+ if receiver
1632
1985
  on_defs(receiver, operator, name, parameters, bodystmt)
1986
+ else
1987
+ on_def(name, parameters, bodystmt)
1633
1988
  end
1634
1989
  end
1635
1990
 
@@ -1639,8 +1994,21 @@ module Prism
1639
1994
  # defined?(a)
1640
1995
  # ^^^^^^^^^^^
1641
1996
  def visit_defined_node(node)
1997
+ bounds(node.keyword_loc)
1998
+ on_kw("defined?")
1999
+
2000
+ if node.lparen_loc
2001
+ bounds(node.lparen_loc)
2002
+ on_lparen("(")
2003
+ end
2004
+
1642
2005
  expression = visit(node.value)
1643
2006
 
2007
+ if node.rparen_loc
2008
+ bounds(node.rparen_loc)
2009
+ on_rparen(")")
2010
+ end
2011
+
1644
2012
  # Very weird circumstances here where something like:
1645
2013
  #
1646
2014
  # defined?
@@ -1661,17 +2029,24 @@ module Prism
1661
2029
  # if foo then bar else baz end
1662
2030
  # ^^^^^^^^^^^^
1663
2031
  def visit_else_node(node)
2032
+ bounds(node.else_keyword_loc)
2033
+ on_kw("else")
2034
+
1664
2035
  statements =
1665
2036
  if node.statements.nil?
1666
2037
  [nil]
1667
2038
  else
1668
2039
  body = node.statements.body
1669
- body.unshift(nil) if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
2040
+ body = [nil, *body] if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
1670
2041
  body
1671
2042
  end
1672
2043
 
2044
+ else_statements = visit_statements_node_body(statements)
2045
+
2046
+ bounds(node.end_keyword_loc)
2047
+ on_kw("end")
1673
2048
  bounds(node.location)
1674
- on_else(visit_statements_node_body(statements))
2049
+ on_else(else_statements)
1675
2050
  end
1676
2051
 
1677
2052
  # "foo #{bar}"
@@ -1709,12 +2084,15 @@ module Prism
1709
2084
 
1710
2085
  # Visit an EnsureNode node.
1711
2086
  def visit_ensure_node(node)
2087
+ bounds(node.ensure_keyword_loc)
2088
+ on_kw("ensure")
2089
+
1712
2090
  statements =
1713
2091
  if node.statements.nil?
1714
2092
  [nil]
1715
2093
  else
1716
2094
  body = node.statements.body
1717
- body.unshift(nil) if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
2095
+ body = [nil, *body] if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
1718
2096
  body
1719
2097
  end
1720
2098
 
@@ -1735,6 +2113,14 @@ module Prism
1735
2113
  # ^^^^^^^^^^^
1736
2114
  def visit_find_pattern_node(node)
1737
2115
  constant = visit(node.constant)
2116
+
2117
+ if node.opening_loc
2118
+ bounds(node.opening_loc)
2119
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
2120
+ end
2121
+ bounds(node.left.operator_loc)
2122
+ on_op("*")
2123
+
1738
2124
  left =
1739
2125
  if node.left.expression.nil?
1740
2126
  bounds(node.left.location)
@@ -1744,6 +2130,10 @@ module Prism
1744
2130
  end
1745
2131
 
1746
2132
  requireds = visit_all(node.requireds) if node.requireds.any?
2133
+
2134
+ bounds(node.right.operator_loc)
2135
+ on_op("*")
2136
+
1747
2137
  right =
1748
2138
  if node.right.expression.nil?
1749
2139
  bounds(node.right.location)
@@ -1752,6 +2142,10 @@ module Prism
1752
2142
  visit(node.right.expression)
1753
2143
  end
1754
2144
 
2145
+ if node.closing_loc
2146
+ bounds(node.closing_loc)
2147
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
2148
+ end
1755
2149
  bounds(node.location)
1756
2150
  on_fndptn(constant, left, requireds, right)
1757
2151
  end
@@ -1760,6 +2154,10 @@ module Prism
1760
2154
  # ^^^^^^^^^^
1761
2155
  def visit_flip_flop_node(node)
1762
2156
  left = visit(node.left)
2157
+
2158
+ bounds(node.operator_loc)
2159
+ on_op(node.operator)
2160
+
1763
2161
  right = visit(node.right)
1764
2162
 
1765
2163
  bounds(node.location)
@@ -1779,8 +2177,18 @@ module Prism
1779
2177
  # for foo in bar do end
1780
2178
  # ^^^^^^^^^^^^^^^^^^^^^
1781
2179
  def visit_for_node(node)
2180
+ bounds(node.for_keyword_loc)
2181
+ on_kw("for")
2182
+
1782
2183
  index = visit(node.index)
2184
+ bounds(node.in_keyword_loc)
2185
+ on_kw("in")
2186
+
1783
2187
  collection = visit(node.collection)
2188
+ if node.do_keyword_loc
2189
+ bounds(node.do_keyword_loc)
2190
+ on_kw("do")
2191
+ end
1784
2192
  statements =
1785
2193
  if node.statements.nil?
1786
2194
  bounds(node.location)
@@ -1789,6 +2197,9 @@ module Prism
1789
2197
  visit(node.statements)
1790
2198
  end
1791
2199
 
2200
+ bounds(node.end_keyword_loc)
2201
+ on_kw("end")
2202
+
1792
2203
  bounds(node.location)
1793
2204
  on_for(index, collection, statements)
1794
2205
  end
@@ -1797,6 +2208,7 @@ module Prism
1797
2208
  # ^^^
1798
2209
  def visit_forwarding_arguments_node(node)
1799
2210
  bounds(node.location)
2211
+ on_op("...")
1800
2212
  on_args_forward
1801
2213
  end
1802
2214
 
@@ -1804,6 +2216,7 @@ module Prism
1804
2216
  # ^^^
1805
2217
  def visit_forwarding_parameter_node(node)
1806
2218
  bounds(node.location)
2219
+ on_op("...")
1807
2220
  on_args_forward
1808
2221
  end
1809
2222
 
@@ -1813,6 +2226,9 @@ module Prism
1813
2226
  # super {}
1814
2227
  # ^^^^^^^^
1815
2228
  def visit_forwarding_super_node(node)
2229
+ bounds(node.keyword_loc)
2230
+ on_kw("super")
2231
+
1816
2232
  if node.block.nil?
1817
2233
  bounds(node.location)
1818
2234
  on_zsuper
@@ -1833,12 +2249,13 @@ module Prism
1833
2249
 
1834
2250
  # $foo = 1
1835
2251
  # ^^^^^^^^
1836
- #
1837
- # $foo, $bar = 1
1838
- # ^^^^ ^^^^
1839
2252
  def visit_global_variable_write_node(node)
1840
2253
  bounds(node.name_loc)
1841
2254
  target = on_var_field(on_gvar(node.name.to_s))
2255
+
2256
+ bounds(node.operator_loc)
2257
+ on_op("=")
2258
+
1842
2259
  value = visit_write_value(node.value)
1843
2260
 
1844
2261
  bounds(node.location)
@@ -1897,6 +2314,9 @@ module Prism
1897
2314
  # {}
1898
2315
  # ^^
1899
2316
  def visit_hash_node(node)
2317
+ bounds(node.opening_loc)
2318
+ on_lbrace("{")
2319
+
1900
2320
  elements =
1901
2321
  if node.elements.any?
1902
2322
  args = visit_all(node.elements)
@@ -1905,6 +2325,8 @@ module Prism
1905
2325
  on_assoclist_from_args(args)
1906
2326
  end
1907
2327
 
2328
+ bounds(node.closing_loc)
2329
+ on_rbrace("}")
1908
2330
  bounds(node.location)
1909
2331
  on_hash(elements)
1910
2332
  end
@@ -1913,6 +2335,15 @@ module Prism
1913
2335
  # ^^
1914
2336
  def visit_hash_pattern_node(node)
1915
2337
  constant = visit(node.constant)
2338
+
2339
+ if node.constant
2340
+ bounds(node.opening_loc)
2341
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
2342
+ elsif node.opening_loc
2343
+ bounds(node.opening_loc)
2344
+ on_lbrace("{")
2345
+ end
2346
+
1916
2347
  elements =
1917
2348
  if node.elements.any? || !node.rest.nil?
1918
2349
  node.elements.map do |element|
@@ -1920,8 +2351,8 @@ module Prism
1920
2351
  if (key = element.key).opening_loc.nil?
1921
2352
  visit(key)
1922
2353
  else
1923
- bounds(key.value_loc)
1924
- if (value = key.value).empty?
2354
+ bounds(key.content_loc)
2355
+ if (value = key.content).empty?
1925
2356
  on_string_content
1926
2357
  else
1927
2358
  on_string_add(on_string_content, on_tstring_content(value))
@@ -1935,12 +2366,21 @@ module Prism
1935
2366
  rest =
1936
2367
  case node.rest
1937
2368
  when AssocSplatNode
2369
+ bounds(node.rest.operator_loc)
2370
+ on_op("**")
1938
2371
  visit(node.rest.value)
1939
2372
  when NoKeywordsParameterNode
1940
2373
  bounds(node.rest.location)
1941
2374
  on_var_field(visit(node.rest))
1942
2375
  end
1943
2376
 
2377
+ if node.constant
2378
+ bounds(node.closing_loc)
2379
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
2380
+ elsif node.closing_loc
2381
+ bounds(node.closing_loc)
2382
+ on_rbrace("}")
2383
+ end
1944
2384
  bounds(node.location)
1945
2385
  on_hshptn(constant, elements, rest)
1946
2386
  end
@@ -1956,13 +2396,27 @@ module Prism
1956
2396
  def visit_if_node(node)
1957
2397
  if node.then_keyword == "?"
1958
2398
  predicate = visit(node.predicate)
2399
+
2400
+ bounds(node.then_keyword_loc)
2401
+ on_op("?")
2402
+
1959
2403
  truthy = visit(node.statements.body.first)
2404
+
2405
+ bounds(node.subsequent.else_keyword_loc)
2406
+ on_op(":")
2407
+
1960
2408
  falsy = visit(node.subsequent.statements.body.first)
1961
2409
 
1962
2410
  bounds(node.location)
1963
2411
  on_ifop(predicate, truthy, falsy)
1964
2412
  elsif node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
2413
+ bounds(node.if_keyword_loc)
2414
+ on_kw(node.if_keyword)
1965
2415
  predicate = visit(node.predicate)
2416
+ if node.then_keyword_loc && node.then_keyword != "?"
2417
+ bounds(node.then_keyword_loc)
2418
+ on_kw("then")
2419
+ end
1966
2420
  statements =
1967
2421
  if node.statements.nil?
1968
2422
  bounds(node.location)
@@ -1972,6 +2426,11 @@ module Prism
1972
2426
  end
1973
2427
  subsequent = visit(node.subsequent)
1974
2428
 
2429
+ if node.end_keyword_loc && !node.subsequent
2430
+ bounds(node.end_keyword_loc)
2431
+ on_kw("end")
2432
+ end
2433
+
1975
2434
  bounds(node.location)
1976
2435
  if node.if_keyword == "if"
1977
2436
  on_if(predicate, statements, subsequent)
@@ -1980,6 +2439,8 @@ module Prism
1980
2439
  end
1981
2440
  else
1982
2441
  statements = visit(node.statements.body.first)
2442
+ bounds(node.if_keyword_loc)
2443
+ on_kw(node.if_keyword)
1983
2444
  predicate = visit(node.predicate)
1984
2445
 
1985
2446
  bounds(node.location)
@@ -2011,7 +2472,14 @@ module Prism
2011
2472
  # This is a special case where we're not going to call on_in directly
2012
2473
  # because we don't have access to the subsequent. Instead, we'll return
2013
2474
  # the component parts and let the parent node handle it.
2475
+ bounds(node.in_keyword_loc)
2476
+ on_kw("in")
2477
+
2014
2478
  pattern = visit_pattern_node(node.pattern)
2479
+ if node.then_keyword_loc
2480
+ bounds(node.then_keyword_loc)
2481
+ on_kw("then")
2482
+ end
2015
2483
  statements =
2016
2484
  if node.statements.nil?
2017
2485
  bounds(node.location)
@@ -2027,8 +2495,15 @@ module Prism
2027
2495
  # ^^^^^^^^^^^^^^^
2028
2496
  def visit_index_operator_write_node(node)
2029
2497
  receiver = visit(node.receiver)
2498
+
2499
+ bounds(node.opening_loc)
2500
+ on_lbracket("[")
2501
+
2030
2502
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2031
2503
 
2504
+ bounds(node.closing_loc)
2505
+ on_rbracket("]")
2506
+
2032
2507
  bounds(node.location)
2033
2508
  target = on_aref_field(receiver, arguments)
2034
2509
 
@@ -2044,8 +2519,15 @@ module Prism
2044
2519
  # ^^^^^^^^^^^^^^^^
2045
2520
  def visit_index_and_write_node(node)
2046
2521
  receiver = visit(node.receiver)
2522
+
2523
+ bounds(node.opening_loc)
2524
+ on_lbracket("[")
2525
+
2047
2526
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2048
2527
 
2528
+ bounds(node.closing_loc)
2529
+ on_rbracket("]")
2530
+
2049
2531
  bounds(node.location)
2050
2532
  target = on_aref_field(receiver, arguments)
2051
2533
 
@@ -2061,8 +2543,15 @@ module Prism
2061
2543
  # ^^^^^^^^^^^^^^^^
2062
2544
  def visit_index_or_write_node(node)
2063
2545
  receiver = visit(node.receiver)
2546
+
2547
+ bounds(node.opening_loc)
2548
+ on_lbracket("[")
2549
+
2064
2550
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2065
2551
 
2552
+ bounds(node.closing_loc)
2553
+ on_rbracket("]")
2554
+
2066
2555
  bounds(node.location)
2067
2556
  target = on_aref_field(receiver, arguments)
2068
2557
 
@@ -2078,8 +2567,15 @@ module Prism
2078
2567
  # ^^^^^^^^
2079
2568
  def visit_index_target_node(node)
2080
2569
  receiver = visit(node.receiver)
2570
+
2571
+ bounds(node.opening_loc)
2572
+ on_lbracket("[")
2573
+
2081
2574
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2082
2575
 
2576
+ bounds(node.closing_loc)
2577
+ on_rbracket("]")
2578
+
2083
2579
  bounds(node.location)
2084
2580
  on_aref_field(receiver, arguments)
2085
2581
  end
@@ -2096,6 +2592,10 @@ module Prism
2096
2592
  def visit_instance_variable_write_node(node)
2097
2593
  bounds(node.name_loc)
2098
2594
  target = on_var_field(on_ivar(node.name.to_s))
2595
+
2596
+ bounds(node.operator_loc)
2597
+ on_op("=")
2598
+
2099
2599
  value = visit_write_value(node.value)
2100
2600
 
2101
2601
  bounds(node.location)
@@ -2198,20 +2698,37 @@ module Prism
2198
2698
  # "foo #{bar}"
2199
2699
  # ^^^^^^^^^^^^
2200
2700
  def visit_interpolated_string_node(node)
2201
- if node.opening&.start_with?("<<~")
2202
- heredoc = visit_heredoc_string_node(node)
2701
+ with_string_bounds(node) do
2702
+ if node.opening&.start_with?("<<~")
2703
+ heredoc = visit_heredoc_string_node(node)
2203
2704
 
2204
- bounds(node.location)
2205
- on_string_literal(heredoc)
2206
- elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2207
- first, *rest = node.parts
2208
- rest.inject(visit(first)) do |content, part|
2209
- concat = visit(part)
2705
+ bounds(node.location)
2706
+ on_string_literal(heredoc)
2707
+ elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2708
+ first, *rest = node.parts
2709
+ rest.inject(visit(first)) do |content, part|
2710
+ concat = visit(part)
2711
+
2712
+ bounds(part.location)
2713
+ on_string_concat(content, concat)
2714
+ end
2715
+ else
2716
+ bounds(node.parts.first.location)
2717
+ parts =
2718
+ node.parts.inject(on_string_content) do |content, part|
2719
+ on_string_add(content, visit_string_content(part))
2720
+ end
2210
2721
 
2211
- bounds(part.location)
2212
- on_string_concat(content, concat)
2722
+ bounds(node.location)
2723
+ on_string_literal(parts)
2213
2724
  end
2214
- else
2725
+ end
2726
+ end
2727
+
2728
+ # :"foo #{bar}"
2729
+ # ^^^^^^^^^^^^^
2730
+ def visit_interpolated_symbol_node(node)
2731
+ with_string_bounds(node) do
2215
2732
  bounds(node.parts.first.location)
2216
2733
  parts =
2217
2734
  node.parts.inject(on_string_content) do |content, part|
@@ -2219,40 +2736,29 @@ module Prism
2219
2736
  end
2220
2737
 
2221
2738
  bounds(node.location)
2222
- on_string_literal(parts)
2739
+ on_dyna_symbol(parts)
2223
2740
  end
2224
2741
  end
2225
2742
 
2226
- # :"foo #{bar}"
2227
- # ^^^^^^^^^^^^^
2228
- def visit_interpolated_symbol_node(node)
2229
- bounds(node.parts.first.location)
2230
- parts =
2231
- node.parts.inject(on_string_content) do |content, part|
2232
- on_string_add(content, visit_string_content(part))
2233
- end
2234
-
2235
- bounds(node.location)
2236
- on_dyna_symbol(parts)
2237
- end
2238
-
2239
2743
  # `foo #{bar}`
2240
2744
  # ^^^^^^^^^^^^
2241
2745
  def visit_interpolated_x_string_node(node)
2242
- if node.opening.start_with?("<<~")
2243
- heredoc = visit_heredoc_x_string_node(node)
2746
+ with_string_bounds(node) do
2747
+ if node.opening.start_with?("<<~")
2748
+ heredoc = visit_heredoc_x_string_node(node)
2244
2749
 
2245
- bounds(node.location)
2246
- on_xstring_literal(heredoc)
2247
- else
2248
- bounds(node.parts.first.location)
2249
- parts =
2250
- node.parts.inject(on_xstring_new) do |content, part|
2251
- on_xstring_add(content, visit_string_content(part))
2252
- end
2750
+ bounds(node.location)
2751
+ on_xstring_literal(heredoc)
2752
+ else
2753
+ bounds(node.parts.first.location)
2754
+ parts =
2755
+ node.parts.inject(on_xstring_new) do |content, part|
2756
+ on_xstring_add(content, visit_string_content(part))
2757
+ end
2253
2758
 
2254
- bounds(node.location)
2255
- on_xstring_literal(parts)
2759
+ bounds(node.location)
2760
+ on_xstring_literal(parts)
2761
+ end
2256
2762
  end
2257
2763
  end
2258
2764
 
@@ -2293,6 +2799,9 @@ module Prism
2293
2799
  # def foo(**); end
2294
2800
  # ^^
2295
2801
  def visit_keyword_rest_parameter_node(node)
2802
+ bounds(node.operator_loc)
2803
+ on_op("**")
2804
+
2296
2805
  if node.name_loc.nil?
2297
2806
  bounds(node.location)
2298
2807
  on_kwrest_param(nil)
@@ -2312,6 +2821,11 @@ module Prism
2312
2821
 
2313
2822
  parameters =
2314
2823
  if node.parameters.is_a?(BlockParametersNode)
2824
+ if node.parameters.opening_loc
2825
+ bounds(node.parameters.opening_loc)
2826
+ on_lparen("(")
2827
+ end
2828
+
2315
2829
  # Ripper does not track block-locals within lambdas, so we skip
2316
2830
  # directly to the parameters here.
2317
2831
  params =
@@ -2322,6 +2836,13 @@ module Prism
2322
2836
  visit(node.parameters.parameters)
2323
2837
  end
2324
2838
 
2839
+ visit_all(node.parameters.locals)
2840
+
2841
+ if node.parameters.closing_loc
2842
+ bounds(node.parameters.closing_loc)
2843
+ on_rparen(")")
2844
+ end
2845
+
2325
2846
  if node.parameters.opening_loc.nil?
2326
2847
  params
2327
2848
  else
@@ -2334,9 +2855,11 @@ module Prism
2334
2855
  end
2335
2856
 
2336
2857
  braces = node.opening == "{"
2858
+ bounds(node.opening_loc)
2337
2859
  if braces
2338
- bounds(node.opening_loc)
2339
2860
  on_tlambeg(node.opening)
2861
+ else
2862
+ on_kw("do")
2340
2863
  end
2341
2864
 
2342
2865
  body =
@@ -2349,7 +2872,7 @@ module Prism
2349
2872
  braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
2350
2873
  when StatementsNode
2351
2874
  stmts = node.body.body
2352
- stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
2875
+ stmts = [nil, *stmts] if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
2353
2876
  stmts = visit_statements_node_body(stmts)
2354
2877
 
2355
2878
  bounds(node.body.location)
@@ -2360,6 +2883,13 @@ module Prism
2360
2883
  raise
2361
2884
  end
2362
2885
 
2886
+ bounds(node.closing_loc)
2887
+ if braces
2888
+ on_rbrace("}")
2889
+ else
2890
+ on_kw("end")
2891
+ end
2892
+
2363
2893
  bounds(node.location)
2364
2894
  on_lambda(parameters, body)
2365
2895
  end
@@ -2376,6 +2906,10 @@ module Prism
2376
2906
  def visit_local_variable_write_node(node)
2377
2907
  bounds(node.name_loc)
2378
2908
  target = on_var_field(on_ident(node.name_loc.slice))
2909
+
2910
+ bounds(node.operator_loc)
2911
+ on_op("=")
2912
+
2379
2913
  value = visit_write_value(node.value)
2380
2914
 
2381
2915
  bounds(node.location)
@@ -2450,6 +2984,8 @@ module Prism
2450
2984
  # ^^^^^^^^^^
2451
2985
  def visit_match_predicate_node(node)
2452
2986
  value = visit(node.value)
2987
+ bounds(node.keyword_loc)
2988
+ on_kw("in")
2453
2989
  pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
2454
2990
 
2455
2991
  on_case(value, pattern)
@@ -2459,6 +2995,10 @@ module Prism
2459
2995
  # ^^^^^^^^^^
2460
2996
  def visit_match_required_node(node)
2461
2997
  value = visit(node.value)
2998
+
2999
+ bounds(node.operator_loc)
3000
+ on_op("=>")
3001
+
2462
3002
  pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
2463
3003
 
2464
3004
  on_case(value, pattern)
@@ -2472,13 +3012,16 @@ module Prism
2472
3012
 
2473
3013
  # A node that is missing from the syntax tree. This is only used in the
2474
3014
  # case of a syntax error.
2475
- def visit_missing_node(node)
2476
- raise "Cannot visit missing nodes directly."
3015
+ def visit_error_recovery_node(node)
3016
+ raise "Cannot visit error recovery nodes directly."
2477
3017
  end
2478
3018
 
2479
3019
  # module Foo; end
2480
3020
  # ^^^^^^^^^^^^^^^
2481
3021
  def visit_module_node(node)
3022
+ bounds(node.module_keyword_loc)
3023
+ on_kw("module")
3024
+
2482
3025
  constant_path =
2483
3026
  if node.constant_path.is_a?(ConstantReadNode)
2484
3027
  bounds(node.constant_path.location)
@@ -2489,6 +3032,9 @@ module Prism
2489
3032
 
2490
3033
  bodystmt = visit_body_node(node.constant_path.location, node.body, true)
2491
3034
 
3035
+ bounds(node.end_keyword_loc)
3036
+ on_kw("end")
3037
+
2492
3038
  bounds(node.location)
2493
3039
  on_module(constant_path, bodystmt)
2494
3040
  end
@@ -2496,9 +3042,19 @@ module Prism
2496
3042
  # (foo, bar), bar = qux
2497
3043
  # ^^^^^^^^^^
2498
3044
  def visit_multi_target_node(node)
3045
+ if node.lparen_loc
3046
+ bounds(node.lparen_loc)
3047
+ on_lparen("(")
3048
+ end
3049
+
2499
3050
  bounds(node.location)
2500
3051
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
2501
3052
 
3053
+ if node.rparen_loc
3054
+ bounds(node.rparen_loc)
3055
+ on_rparen(")")
3056
+ end
3057
+
2502
3058
  if node.lparen_loc.nil?
2503
3059
  targets
2504
3060
  else
@@ -2550,9 +3106,22 @@ module Prism
2550
3106
  # foo, bar = baz
2551
3107
  # ^^^^^^^^^^^^^^
2552
3108
  def visit_multi_write_node(node)
3109
+ if node.lparen_loc
3110
+ bounds(node.lparen_loc)
3111
+ on_lparen("(")
3112
+ end
3113
+
2553
3114
  bounds(node.location)
2554
3115
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
2555
3116
 
3117
+ if node.rparen_loc
3118
+ bounds(node.rparen_loc)
3119
+ on_rparen(")")
3120
+ end
3121
+
3122
+ bounds(node.operator_loc)
3123
+ on_op("=")
3124
+
2556
3125
  unless node.lparen_loc.nil?
2557
3126
  bounds(node.lparen_loc)
2558
3127
  targets = on_mlhs_paren(targets)
@@ -2570,6 +3139,9 @@ module Prism
2570
3139
  # next foo
2571
3140
  # ^^^^^^^^
2572
3141
  def visit_next_node(node)
3142
+ bounds(node.keyword_loc)
3143
+ on_kw("next")
3144
+
2573
3145
  if node.arguments.nil?
2574
3146
  bounds(node.location)
2575
3147
  on_next(on_args_new)
@@ -2588,9 +3160,24 @@ module Prism
2588
3160
  on_var_ref(on_kw("nil"))
2589
3161
  end
2590
3162
 
3163
+ # def foo(&nil); end
3164
+ # ^^^^
3165
+ def visit_no_block_parameter_node(node)
3166
+ bounds(node.operator_loc)
3167
+ on_op("&")
3168
+ bounds(node.keyword_loc)
3169
+ on_kw("nil")
3170
+ bounds(node.location)
3171
+ on_blockarg(:nil)
3172
+ end
3173
+
2591
3174
  # def foo(**nil); end
2592
3175
  # ^^^^^
2593
3176
  def visit_no_keywords_parameter_node(node)
3177
+ bounds(node.operator_loc)
3178
+ on_op("**")
3179
+ bounds(node.keyword_loc)
3180
+ on_kw("nil")
2594
3181
  bounds(node.location)
2595
3182
  on_nokw_param(nil)
2596
3183
 
@@ -2623,7 +3210,11 @@ module Prism
2623
3210
  # ^^^^^^^
2624
3211
  def visit_optional_parameter_node(node)
2625
3212
  bounds(node.name_loc)
2626
- name = visit_token(node.name.to_s)
3213
+ name = on_ident(node.name.to_s)
3214
+
3215
+ bounds(node.operator_loc)
3216
+ on_op("=")
3217
+
2627
3218
  value = visit(node.value)
2628
3219
 
2629
3220
  [name, value]
@@ -2633,6 +3224,14 @@ module Prism
2633
3224
  # ^^^^^^
2634
3225
  def visit_or_node(node)
2635
3226
  left = visit(node.left)
3227
+
3228
+ bounds(node.operator_loc)
3229
+ if node.operator == "or"
3230
+ on_kw("or")
3231
+ else
3232
+ on_op("||")
3233
+ end
3234
+
2636
3235
  right = visit(node.right)
2637
3236
 
2638
3237
  bounds(node.location)
@@ -2656,9 +3255,19 @@ module Prism
2656
3255
 
2657
3256
  # Visit a destructured positional parameter node.
2658
3257
  private def visit_destructured_parameter_node(node)
3258
+ if node.lparen_loc
3259
+ bounds(node.lparen_loc)
3260
+ on_lparen("(")
3261
+ end
3262
+
2659
3263
  bounds(node.location)
2660
3264
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, false)
2661
3265
 
3266
+ if node.rparen_loc
3267
+ bounds(node.rparen_loc)
3268
+ on_rparen(")")
3269
+ end
3270
+
2662
3271
  bounds(node.lparen_loc)
2663
3272
  on_mlhs_paren(targets)
2664
3273
  end
@@ -2669,6 +3278,9 @@ module Prism
2669
3278
  # (1)
2670
3279
  # ^^^
2671
3280
  def visit_parentheses_node(node)
3281
+ bounds(node.opening_loc)
3282
+ on_lparen("(")
3283
+
2672
3284
  body =
2673
3285
  if node.body.nil?
2674
3286
  on_stmts_add(on_stmts_new, on_void_stmt)
@@ -2676,6 +3288,8 @@ module Prism
2676
3288
  visit(node.body)
2677
3289
  end
2678
3290
 
3291
+ bounds(node.closing_loc)
3292
+ on_rparen(")")
2679
3293
  bounds(node.location)
2680
3294
  on_paren(body)
2681
3295
  end
@@ -2683,8 +3297,15 @@ module Prism
2683
3297
  # foo => ^(bar)
2684
3298
  # ^^^^^^
2685
3299
  def visit_pinned_expression_node(node)
3300
+ bounds(node.operator_loc)
3301
+ on_op("^")
3302
+ bounds(node.lparen_loc)
3303
+ on_lparen("(")
3304
+
2686
3305
  expression = visit(node.expression)
2687
3306
 
3307
+ bounds(node.rparen_loc)
3308
+ on_rparen(")")
2688
3309
  bounds(node.location)
2689
3310
  on_begin(expression)
2690
3311
  end
@@ -2692,12 +3313,20 @@ module Prism
2692
3313
  # foo = 1 and bar => ^foo
2693
3314
  # ^^^^
2694
3315
  def visit_pinned_variable_node(node)
3316
+ bounds(node.operator_loc)
3317
+ on_op("^")
3318
+
2695
3319
  visit(node.variable)
2696
3320
  end
2697
3321
 
2698
3322
  # END {}
2699
3323
  # ^^^^^^
2700
3324
  def visit_post_execution_node(node)
3325
+ bounds(node.keyword_loc)
3326
+ on_kw("END")
3327
+ bounds(node.opening_loc)
3328
+ on_lbrace("{")
3329
+
2701
3330
  statements =
2702
3331
  if node.statements.nil?
2703
3332
  bounds(node.location)
@@ -2706,6 +3335,8 @@ module Prism
2706
3335
  visit(node.statements)
2707
3336
  end
2708
3337
 
3338
+ bounds(node.closing_loc)
3339
+ on_rbrace("}")
2709
3340
  bounds(node.location)
2710
3341
  on_END(statements)
2711
3342
  end
@@ -2713,6 +3344,11 @@ module Prism
2713
3344
  # BEGIN {}
2714
3345
  # ^^^^^^^^
2715
3346
  def visit_pre_execution_node(node)
3347
+ bounds(node.keyword_loc)
3348
+ on_kw("BEGIN")
3349
+ bounds(node.opening_loc)
3350
+ on_lbrace("{")
3351
+
2716
3352
  statements =
2717
3353
  if node.statements.nil?
2718
3354
  bounds(node.location)
@@ -2721,6 +3357,8 @@ module Prism
2721
3357
  visit(node.statements)
2722
3358
  end
2723
3359
 
3360
+ bounds(node.closing_loc)
3361
+ on_rbrace("}")
2724
3362
  bounds(node.location)
2725
3363
  on_BEGIN(statements)
2726
3364
  end
@@ -2728,7 +3366,7 @@ module Prism
2728
3366
  # The top-level program node.
2729
3367
  def visit_program_node(node)
2730
3368
  body = node.statements.body
2731
- body << nil if body.empty?
3369
+ body = [nil] if body.empty?
2732
3370
  statements = visit_statements_node_body(body)
2733
3371
 
2734
3372
  bounds(node.location)
@@ -2739,6 +3377,10 @@ module Prism
2739
3377
  # ^^^^
2740
3378
  def visit_range_node(node)
2741
3379
  left = visit(node.left)
3380
+
3381
+ bounds(node.operator_loc)
3382
+ on_op(node.operator)
3383
+
2742
3384
  right = visit(node.right)
2743
3385
 
2744
3386
  bounds(node.location)
@@ -2759,6 +3401,7 @@ module Prism
2759
3401
  # ^^^^
2760
3402
  def visit_redo_node(node)
2761
3403
  bounds(node.location)
3404
+ on_kw("redo")
2762
3405
  on_redo
2763
3406
  end
2764
3407
 
@@ -2801,6 +3444,9 @@ module Prism
2801
3444
  # foo rescue bar
2802
3445
  # ^^^^^^^^^^^^^^
2803
3446
  def visit_rescue_modifier_node(node)
3447
+ bounds(node.keyword_loc)
3448
+ on_kw("rescue")
3449
+
2804
3450
  expression = visit_write_value(node.expression)
2805
3451
  rescue_expression = visit(node.rescue_expression)
2806
3452
 
@@ -2811,6 +3457,9 @@ module Prism
2811
3457
  # begin; rescue; end
2812
3458
  # ^^^^^^^
2813
3459
  def visit_rescue_node(node)
3460
+ bounds(node.keyword_loc)
3461
+ on_kw("rescue")
3462
+
2814
3463
  exceptions =
2815
3464
  case node.exceptions.length
2816
3465
  when 0
@@ -2848,6 +3497,11 @@ module Prism
2848
3497
  end
2849
3498
  end
2850
3499
 
3500
+ if node.operator_loc
3501
+ bounds(node.operator_loc)
3502
+ on_op("=>")
3503
+ end
3504
+
2851
3505
  reference = visit(node.reference)
2852
3506
  statements =
2853
3507
  if node.statements.nil?
@@ -2869,12 +3523,15 @@ module Prism
2869
3523
  # def foo(*); end
2870
3524
  # ^
2871
3525
  def visit_rest_parameter_node(node)
3526
+ bounds(node.operator_loc)
3527
+ on_op("*")
3528
+
2872
3529
  if node.name_loc.nil?
2873
3530
  bounds(node.location)
2874
3531
  on_rest_param(nil)
2875
3532
  else
2876
3533
  bounds(node.name_loc)
2877
- on_rest_param(visit_token(node.name.to_s))
3534
+ on_rest_param(on_ident(node.name.to_s))
2878
3535
  end
2879
3536
  end
2880
3537
 
@@ -2882,6 +3539,7 @@ module Prism
2882
3539
  # ^^^^^
2883
3540
  def visit_retry_node(node)
2884
3541
  bounds(node.location)
3542
+ on_kw("retry")
2885
3543
  on_retry
2886
3544
  end
2887
3545
 
@@ -2891,6 +3549,9 @@ module Prism
2891
3549
  # return 1
2892
3550
  # ^^^^^^^^
2893
3551
  def visit_return_node(node)
3552
+ bounds(node.keyword_loc)
3553
+ on_kw("return")
3554
+
2894
3555
  if node.arguments.nil?
2895
3556
  bounds(node.location)
2896
3557
  on_return0
@@ -2917,9 +3578,17 @@ module Prism
2917
3578
  # class << self; end
2918
3579
  # ^^^^^^^^^^^^^^^^^^
2919
3580
  def visit_singleton_class_node(node)
3581
+ bounds(node.class_keyword_loc)
3582
+ on_kw("class")
3583
+ bounds(node.operator_loc)
3584
+ on_op("<<")
3585
+
2920
3586
  expression = visit(node.expression)
2921
3587
  bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body)
2922
3588
 
3589
+ bounds(node.end_keyword_loc)
3590
+ on_kw("end")
3591
+
2923
3592
  bounds(node.location)
2924
3593
  on_sclass(expression, bodystmt)
2925
3594
  end
@@ -2954,6 +3623,8 @@ module Prism
2954
3623
  # def foo(*); bar(*); end
2955
3624
  # ^
2956
3625
  def visit_splat_node(node)
3626
+ bounds(node.operator_loc)
3627
+ on_op("*")
2957
3628
  visit(node.expression)
2958
3629
  end
2959
3630
 
@@ -2976,24 +3647,66 @@ module Prism
2976
3647
  # "foo"
2977
3648
  # ^^^^^
2978
3649
  def visit_string_node(node)
2979
- if (content = node.content).empty?
2980
- bounds(node.location)
2981
- on_string_literal(on_string_content)
2982
- elsif (opening = node.opening) == "?"
2983
- bounds(node.location)
2984
- on_CHAR("?#{node.content}")
2985
- elsif opening.start_with?("<<~")
2986
- heredoc = visit_heredoc_string_node(node.to_interpolated)
3650
+ with_string_bounds(node) do
3651
+ if (content = node.content).empty?
3652
+ bounds(node.location)
3653
+ on_string_literal(on_string_content)
3654
+ elsif (opening = node.opening) == "?"
3655
+ bounds(node.location)
3656
+ on_CHAR("?#{node.content}")
3657
+ elsif opening.start_with?("<<~")
3658
+ heredoc = visit_heredoc_string_node(node.to_interpolated)
2987
3659
 
2988
- bounds(node.location)
2989
- on_string_literal(heredoc)
2990
- else
2991
- bounds(node.content_loc)
2992
- tstring_content = on_tstring_content(content)
3660
+ bounds(node.location)
3661
+ on_string_literal(heredoc)
3662
+ else
3663
+ bounds(node.content_loc)
3664
+ tstring_content = on_tstring_content(content)
2993
3665
 
2994
- bounds(node.location)
2995
- on_string_literal(on_string_add(on_string_content, tstring_content))
3666
+ bounds(node.location)
3667
+ on_string_literal(on_string_add(on_string_content, tstring_content))
3668
+ end
3669
+ end
3670
+ end
3671
+
3672
+ # Responsible for emitting the various string-like begin/end events
3673
+ private def with_string_bounds(node)
3674
+ # `foo "bar": baz` doesn't emit the closing location
3675
+ assoc = !(opening = node.opening)&.include?(":") && node.closing&.end_with?(":")
3676
+
3677
+ is_heredoc = opening&.start_with?("<<")
3678
+ if is_heredoc
3679
+ bounds(node.opening_loc)
3680
+ on_heredoc_beg(node.opening)
3681
+ elsif opening&.start_with?(":", "%s")
3682
+ bounds(node.opening_loc)
3683
+ on_symbeg(node.opening)
3684
+ elsif opening&.start_with?("`", "%x")
3685
+ bounds(node.opening_loc)
3686
+ on_backtick(node.opening)
3687
+ elsif opening && !opening.start_with?("?")
3688
+ bounds(node.opening_loc)
3689
+ on_tstring_beg(opening)
3690
+ end
3691
+
3692
+ result = yield
3693
+ if assoc
3694
+ if node.closing != ":"
3695
+ bounds(node.closing_loc)
3696
+ on_label_end(node.closing)
3697
+ end
3698
+ return result
2996
3699
  end
3700
+
3701
+ if is_heredoc
3702
+ bounds(node.closing_loc)
3703
+ on_heredoc_end(node.closing)
3704
+ elsif node.closing_loc
3705
+ bounds(node.closing_loc)
3706
+ on_tstring_end(node.closing)
3707
+ end
3708
+
3709
+ result
2997
3710
  end
2998
3711
 
2999
3712
  # Ripper gives back the escaped string content but strips out the common
@@ -3073,42 +3786,39 @@ module Prism
3073
3786
 
3074
3787
  # Visit a heredoc node that is representing a string.
3075
3788
  private def visit_heredoc_string_node(node)
3076
- bounds(node.opening_loc)
3077
- on_heredoc_beg(node.opening)
3078
-
3079
3789
  bounds(node.location)
3080
- result =
3081
- visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3082
- on_string_add(parts, part)
3083
- end
3084
-
3085
- bounds(node.closing_loc)
3086
- on_heredoc_end(node.closing)
3087
-
3088
- result
3790
+ visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3791
+ on_string_add(parts, part)
3792
+ end
3089
3793
  end
3090
3794
 
3091
3795
  # Visit a heredoc node that is representing an xstring.
3092
3796
  private def visit_heredoc_x_string_node(node)
3093
- bounds(node.opening_loc)
3094
- on_heredoc_beg(node.opening)
3095
-
3096
3797
  bounds(node.location)
3097
- result =
3098
- visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3099
- on_xstring_add(parts, part)
3100
- end
3101
-
3102
- bounds(node.closing_loc)
3103
- on_heredoc_end(node.closing)
3104
-
3105
- result
3798
+ visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3799
+ on_xstring_add(parts, part)
3800
+ end
3106
3801
  end
3107
3802
 
3108
3803
  # super(foo)
3109
3804
  # ^^^^^^^^^^
3110
3805
  def visit_super_node(node)
3111
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3806
+ bounds(node.keyword_loc)
3807
+ on_kw("super")
3808
+
3809
+ if node.lparen_loc
3810
+ bounds(node.lparen_loc)
3811
+ on_lparen("(")
3812
+ end
3813
+
3814
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3815
+
3816
+ if node.rparen_loc
3817
+ bounds(node.rparen_loc)
3818
+ on_rparen(")")
3819
+ end
3820
+
3821
+ block = visit(block_node)
3112
3822
 
3113
3823
  if !node.lparen_loc.nil?
3114
3824
  bounds(node.lparen_loc)
@@ -3118,35 +3828,38 @@ module Prism
3118
3828
  bounds(node.location)
3119
3829
  call = on_super(arguments)
3120
3830
 
3121
- if block.nil?
3122
- call
3123
- else
3831
+ if block_node
3124
3832
  bounds(node.block.location)
3125
3833
  on_method_add_block(call, block)
3834
+ else
3835
+ call
3126
3836
  end
3127
3837
  end
3128
3838
 
3129
3839
  # :foo
3130
3840
  # ^^^^
3131
3841
  def visit_symbol_node(node)
3132
- if (opening = node.opening)&.match?(/^%s|['"]:?$/)
3133
- bounds(node.value_loc)
3134
- content = on_string_content
3842
+ with_string_bounds(node) do
3843
+ if (opening = node.opening)&.match?(/^%s|['"]:?$/)
3844
+ bounds(node.content_loc)
3845
+ content = on_string_content
3135
3846
 
3136
- if !(value = node.value).empty?
3137
- content = on_string_add(content, on_tstring_content(value))
3138
- end
3847
+ if !(value = node.content).empty?
3848
+ content = on_string_add(content, on_tstring_content(value))
3849
+ end
3139
3850
 
3140
- on_dyna_symbol(content)
3141
- elsif (closing = node.closing) == ":"
3142
- bounds(node.location)
3143
- on_label("#{node.value}:")
3144
- elsif opening.nil? && node.closing_loc.nil?
3145
- bounds(node.value_loc)
3146
- on_symbol_literal(visit_token(node.value))
3147
- else
3148
- bounds(node.value_loc)
3149
- on_symbol_literal(on_symbol(visit_token(node.value)))
3851
+ bounds(node.location)
3852
+ on_dyna_symbol(content)
3853
+ elsif (closing = node.closing) == ":"
3854
+ bounds(node.location)
3855
+ on_label("#{node.content}:")
3856
+ elsif opening.nil? && node.closing_loc.nil?
3857
+ bounds(node.content_loc)
3858
+ on_symbol_literal(visit_token(node.content))
3859
+ else
3860
+ bounds(node.content_loc)
3861
+ on_symbol_literal(on_symbol(visit_token(node.content)))
3862
+ end
3150
3863
  end
3151
3864
  end
3152
3865
 
@@ -3160,6 +3873,9 @@ module Prism
3160
3873
  # undef foo
3161
3874
  # ^^^^^^^^^
3162
3875
  def visit_undef_node(node)
3876
+ bounds(node.keyword_loc)
3877
+ on_kw("undef")
3878
+
3163
3879
  names = visit_all(node.names)
3164
3880
 
3165
3881
  bounds(node.location)
@@ -3173,7 +3889,13 @@ module Prism
3173
3889
  # ^^^^^^^^^^^^^^
3174
3890
  def visit_unless_node(node)
3175
3891
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3892
+ bounds(node.unless_keyword_loc)
3893
+ on_kw("unless")
3176
3894
  predicate = visit(node.predicate)
3895
+ if node.then_keyword_loc
3896
+ bounds(node.then_keyword_loc)
3897
+ on_kw("then")
3898
+ end
3177
3899
  statements =
3178
3900
  if node.statements.nil?
3179
3901
  bounds(node.location)
@@ -3183,10 +3905,17 @@ module Prism
3183
3905
  end
3184
3906
  else_clause = visit(node.else_clause)
3185
3907
 
3908
+ if node.end_keyword_loc && !node.else_clause
3909
+ bounds(node.end_keyword_loc)
3910
+ on_kw("end")
3911
+ end
3912
+
3186
3913
  bounds(node.location)
3187
3914
  on_unless(predicate, statements, else_clause)
3188
3915
  else
3189
3916
  statements = visit(node.statements.body.first)
3917
+ bounds(node.unless_keyword_loc)
3918
+ on_kw("unless")
3190
3919
  predicate = visit(node.predicate)
3191
3920
 
3192
3921
  bounds(node.location)
@@ -3200,7 +3929,14 @@ module Prism
3200
3929
  # bar until foo
3201
3930
  # ^^^^^^^^^^^^^
3202
3931
  def visit_until_node(node)
3932
+ bounds(node.until_keyword_loc)
3933
+ on_kw("until")
3934
+
3203
3935
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3936
+ if node.do_keyword_loc
3937
+ bounds(node.do_keyword_loc)
3938
+ on_kw("do")
3939
+ end
3204
3940
  predicate = visit(node.predicate)
3205
3941
  statements =
3206
3942
  if node.statements.nil?
@@ -3210,6 +3946,11 @@ module Prism
3210
3946
  visit(node.statements)
3211
3947
  end
3212
3948
 
3949
+ if node.end_keyword_loc
3950
+ bounds(node.end_keyword_loc)
3951
+ on_kw("end")
3952
+ end
3953
+
3213
3954
  bounds(node.location)
3214
3955
  on_until(predicate, statements)
3215
3956
  else
@@ -3227,7 +3968,14 @@ module Prism
3227
3968
  # This is a special case where we're not going to call on_when directly
3228
3969
  # because we don't have access to the subsequent. Instead, we'll return
3229
3970
  # the component parts and let the parent node handle it.
3971
+ bounds(node.when_keyword_loc)
3972
+ on_kw("when")
3973
+
3230
3974
  conditions = visit_arguments(node.conditions)
3975
+ if node.then_keyword_loc
3976
+ bounds(node.then_keyword_loc)
3977
+ on_kw("then")
3978
+ end
3231
3979
  statements =
3232
3980
  if node.statements.nil?
3233
3981
  bounds(node.location)
@@ -3246,7 +3994,17 @@ module Prism
3246
3994
  # ^^^^^^^^^^^^^
3247
3995
  def visit_while_node(node)
3248
3996
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3997
+ bounds(node.while_keyword_loc)
3998
+ on_kw("while")
3999
+ if node.do_keyword_loc
4000
+ bounds(node.do_keyword_loc)
4001
+ on_kw("do")
4002
+ end
3249
4003
  predicate = visit(node.predicate)
4004
+ if node.end_keyword_loc
4005
+ bounds(node.end_keyword_loc)
4006
+ on_kw("end")
4007
+ end
3250
4008
  statements =
3251
4009
  if node.statements.nil?
3252
4010
  bounds(node.location)
@@ -3259,6 +4017,8 @@ module Prism
3259
4017
  on_while(predicate, statements)
3260
4018
  else
3261
4019
  statements = visit(node.statements.body.first)
4020
+ bounds(node.while_keyword_loc)
4021
+ on_kw("while")
3262
4022
  predicate = visit(node.predicate)
3263
4023
 
3264
4024
  bounds(node.location)
@@ -3269,20 +4029,22 @@ module Prism
3269
4029
  # `foo`
3270
4030
  # ^^^^^
3271
4031
  def visit_x_string_node(node)
3272
- if node.unescaped.empty?
3273
- bounds(node.location)
3274
- on_xstring_literal(on_xstring_new)
3275
- elsif node.opening.start_with?("<<~")
3276
- heredoc = visit_heredoc_x_string_node(node.to_interpolated)
4032
+ with_string_bounds(node) do
4033
+ if node.unescaped.empty?
4034
+ bounds(node.location)
4035
+ on_xstring_literal(on_xstring_new)
4036
+ elsif node.opening.start_with?("<<~")
4037
+ heredoc = visit_heredoc_x_string_node(node.to_interpolated)
3277
4038
 
3278
- bounds(node.location)
3279
- on_xstring_literal(heredoc)
3280
- else
3281
- bounds(node.content_loc)
3282
- content = on_tstring_content(node.content)
4039
+ bounds(node.location)
4040
+ on_xstring_literal(heredoc)
4041
+ else
4042
+ bounds(node.content_loc)
4043
+ content = on_tstring_content(node.content)
3283
4044
 
3284
- bounds(node.location)
3285
- on_xstring_literal(on_xstring_add(on_xstring_new, content))
4045
+ bounds(node.location)
4046
+ on_xstring_literal(on_xstring_add(on_xstring_new, content))
4047
+ end
3286
4048
  end
3287
4049
  end
3288
4050
 
@@ -3292,10 +4054,18 @@ module Prism
3292
4054
  # yield 1
3293
4055
  # ^^^^^^^
3294
4056
  def visit_yield_node(node)
4057
+ bounds(node.keyword_loc)
4058
+ on_kw("yield")
4059
+
3295
4060
  if node.arguments.nil? && node.lparen_loc.nil?
3296
4061
  bounds(node.location)
3297
4062
  on_yield0
3298
4063
  else
4064
+ if node.lparen_loc
4065
+ bounds(node.lparen_loc)
4066
+ on_lparen("(")
4067
+ end
4068
+
3299
4069
  arguments =
3300
4070
  if node.arguments.nil?
3301
4071
  bounds(node.location)
@@ -3305,6 +4075,8 @@ module Prism
3305
4075
  end
3306
4076
 
3307
4077
  unless node.lparen_loc.nil?
4078
+ bounds(node.rparen_loc)
4079
+ on_rparen(")")
3308
4080
  bounds(node.lparen_loc)
3309
4081
  arguments = on_paren(arguments)
3310
4082
  end
@@ -3318,7 +4090,11 @@ module Prism
3318
4090
 
3319
4091
  # Lazily initialize the parse result.
3320
4092
  def result
3321
- @result ||= Prism.parse(source, partial_script: true, version: "current")
4093
+ @result ||= Prism.parse(source, partial_script: true, version: "current", freeze: true, encoding: source.encoding)
4094
+ end
4095
+
4096
+ def line_and_column_cache
4097
+ @line_and_column_cache ||= LineAndColumnCache.new(result.source)
3322
4098
  end
3323
4099
 
3324
4100
  ##########################################################################
@@ -3339,30 +4115,34 @@ module Prism
3339
4115
  # Visit the string content of a particular node. This method is used to
3340
4116
  # split into the various token types.
3341
4117
  def visit_token(token, allow_keywords = true)
3342
- case token
3343
- when "."
4118
+ if token == "."
3344
4119
  on_period(token)
3345
- when "`"
4120
+ elsif token == "`"
3346
4121
  on_backtick(token)
3347
- when *(allow_keywords ? KEYWORDS : [])
4122
+ elsif allow_keywords && KEYWORDS.include?(token)
3348
4123
  on_kw(token)
3349
- when /^_/
4124
+ elsif token.start_with?("_")
3350
4125
  on_ident(token)
3351
- when /^[[:upper:]]\w*$/
4126
+ elsif token.match?(/^[[:upper:]]\w*$/)
3352
4127
  on_const(token)
3353
- when /^@@/
4128
+ elsif token.start_with?("@@")
3354
4129
  on_cvar(token)
3355
- when /^@/
4130
+ elsif token.start_with?("@")
3356
4131
  on_ivar(token)
3357
- when /^\$/
4132
+ elsif token.start_with?("$")
3358
4133
  on_gvar(token)
3359
- when /^[[:punct:]]/
4134
+ elsif token.match?(/^[[:punct:]]/)
3360
4135
  on_op(token)
3361
4136
  else
3362
4137
  on_ident(token)
3363
4138
  end
3364
4139
  end
3365
4140
 
4141
+ # Visit either `.`, `&.`, or `::`.
4142
+ def visit_call_operator(token)
4143
+ token == "." ? on_period(token) : on_op(token)
4144
+ end
4145
+
3366
4146
  # Visit a node that represents a number. We need to explicitly handle the
3367
4147
  # unary - operator.
3368
4148
  def visit_number_node(node)
@@ -3370,6 +4150,9 @@ module Prism
3370
4150
  location = node.location
3371
4151
 
3372
4152
  if slice[0] == "-"
4153
+ bounds(location.copy(length: 1))
4154
+ on_op("-")
4155
+
3373
4156
  bounds(location.copy(start_offset: location.start_offset + 1))
3374
4157
  value = yield slice[1..-1]
3375
4158
 
@@ -3418,26 +4201,24 @@ module Prism
3418
4201
 
3419
4202
  # This method is responsible for updating lineno and column information
3420
4203
  # to reflect the current node.
3421
- #
3422
- # This method could be drastically improved with some caching on the start
3423
- # of every line, but for now it's good enough.
3424
4204
  def bounds(location)
3425
- @lineno = location.start_line
3426
- @column = location.start_column
4205
+ @lineno, @column = line_and_column_cache.line_and_column(location.start_offset)
3427
4206
  end
3428
4207
 
4208
+ # :startdoc:
4209
+
3429
4210
  ##########################################################################
3430
4211
  # Ripper interface
3431
4212
  ##########################################################################
3432
4213
 
3433
4214
  # :stopdoc:
3434
4215
  def _dispatch_0; end
3435
- def _dispatch_1(_); end
3436
- def _dispatch_2(_, _); end
3437
- def _dispatch_3(_, _, _); end
3438
- def _dispatch_4(_, _, _, _); end
3439
- def _dispatch_5(_, _, _, _, _); end
3440
- def _dispatch_7(_, _, _, _, _, _, _); end
4216
+ def _dispatch_1(arg); arg end
4217
+ def _dispatch_2(arg, _); arg end
4218
+ def _dispatch_3(arg, _, _); arg end
4219
+ def _dispatch_4(arg, _, _, _); arg end
4220
+ def _dispatch_5(arg, _, _, _, _); arg end
4221
+ def _dispatch_7(arg, _, _, _, _, _, _); arg end
3441
4222
  # :startdoc:
3442
4223
 
3443
4224
  #