Repository: ruby/yarp
Pull request #1283 (2023-08-18 15:57:22)

Update fixtures
* Updating our fixtures to use the latest versions from the various test suites that we vendor.
* Fixing locals tests when you have an interpolated regular expression with the `o` modifier.
* Ensuring the correct end location for `if/elsif` chains with no `else`.
* Allow a `FOCUS` environment variable for the parser test to specify a single fixture to work on.
* Fix newline tests when there is an `__END__` to not include those offsets in the test.
* Ensure you still get an `__END__` when it's at the very end of the file.


Patch:
diff --git a/lib/yarp.rb b/lib/yarp.rb
index aa92113d..1d56c779 100644
--- a/lib/yarp.rb
+++ b/lib/yarp.rb
@@ -457,6 +457,8 @@ module YARP
           locals << []
         when PostExecutionNode
           locals.push([], [])
+        when InterpolatedRegularExpressionNode
+          locals << [] if node.once?
         end
 
         stack.concat(node.child_nodes.compact)
diff --git a/rakelib/seattlerb.rake b/rakelib/seattlerb.rake
index aa9a127a..4618a46f 100644
--- a/rakelib/seattlerb.rake
+++ b/rakelib/seattlerb.rake
@@ -1,16 +1,5 @@
 # frozen_string_literal: true
 
-# These files are not valid Ruby
-known_failures = %w(
-  test/fixtures/seattlerb/begin_else_return_value.txt
-  test/fixtures/seattlerb/block_yield.txt
-  test/fixtures/seattlerb/bug_begin_else.txt
-  test/fixtures/seattlerb/bug170.txt
-  test/fixtures/seattlerb/call_block_arg_unnamed.txt
-  test/fixtures/seattlerb/iter_array_curly.txt
-  test/fixtures/seattlerb/magic_encoding_comment__bad.txt
-)
-
 namespace :seattlerb do
   desc "Ensure there's a local copy of seattlerb/ruby_parser"
   file "tmp/seattlerb" do
@@ -24,6 +13,35 @@ namespace :seattlerb do
 
   desc "Import the seattlerb tests"
   task import: ["tmp/seattlerb", "test/fixtures/seattlerb"] do
+    require "base64"
+
+    # These files are not valid Ruby
+    known_failures = %w[
+      test/fixtures/seattlerb/args_dstar__anon_solo.txt
+      test/fixtures/seattlerb/args_dstar__anon_trailing.txt
+      test/fixtures/seattlerb/args_star__anon_solo.txt
+      test/fixtures/seattlerb/args_star__anon_trailing.txt
+      test/fixtures/seattlerb/begin_else_return_value.txt
+      test/fixtures/seattlerb/block_yield.txt
+      test/fixtures/seattlerb/bug_begin_else.txt
+      test/fixtures/seattlerb/bug170.txt
+      test/fixtures/seattlerb/call_block_arg_unnamed.txt
+      test/fixtures/seattlerb/iter_array_curly.txt
+      test/fixtures/seattlerb/magic_encoding_comment__bad.txt
+    ]
+
+    # Cleaning up some file names
+    renames = [
+      "aGVyZWRvY193dGZfSV9oYXRlX3lvdQ==\n",
+      "aV9mdWNraW5nX2hhdGVf\n",
+      "aV9oYXZlX25vX2ZyZWFraW5fY2x1ZQ==\n",
+      "a2lsbF9tZQ==\n",
+      "bW90aGVyZnVja2lu\n",
+      "d3RmX2lfaGF0ZV95b3U=\n",
+      "d3Rm\n",
+      "em9tZ19zb21ldGltZXNfaV9oYXRlX3RoaXNfcHJvamVjdA==\n"
+    ].map { Base64.decode64(_1) }
+
     # The license is in the README
     cp "tmp/seattlerb/README.rdoc", "test/fixtures/seattlerb/README.rdoc"
 
@@ -36,7 +54,7 @@ namespace :seattlerb do
 
       def assert_parse(source, _)
         entry = caller.find { _1.include?("test_ruby_parser.rb") }
-        name = entry[/\d+:in `(?:block in )?(?:assert_|test_)?(.+)'/, 1]
+        name = entry[/\d+:in `(?:block (?:\(\d+ levels\) )?in )?(?:assert_|test_|<module:)?(.+?)\>?'/, 1]
 
         COLLECTED[name] << source
         super
@@ -46,7 +64,17 @@ namespace :seattlerb do
     RubyParserTestCase.prepend(Hook)
     Minitest.after_run do
       Hook::COLLECTED.each do |(name, codes)|
-        filepath = "test/fixtures/seattlerb/#{name.delete!('?')}.txt"
+        name = name.delete("?")
+
+        # Clean up the names a bit
+        renames.each_with_index do |rename, index|
+          if name.start_with?(rename)
+            name = "difficult#{index}_#{name.delete_prefix(rename)}"
+            break
+          end
+        end
+
+        filepath = "test/fixtures/seattlerb/#{name}.txt"
         File.write(filepath, "#{codes.uniq.sort.join("\n\n")}\n")
       end
 
diff --git a/rakelib/unparser.rake b/rakelib/unparser.rake
index 94f32c56..bd38388d 100644
--- a/rakelib/unparser.rake
+++ b/rakelib/unparser.rake
@@ -1,10 +1,5 @@
 # frozen_string_literal: true
 
-# These files are not valid Ruby
-known_failures = %w(
-  test/fixtures/unparser/corpus/literal/binary.txt
-)
-
 namespace :unparser do
   desc "Ensure there's a local copy of mbj/unparser"
   file "tmp/unparser" do
@@ -30,6 +25,11 @@ namespace :unparser do
     filepath = "test/fixtures/unparser/corpus/literal/pattern.txt"
     File.write(filepath, File.read(filepath).gsub("\"\#{\"a\"}\"", "\"a\""))
 
+    # These files are not valid Ruby
+    known_failures = %w[
+      test/fixtures/unparser/corpus/literal/binary.txt
+    ]
+
     # Remove all invalid Ruby files
     known_failures.each { rm _1 }
   end
diff --git a/rakelib/whitequark.rake b/rakelib/whitequark.rake
index 16461129..ffa871c8 100644
--- a/rakelib/whitequark.rake
+++ b/rakelib/whitequark.rake
@@ -11,13 +11,6 @@
 # APIs provided to the test suite (for example the ParseHelper module below).
 # This is obviously relatively brittle, but it's effective for now.
 
-# These files are not valid Ruby
-known_failures = %w(
-  test/fixtures/whitequark/range_endless.txt
-  test/fixtures/whitequark/control_meta_escape_chars_in_regexp__since_31.txt
-  test/fixtures/whitequark/pattern_match.txt
-)
-
 namespace :whitequark do
   desc "Ensure there's a local copy of whitequark/parser"
   file "tmp/whitequark" do
@@ -85,6 +78,13 @@ namespace :whitequark do
         File.write("test/fixtures/whitequark/#{name}.txt", "#{codes.sort.join("\n\n")}\n")
       end
 
+      # These files are not valid Ruby
+      known_failures = %w[
+        test/fixtures/whitequark/range_endless.txt
+        test/fixtures/whitequark/control_meta_escape_chars_in_regexp__since_31.txt
+        test/fixtures/whitequark/pattern_match.txt
+      ]
+
       # Remove all invalid Ruby files
       known_failures.each { rm _1 }
     end
diff --git a/src/yarp.c b/src/yarp.c
index ed777a45..13f1dd86 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -6494,11 +6494,13 @@ parser_lex(yp_parser_t *parser) {
 
                 // % %= %i %I %q %Q %w %W
                 case '%': {
-                    // If there is no subsequent character then we have an invalid token. We're
-                    // going to say it's the percent operator because we don't want to move into the
-                    // string lex mode unnecessarily.
-                    if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
-                        yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected end of input");
+                    // In a BEG state, if you encounter a % then you must be
+                    // starting something. In this case if there is no
+                    // subsequent character then we have an invalid token. We're
+                    // going to say it's the percent operator because we don't
+                    // want to move into the string lex mode unnecessarily.
+                    if (lex_state_beg_p(parser) && (parser->current.end >= parser->end)) {
+                        yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "unexpected end of input");
                         LEX(YP_TOKEN_PERCENT);
                     }
 
@@ -6671,7 +6673,7 @@ parser_lex(yp_parser_t *parser) {
                         ((parser->current.end - parser->current.start) == 7) &&
                         current_token_starts_line(parser) &&
                         (strncmp(parser->current.start, "__END__", 7) == 0) &&
-                        (*parser->current.end == '\n' || (*parser->current.end == '\r' && parser->current.end[1] == '\n'))
+                        (parser->current.end == parser->end || *parser->current.end == '\n' || (*parser->current.end == '\r' && parser->current.end[1] == '\n'))
                     ) {
                         parser->current.end = parser->end;
                         parser->current.type = YP_TOKEN___END__;
@@ -9011,7 +9013,7 @@ parse_conditional(yp_parser_t *parser, yp_context_t context) {
     }
 
     yp_token_t end_keyword = not_provided(parser);
-    yp_node_t *parent;
+    yp_node_t *parent = NULL;
 
     switch (context) {
         case YP_CONTEXT_IF:
@@ -9021,7 +9023,6 @@ parse_conditional(yp_parser_t *parser, yp_context_t context) {
             parent = (yp_node_t *) yp_unless_node_create(parser, &keyword, predicate, statements);
             break;
         default:
-            parent = NULL;
             assert(false && "unreachable");
             break;
     }
@@ -9067,50 +9068,49 @@ parse_conditional(yp_parser_t *parser, yp_context_t context) {
         switch (context) {
             case YP_CONTEXT_IF:
                 ((yp_if_node_t *) current)->consequent = (yp_node_t *) else_node;
-                // Recurse down if nodes setting the appropriate end location in
-                // all cases.
-                yp_node_t *recursing_node = parent;
-                bool recursing = true;
-
-                while (recursing) {
-                    switch (YP_NODE_TYPE(recursing_node)) {
-                        case YP_NODE_IF_NODE:
-                            yp_if_node_end_keyword_loc_set((yp_if_node_t *) recursing_node, &parser->previous);
-                            recursing_node = ((yp_if_node_t *) recursing_node)->consequent;
-                            break;
-                        case YP_NODE_ELSE_NODE:
-                            yp_else_node_end_keyword_loc_set((yp_else_node_t *) recursing_node, &parser->previous);
-                            recursing = false;
-                            break;
-                        default: {
-                            recursing = false;
-                            break;
-                        }
-                    }
-                }
                 break;
             case YP_CONTEXT_UNLESS:
                 ((yp_unless_node_t *) parent)->consequent = else_node;
-                yp_unless_node_end_keyword_loc_set((yp_unless_node_t *) parent, &parser->previous);
                 break;
             default:
                 assert(false && "unreachable");
                 break;
         }
     } else {
-        expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `if` statement.");
+        expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close conditional statement.");
+    }
 
-        switch (context) {
-            case YP_CONTEXT_IF:
-                yp_if_node_end_keyword_loc_set((yp_if_node_t *) parent, &parser->previous);
-                break;
-            case YP_CONTEXT_UNLESS:
-                yp_unless_node_end_keyword_loc_set((yp_unless_node_t *) parent, &parser->previous);
-                break;
-            default:
-                assert(false && "unreachable");
-                break;
+    // Set the appropriate end location for all of the nodes in the subtree.
+    switch (context) {
+        case YP_CONTEXT_IF: {
+            yp_node_t *current = parent;
+            bool recursing = true;
+
+            while (recursing) {
+                switch (YP_NODE_TYPE(current)) {
+                    case YP_NODE_IF_NODE:
+                        yp_if_node_end_keyword_loc_set((yp_if_node_t *) current, &parser->previous);
+                        current = ((yp_if_node_t *) current)->consequent;
+                        recursing = current != NULL;
+                        break;
+                    case YP_NODE_ELSE_NODE:
+                        yp_else_node_end_keyword_loc_set((yp_else_node_t *) current, &parser->previous);
+                        recursing = false;
+                        break;
+                    default: {
+                        recursing = false;
+                        break;
+                    }
+                }
+            }
+            break;
         }
+        case YP_CONTEXT_UNLESS:
+            yp_unless_node_end_keyword_loc_set((yp_unless_node_t *) parent, &parser->previous);
+            break;
+        default:
+            assert(false && "unreachable");
+            break;
     }
 
     return parent;


Test patch:
diff --git a/test/errors_test.rb b/test/errors_test.rb
index 355e7a8a..3b880eda 100644
--- a/test/errors_test.rb
+++ b/test/errors_test.rb
@@ -155,13 +155,6 @@ class ErrorsTest < Test::Unit::TestCase
     ]
   end
 
-  def test_unterminated_argument_expression
-    assert_errors expression('a %'), 'a %', [
-      ["Unexpected end of input", 2..3],
-      ["Expected a value after the operator.", 3..3],
-    ]
-  end
-
   def test_1_2_3
     assert_errors expression("(1, 2, 3)"), "(1, 2, 3)", [
       ["Expected to be able to parse an expression.", 2..2],
diff --git a/test/fixtures/regex.txt b/test/fixtures/regex.txt
index fe7b848b..1e917e7d 100644
--- a/test/fixtures/regex.txt
+++ b/test/fixtures/regex.txt
@@ -26,3 +26,5 @@ foo /bar/
 /(?#\))/ =~ "hi"
 
 %r#pound#
+
+/aaa #{bbb}/o
diff --git a/test/fixtures/seattlerb/TestRubyParserShared.txt b/test/fixtures/seattlerb/TestRubyParserShared.txt
new file mode 100644
index 00000000..c55b3e1f
--- /dev/null
+++ b/test/fixtures/seattlerb/TestRubyParserShared.txt
@@ -0,0 +1,92 @@
+%I[
+
+
+]
+
+%I[
+line2
+line3
+]
+
+%W[
+
+
+]
+
+%W[
+line2
+line3
+]
+
+%i[
+
+
+]
+
+%i[
+line2
+line3
+]
+
+%r[
+
+
+]
+
+%w[
+
+
+]
+
+%w[
+line2
+line3
+]
+
+[
+:line2,
+:line3
+]
+
+class X         # line 1
+  def self.y(a, # line 2
+        b)      # line 3
+    a + b       # line 4
+  end           # line 5
+end             # line 6
+
+
+class X    # line 1
+  class Y  # line 2
+    Z = 42 # line 3
+  end      # line 4
+end        # line 5
+
+
+class X    # line 1
+  def y(a, # line 2
+        b) # line 3
+    a + b  # line 4
+  end      # line 5
+end        # line 6
+
+
+module X
+  X = [
+    :line3,
+    :line4,
+  ]
+end
+
+
+module X   # line 1
+  module Y # line 2
+    Z = 42 # line 3
+  end      # line 4
+end        # line 5
+
+
+x(
+:line2,
+:line3
+)
diff --git a/test/fixtures/seattlerb/case_in.txt b/test/fixtures/seattlerb/case_in.txt
index f28c2863..0835da09 100644
--- a/test/fixtures/seattlerb/case_in.txt
+++ b/test/fixtures/seattlerb/case_in.txt
@@ -1,5 +1,5 @@
 case :a
-in  "b":
+in  "b": 
 end
 
 case :a
diff --git a/test/fixtures/seattlerb/defn_forward_args__no_parens.txt b/test/fixtures/seattlerb/defn_forward_args__no_parens.txt
new file mode 100644
index 00000000..2d34077c
--- /dev/null
+++ b/test/fixtures/seattlerb/defn_forward_args__no_parens.txt
@@ -0,0 +1,3 @@
+def f ...
+  m(...)
+end
diff --git a/test/fixtures/seattlerb/heredoc_wtf_I_hate_you.txt b/test/fixtures/seattlerb/difficult0_.txt
similarity index 100%
rename from test/fixtures/seattlerb/heredoc_wtf_I_hate_you.txt
rename to test/fixtures/seattlerb/difficult0_.txt
diff --git a/test/fixtures/seattlerb/i_fucking_hate_line_numbers.txt b/test/fixtures/seattlerb/difficult1_line_numbers.txt
similarity index 100%
rename from test/fixtures/seattlerb/i_fucking_hate_line_numbers.txt
rename to test/fixtures/seattlerb/difficult1_line_numbers.txt
diff --git a/test/fixtures/seattlerb/i_fucking_hate_line_numbers2.txt b/test/fixtures/seattlerb/difficult1_line_numbers2.txt
similarity index 82%
rename from test/fixtures/seattlerb/i_fucking_hate_line_numbers2.txt
rename to test/fixtures/seattlerb/difficult1_line_numbers2.txt
index 898a70fb..19645624 100644
--- a/test/fixtures/seattlerb/i_fucking_hate_line_numbers2.txt
+++ b/test/fixtures/seattlerb/difficult1_line_numbers2.txt
@@ -1,5 +1,5 @@
 if true then
-  p('a')
+  p("a")
   b = 1
   p b
   c =1
diff --git a/test/fixtures/seattlerb/i_have_no_freakin_clue.txt b/test/fixtures/seattlerb/difficult2_.txt
similarity index 100%
rename from test/fixtures/seattlerb/i_have_no_freakin_clue.txt
rename to test/fixtures/seattlerb/difficult2_.txt
diff --git a/test/fixtures/seattlerb/kill_me.txt b/test/fixtures/seattlerb/difficult3_.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me.txt
rename to test/fixtures/seattlerb/difficult3_.txt
diff --git a/test/fixtures/seattlerb/kill_me2.txt b/test/fixtures/seattlerb/difficult3_2.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me2.txt
rename to test/fixtures/seattlerb/difficult3_2.txt
diff --git a/test/fixtures/seattlerb/kill_me3.txt b/test/fixtures/seattlerb/difficult3_3.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me3.txt
rename to test/fixtures/seattlerb/difficult3_3.txt
diff --git a/test/fixtures/seattlerb/kill_me4.txt b/test/fixtures/seattlerb/difficult3_4.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me4.txt
rename to test/fixtures/seattlerb/difficult3_4.txt
diff --git a/test/fixtures/seattlerb/kill_me5.txt b/test/fixtures/seattlerb/difficult3_5.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me5.txt
rename to test/fixtures/seattlerb/difficult3_5.txt
diff --git a/test/fixtures/seattlerb/kill_me_10.txt b/test/fixtures/seattlerb/difficult3__10.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_10.txt
rename to test/fixtures/seattlerb/difficult3__10.txt
diff --git a/test/fixtures/seattlerb/kill_me_11.txt b/test/fixtures/seattlerb/difficult3__11.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_11.txt
rename to test/fixtures/seattlerb/difficult3__11.txt
diff --git a/test/fixtures/seattlerb/kill_me_12.txt b/test/fixtures/seattlerb/difficult3__12.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_12.txt
rename to test/fixtures/seattlerb/difficult3__12.txt
diff --git a/test/fixtures/seattlerb/kill_me_6.txt b/test/fixtures/seattlerb/difficult3__6.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_6.txt
rename to test/fixtures/seattlerb/difficult3__6.txt
diff --git a/test/fixtures/seattlerb/kill_me_7.txt b/test/fixtures/seattlerb/difficult3__7.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_7.txt
rename to test/fixtures/seattlerb/difficult3__7.txt
diff --git a/test/fixtures/seattlerb/kill_me_8.txt b/test/fixtures/seattlerb/difficult3__8.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_8.txt
rename to test/fixtures/seattlerb/difficult3__8.txt
diff --git a/test/fixtures/seattlerb/kill_me_9.txt b/test/fixtures/seattlerb/difficult3__9.txt
similarity index 100%
rename from test/fixtures/seattlerb/kill_me_9.txt
rename to test/fixtures/seattlerb/difficult3__9.txt
diff --git a/test/fixtures/seattlerb/motherfuckin_leading_dots.txt b/test/fixtures/seattlerb/difficult4__leading_dots.txt
similarity index 100%
rename from test/fixtures/seattlerb/motherfuckin_leading_dots.txt
rename to test/fixtures/seattlerb/difficult4__leading_dots.txt
diff --git a/test/fixtures/seattlerb/motherfuckin_leading_dots2.txt b/test/fixtures/seattlerb/difficult4__leading_dots2.txt
similarity index 100%
rename from test/fixtures/seattlerb/motherfuckin_leading_dots2.txt
rename to test/fixtures/seattlerb/difficult4__leading_dots2.txt
diff --git a/test/fixtures/seattlerb/wtf.txt b/test/fixtures/seattlerb/difficult6_.txt
similarity index 100%
rename from test/fixtures/seattlerb/wtf.txt
rename to test/fixtures/seattlerb/difficult6_.txt
diff --git a/test/fixtures/seattlerb/wtf_7.txt b/test/fixtures/seattlerb/difficult6__7.txt
similarity index 100%
rename from test/fixtures/seattlerb/wtf_7.txt
rename to test/fixtures/seattlerb/difficult6__7.txt
diff --git a/test/fixtures/seattlerb/wtf_8.txt b/test/fixtures/seattlerb/difficult6__8.txt
similarity index 100%
rename from test/fixtures/seattlerb/wtf_8.txt
rename to test/fixtures/seattlerb/difficult6__8.txt
diff --git a/test/fixtures/seattlerb/zomg_sometimes_i_hate_this_project.txt b/test/fixtures/seattlerb/difficult7_.txt
similarity index 100%
rename from test/fixtures/seattlerb/zomg_sometimes_i_hate_this_project.txt
rename to test/fixtures/seattlerb/difficult7_.txt
diff --git a/test/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt b/test/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt
index 0d89134c..3f919829 100644
--- a/test/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt
+++ b/test/fixtures/seattlerb/heredoc_squiggly_visually_blank_lines.txt
@@ -1,6 +1,6 @@
 a = <<~EOF
   x
-
+ 
   z
 EOF
 
diff --git a/test/fixtures/seattlerb/module_comments.txt b/test/fixtures/seattlerb/module_comments.txt
index f8d5aae5..cecb717c 100644
--- a/test/fixtures/seattlerb/module_comments.txt
+++ b/test/fixtures/seattlerb/module_comments.txt
@@ -1,5 +1,5 @@
 # blah 1
-
+  
   # blah 2
 
 module X
diff --git a/test/fixtures/seattlerb/parse_line_trailing_newlines.txt b/test/fixtures/seattlerb/parse_line_trailing_newlines.txt
index 422c2b7a..afa826fb 100644
--- a/test/fixtures/seattlerb/parse_line_trailing_newlines.txt
+++ b/test/fixtures/seattlerb/parse_line_trailing_newlines.txt
@@ -1,2 +1,2 @@
-a
+a 
 b
diff --git a/test/fixtures/seattlerb/pct_Q_backslash_nl.txt b/test/fixtures/seattlerb/pct_Q_backslash_nl.txt
index d88e1fd2..4420560d 100644
--- a/test/fixtures/seattlerb/pct_Q_backslash_nl.txt
+++ b/test/fixtures/seattlerb/pct_Q_backslash_nl.txt
@@ -1,2 +1,2 @@
-%Q{ \
+%q{ \
 }
diff --git a/test/fixtures/seattlerb/slashy_newlines_within_string.txt b/test/fixtures/seattlerb/slashy_newlines_within_string.txt
index cdf7a70c..421989c7 100644
--- a/test/fixtures/seattlerb/slashy_newlines_within_string.txt
+++ b/test/fixtures/seattlerb/slashy_newlines_within_string.txt
@@ -4,4 +4,4 @@ puts "hello\
  friend"
 
 a + b
-
+    
diff --git a/test/fixtures/unparser/corpus/literal/send.txt b/test/fixtures/unparser/corpus/literal/send.txt
index 4361cf73..1e9c2a94 100644
--- a/test/fixtures/unparser/corpus/literal/send.txt
+++ b/test/fixtures/unparser/corpus/literal/send.txt
@@ -81,3 +81,4 @@ self.foo=:bar
 x(**foo)
 foo&.!
 foo.~(b)
+a&.+(b)
diff --git a/test/fixtures/whitequark/dedenting_heredoc.txt b/test/fixtures/whitequark/dedenting_heredoc.txt
index 2840ec3e..84937d84 100644
--- a/test/fixtures/whitequark/dedenting_heredoc.txt
+++ b/test/fixtures/whitequark/dedenting_heredoc.txt
@@ -49,7 +49,7 @@ E
 
 p <<~E
   x
-
+    
   y
 E
 
diff --git a/test/fixtures/whitequark/interp_digit_var.txt b/test/fixtures/whitequark/interp_digit_var.txt
index 37487ddb..1ae5a2e3 100644
--- a/test/fixtures/whitequark/interp_digit_var.txt
+++ b/test/fixtures/whitequark/interp_digit_var.txt
@@ -1,66 +1,66 @@
- "#@1"
+ "#@1" 
 
- "#@@1"
+ "#@@1" 
 
- %I[#@1]
+ %I[#@1] 
 
- %I[#@@1]
+ %I[#@@1] 
 
- %Q{#@1}
+ %Q{#@1} 
 
- %Q{#@@1}
+ %Q{#@@1} 
 
- %W[#@1]
+ %W[#@1] 
 
- %W[#@@1]
+ %W[#@@1] 
 
- %i[ #@1 ]
+ %i[ #@1 ] 
 
- %i[ #@@1 ]
+ %i[ #@@1 ] 
 
- %q{#@1}
+ %q{#@1} 
 
- %q{#@@1}
+ %q{#@@1} 
 
- %r{#@1}
+ %r{#@1} 
 
- %r{#@@1}
+ %r{#@@1} 
 
- %s{#@1}
+ %s{#@1} 
 
- %s{#@@1}
+ %s{#@@1} 
 
- %w[ #@1 ]
+ %w[ #@1 ] 
 
- %w[ #@@1 ]
+ %w[ #@@1 ] 
 
- %x{#@1}
+ %x{#@1} 
 
- %x{#@@1}
+ %x{#@@1} 
 
- %{#@1}
+ %{#@1} 
 
- %{#@@1}
+ %{#@@1} 
 
- '#@1'
+ '#@1' 
 
- '#@@1'
+ '#@@1' 
 
- /#@1/
+ /#@1/ 
 
- /#@@1/
+ /#@@1/ 
 
- :"#@1"
+ :"#@1" 
 
- :"#@@1"
+ :"#@@1" 
 
- :'#@1'
+ :'#@1' 
 
- :'#@@1'
+ :'#@@1' 
 
- `#@1`
+ `#@1` 
 
- `#@@1`
+ `#@@1` 
 
 <<-"HERE"
 #@1
diff --git a/test/fixtures/whitequark/procarg0.txt b/test/fixtures/whitequark/procarg0.txt
index 70b368e7..74cae2c2 100644
--- a/test/fixtures/whitequark/procarg0.txt
+++ b/test/fixtures/whitequark/procarg0.txt
@@ -1,3 +1,3 @@
-m { |(foo, bar)| }
+m { |(foo, bar)| } 
 
-m { |foo| }
+m { |foo| } 
diff --git a/test/parse_test.rb b/test/parse_test.rb
index 7a910f7d..ed0567d0 100644
--- a/test/parse_test.rb
+++ b/test/parse_test.rb
@@ -34,8 +34,12 @@ class ParseTest < Test::Unit::TestCase
   # running on Ruby 3.2+.
   check_ripper = RUBY_VERSION >= "3.2.0"
 
+  # The FOCUS environment variable allows you to specify one particular fixture
+  # to test, instead of all of them.
   base = File.join(__dir__, "fixtures")
-  Dir["**/*.txt", base: base].each do |relative|
+  relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
+
+  relatives.each do |relative|
     # These fail on TruffleRuby due to a difference in Symbol#inspect: :测试 vs :"测试"
     next if RUBY_ENGINE == "truffleruby" and %w[seattlerb/bug202.txt seattlerb/magic_encoding_comment.txt].include?(relative)
 
@@ -91,6 +95,13 @@ class ParseTest < Test::Unit::TestCase
       # Next, assert that the newlines are in the expected places.
       expected_newlines = [0]
       source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
+
+      # If there's a __END__, then we should trip out those newlines because we
+      # don't actually scan them during parsing (because we don't need to).
+      if found = result.comments.find { |comment| comment.type == :__END__ }
+        expected_newlines = expected_newlines[...found.location.start_line]
+      end
+
       assert_equal expected_newlines, YARP.const_get(:Debug).newlines(source)
 
       # This file has changed behavior in Ripper in Ruby 3.3, so we skip it if
diff --git a/test/snapshots/if.txt b/test/snapshots/if.txt
index c12691ab..d151d6b5 100644
--- a/test/snapshots/if.txt
+++ b/test/snapshots/if.txt
@@ -203,7 +203,7 @@ ProgramNode(0...382)(
          (269...271)
        ),
        nil,
-       IfNode(274...289)(
+       IfNode(274...293)(
          (274...279),
          MatchPredicateNode(280...289)(
            CallNode(280...284)(
@@ -222,7 +222,7 @@ ProgramNode(0...382)(
          ),
          nil,
          nil,
-         nil
+         (290...293)
        ),
        (290...293)
      ),
diff --git a/test/snapshots/regex.txt b/test/snapshots/regex.txt
index 67d7ab04..6ba39427 100644
--- a/test/snapshots/regex.txt
+++ b/test/snapshots/regex.txt
@@ -1,6 +1,6 @@
-ProgramNode(0...278)(
+ProgramNode(0...293)(
   [:foo],
-  StatementsNode(0...278)(
+  StatementsNode(0...293)(
     [CallNode(0...9)(
        nil,
        nil,
@@ -138,6 +138,29 @@ ProgramNode(0...278)(
        (277...278),
        "pound",
        0
+     ),
+     InterpolatedRegularExpressionNode(280...293)(
+       (280...281),
+       [StringNode(281...285)(nil, (281...285), nil, "aaa "),
+        EmbeddedStatementsNode(285...291)(
+          (285...287),
+          StatementsNode(287...290)(
+            [CallNode(287...290)(
+               nil,
+               nil,
+               (287...290),
+               nil,
+               nil,
+               nil,
+               nil,
+               2,
+               "bbb"
+             )]
+          ),
+          (290...291)
+        )],
+       (291...293),
+       128
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/TestRubyParserShared.txt b/test/snapshots/seattlerb/TestRubyParserShared.txt
new file mode 100644
index 00000000..cfad7e8a
--- /dev/null
+++ b/test/snapshots/seattlerb/TestRubyParserShared.txt
@@ -0,0 +1,216 @@
+ProgramNode(0...689)(
+  [],
+  StatementsNode(0...689)(
+    [ArrayNode(0...7)([], (0...3), (6...7)),
+     ArrayNode(9...26)(
+       [SymbolNode(13...18)(nil, (13...18), nil, "line2"),
+        SymbolNode(19...24)(nil, (19...24), nil, "line3")],
+       (9...12),
+       (25...26)
+     ),
+     ArrayNode(28...35)([], (28...31), (34...35)),
+     ArrayNode(37...54)(
+       [StringNode(41...46)(nil, (41...46), nil, "line2"),
+        StringNode(47...52)(nil, (47...52), nil, "line3")],
+       (37...40),
+       (53...54)
+     ),
+     ArrayNode(56...63)([], (56...59), (62...63)),
+     ArrayNode(65...82)(
+       [SymbolNode(69...74)(nil, (69...74), nil, "line2"),
+        SymbolNode(75...80)(nil, (75...80), nil, "line3")],
+       (65...68),
+       (81...82)
+     ),
+     RegularExpressionNode(84...91)(
+       (84...87),
+       (87...90),
+       (90...91),
+       "\n" + "\n" + "\n",
+       0
+     ),
+     ArrayNode(93...100)([], (93...96), (99...100)),
+     ArrayNode(102...119)(
+       [StringNode(106...111)(nil, (106...111), nil, "line2"),
+        StringNode(112...117)(nil, (112...117), nil, "line3")],
+       (102...105),
+       (118...119)
+     ),
+     ArrayNode(121...139)(
+       [SymbolNode(123...129)((123...124), (124...129), nil, "line2"),
+        SymbolNode(131...137)((131...132), (132...137), nil, "line3")],
+       (121...122),
+       (138...139)
+     ),
+     ClassNode(141...269)(
+       [],
+       (141...146),
+       ConstantReadNode(147...148)(),
+       nil,
+       nil,
+       StatementsNode(168...246)(
+         [DefNode(168...246)(
+            (177...178),
+            SelfNode(172...176)(),
+            ParametersNode(179...200)(
+              [RequiredParameterNode(179...180)(:a),
+               RequiredParameterNode(199...200)(:b)],
+              [],
+              [],
+              nil,
+              [],
+              nil,
+              nil
+            ),
+            StatementsNode(220...225)(
+              [CallNode(220...225)(
+                 LocalVariableReadNode(220...221)(:a, 0),
+                 nil,
+                 (222...223),
+                 nil,
+                 ArgumentsNode(224...225)(
+                   [LocalVariableReadNode(224...225)(:b, 0)]
+                 ),
+                 nil,
+                 nil,
+                 0,
+                 "+"
+               )]
+            ),
+            [:a, :b],
+            (168...171),
+            (176...177),
+            (178...179),
+            (200...201),
+            nil,
+            (243...246)
+          )]
+       ),
+       (266...269)
+     ),
+     ClassNode(293...376)(
+       [],
+       (293...298),
+       ConstantReadNode(299...300)(),
+       nil,
+       nil,
+       StatementsNode(315...358)(
+         [ClassNode(315...358)(
+            [],
+            (315...320),
+            ConstantReadNode(321...322)(),
+            nil,
+            nil,
+            StatementsNode(337...343)(
+              [ConstantWriteNode(337...343)(
+                 (337...338),
+                 IntegerNode(341...343)(),
+                 (339...340)
+               )]
+            ),
+            (355...358)
+          )]
+       ),
+       (373...376)
+     ),
+     ClassNode(395...498)(
+       [],
+       (395...400),
+       ConstantReadNode(401...402)(),
+       nil,
+       nil,
+       StatementsNode(417...480)(
+         [DefNode(417...480)(
+            (421...422),
+            nil,
+            ParametersNode(423...444)(
+              [RequiredParameterNode(423...424)(:a),
+               RequiredParameterNode(443...444)(:b)],
+              [],
+              [],
+              nil,
+              [],
+              nil,
+              nil
+            ),
+            StatementsNode(459...464)(
+              [CallNode(459...464)(
+                 LocalVariableReadNode(459...460)(:a, 0),
+                 nil,
+                 (461...462),
+                 nil,
+                 ArgumentsNode(463...464)(
+                   [LocalVariableReadNode(463...464)(:b, 0)]
+                 ),
+                 nil,
+                 nil,
+                 0,
+                 "+"
+               )]
+            ),
+            [:a, :b],
+            (417...420),
+            nil,
+            (422...423),
+            (444...445),
+            nil,
+            (477...480)
+          )]
+       ),
+       (495...498)
+     ),
+     ModuleNode(517...565)(
+       [],
+       (517...523),
+       ConstantReadNode(524...525)(),
+       StatementsNode(528...561)(
+         [ConstantWriteNode(528...561)(
+            (528...529),
+            ArrayNode(532...561)(
+              [SymbolNode(538...544)((538...539), (539...544), nil, "line3"),
+               SymbolNode(550...556)((550...551), (551...556), nil, "line4")],
+              (532...533),
+              (560...561)
+            ),
+            (530...531)
+          )]
+       ),
+       (562...565)
+     ),
+     ModuleNode(568...651)(
+       [],
+       (568...574),
+       ConstantReadNode(575...576)(),
+       StatementsNode(590...633)(
+         [ModuleNode(590...633)(
+            [],
+            (590...596),
+            ConstantReadNode(597...598)(),
+            StatementsNode(612...618)(
+              [ConstantWriteNode(612...618)(
+                 (612...613),
+                 IntegerNode(616...618)(),
+                 (614...615)
+               )]
+            ),
+            (630...633)
+          )]
+       ),
+       (648...651)
+     ),
+     CallNode(670...689)(
+       nil,
+       nil,
+       (670...671),
+       (671...672),
+       ArgumentsNode(673...687)(
+         [SymbolNode(673...679)((673...674), (674...679), nil, "line2"),
+          SymbolNode(681...687)((681...682), (682...687), nil, "line3")]
+       ),
+       (688...689),
+       nil,
+       0,
+       "x"
+     )]
+  )
+)
diff --git a/test/snapshots/seattlerb/case_in.txt b/test/snapshots/seattlerb/case_in.txt
index e339f35c..8277b654 100644
--- a/test/snapshots/seattlerb/case_in.txt
+++ b/test/snapshots/seattlerb/case_in.txt
@@ -1,7 +1,7 @@
-ProgramNode(0...746)(
+ProgramNode(0...747)(
   [:b, :_, :lhs, :x, :rhs, :c, :e],
-  StatementsNode(0...746)(
-    [CaseNode(0...20)(
+  StatementsNode(0...747)(
+    [CaseNode(0...21)(
        SymbolNode(5...7)((5...6), (6...7), nil, "a"),
        [InNode(8...16)(
           HashPatternNode(12...16)(
@@ -21,207 +21,207 @@ ProgramNode(0...746)(
         )],
        nil,
        (0...4),
-       (17...20)
+       (18...21)
      ),
-     CaseNode(22...44)(
-       SymbolNode(27...29)((27...28), (28...29), nil, "a"),
-       [InNode(30...40)(
-          ArrayNode(33...40)(
-            [SymbolNode(36...37)(nil, (36...37), nil, "a"),
-             SymbolNode(38...39)(nil, (38...39), nil, "b")],
-            (33...36),
-            (39...40)
+     CaseNode(23...45)(
+       SymbolNode(28...30)((28...29), (29...30), nil, "a"),
+       [InNode(31...41)(
+          ArrayNode(34...41)(
+            [SymbolNode(37...38)(nil, (37...38), nil, "a"),
+             SymbolNode(39...40)(nil, (39...40), nil, "b")],
+            (34...37),
+            (40...41)
           ),
           nil,
-          (30...32),
+          (31...33),
           nil
         )],
        nil,
-       (22...26),
-       (41...44)
+       (23...27),
+       (42...45)
      ),
-     CaseNode(46...68)(
-       SymbolNode(51...53)((51...52), (52...53), nil, "a"),
-       [InNode(54...64)(
-          ArrayNode(57...64)(
-            [StringNode(60...61)(nil, (60...61), nil, "a"),
-             StringNode(62...63)(nil, (62...63), nil, "b")],
-            (57...60),
-            (63...64)
+     CaseNode(47...69)(
+       SymbolNode(52...54)((52...53), (53...54), nil, "a"),
+       [InNode(55...65)(
+          ArrayNode(58...65)(
+            [StringNode(61...62)(nil, (61...62), nil, "a"),
+             StringNode(63...64)(nil, (63...64), nil, "b")],
+            (58...61),
+            (64...65)
           ),
           nil,
-          (54...56),
+          (55...57),
           nil
         )],
        nil,
-       (46...50),
-       (65...68)
+       (47...51),
+       (66...69)
      ),
-     CaseNode(70...92)(
-       SymbolNode(75...77)((75...76), (76...77), nil, "a"),
-       [InNode(78...88)(
-          ArrayNode(81...88)(
-            [SymbolNode(84...85)(nil, (84...85), nil, "a"),
-             SymbolNode(86...87)(nil, (86...87), nil, "b")],
-            (81...84),
-            (87...88)
+     CaseNode(71...93)(
+       SymbolNode(76...78)((76...77), (77...78), nil, "a"),
+       [InNode(79...89)(
+          ArrayNode(82...89)(
+            [SymbolNode(85...86)(nil, (85...86), nil, "a"),
+             SymbolNode(87...88)(nil, (87...88), nil, "b")],
+            (82...85),
+            (88...89)
           ),
           nil,
-          (78...80),
+          (79...81),
           nil
         )],
        nil,
-       (70...74),
-       (89...92)
+       (71...75),
+       (90...93)
      ),
-     CaseNode(94...116)(
-       SymbolNode(99...101)((99...100), (100...101), nil, "a"),
-       [InNode(102...112)(
-          ArrayNode(105...112)(
-            [StringNode(108...109)(nil, (108...109), nil, "a"),
-             StringNode(110...111)(nil, (110...111), nil, "b")],
-            (105...108),
-            (111...112)
+     CaseNode(95...117)(
+       SymbolNode(100...102)((100...101), (101...102), nil, "a"),
+       [InNode(103...113)(
+          ArrayNode(106...113)(
+            [StringNode(109...110)(nil, (109...110), nil, "a"),
+             StringNode(111...112)(nil, (111...112), nil, "b")],
+            (106...109),
+            (112...113)
           ),
           nil,
-          (102...104),
+          (103...105),
           nil
         )],
        nil,
-       (94...98),
-       (113...116)
+       (95...99),
+       (114...117)
      ),
-     CaseNode(118...140)(
-       SymbolNode(123...125)((123...124), (124...125), nil, "a"),
-       [InNode(126...135)(
-          RangeNode(130...135)(nil, IntegerNode(133...135)(), (130...133), 1),
+     CaseNode(119...141)(
+       SymbolNode(124...126)((124...125), (125...126), nil, "a"),
+       [InNode(127...136)(
+          RangeNode(131...136)(nil, IntegerNode(134...136)(), (131...134), 1),
           nil,
-          (126...128),
+          (127...129),
           nil
         )],
        nil,
-       (118...122),
-       (137...140)
+       (119...123),
+       (138...141)
      ),
-     CaseNode(142...163)(
-       SymbolNode(147...149)((147...148), (148...149), nil, "a"),
-       [InNode(150...158)(
-          RangeNode(154...158)(nil, IntegerNode(156...158)(), (154...156), 0),
+     CaseNode(143...164)(
+       SymbolNode(148...150)((148...149), (149...150), nil, "a"),
+       [InNode(151...159)(
+          RangeNode(155...159)(nil, IntegerNode(157...159)(), (155...157), 0),
           nil,
-          (150...152),
+          (151...153),
           nil
         )],
        nil,
-       (142...146),
-       (160...163)
+       (143...147),
+       (161...164)
      ),
-     CaseNode(165...186)(
-       SymbolNode(170...172)((170...171), (171...172), nil, "a"),
-       [InNode(173...181)(
-          RangeNode(177...181)(IntegerNode(177...178)(), nil, (178...181), 1),
+     CaseNode(166...187)(
+       SymbolNode(171...173)((171...172), (172...173), nil, "a"),
+       [InNode(174...182)(
+          RangeNode(178...182)(IntegerNode(178...179)(), nil, (179...182), 1),
           nil,
-          (173...175),
+          (174...176),
           nil
         )],
        nil,
-       (165...169),
-       (183...186)
+       (166...170),
+       (184...187)
      ),
-     CaseNode(188...210)(
-       SymbolNode(193...195)((193...194), (194...195), nil, "a"),
-       [InNode(196...205)(
-          RangeNode(200...205)(
-            IntegerNode(200...201)(),
-            IntegerNode(204...205)(),
-            (201...204),
+     CaseNode(189...211)(
+       SymbolNode(194...196)((194...195), (195...196), nil, "a"),
+       [InNode(197...206)(
+          RangeNode(201...206)(
+            IntegerNode(201...202)(),
+            IntegerNode(205...206)(),
+            (202...205),
             1
           ),
           nil,
-          (196...198),
+          (197...199),
           nil
         )],
        nil,
-       (188...192),
-       (207...210)
+       (189...193),
+       (208...211)
      ),
-     CaseNode(212...231)(
-       SymbolNode(217...219)((217...218), (218...219), nil, "a"),
-       [InNode(220...226)(IntegerNode(224...226)(), nil, (220...222), nil)],
+     CaseNode(213...232)(
+       SymbolNode(218...220)((218...219), (219...220), nil, "a"),
+       [InNode(221...227)(IntegerNode(225...227)(), nil, (221...223), nil)],
        nil,
-       (212...216),
-       (228...231)
+       (213...217),
+       (229...232)
      ),
-     CaseNode(233...253)(
-       SymbolNode(238...240)((238...239), (239...240), nil, "a"),
-       [InNode(241...249)(
-          HashPatternNode(244...249)(
+     CaseNode(234...254)(
+       SymbolNode(239...241)((239...240), (240...241), nil, "a"),
+       [InNode(242...250)(
+          HashPatternNode(245...250)(
             nil,
-            [NoKeywordsParameterNode(244...249)((244...246), (246...249))],
+            [NoKeywordsParameterNode(245...250)((245...247), (247...250))],
             nil,
             nil,
             nil
           ),
           nil,
-          (241...243),
+          (242...244),
           nil
         )],
        nil,
-       (233...237),
-       (250...253)
+       (234...238),
+       (251...254)
      ),
-     CaseNode(255...278)(
-       SymbolNode(260...262)((260...261), (261...262), nil, "a"),
-       [InNode(263...274)(
-          RegularExpressionNode(266...274)(
-            (266...267),
-            (267...273),
-            (273...274),
+     CaseNode(256...279)(
+       SymbolNode(261...263)((261...262), (262...263), nil, "a"),
+       [InNode(264...275)(
+          RegularExpressionNode(267...275)(
+            (267...268),
+            (268...274),
+            (274...275),
             "regexp",
             0
           ),
           nil,
-          (263...265),
+          (264...266),
           nil
         )],
        nil,
-       (255...259),
-       (275...278)
+       (256...260),
+       (276...279)
      ),
-     CaseNode(280...305)(
-       SymbolNode(285...287)((285...286), (286...287), nil, "a"),
-       [InNode(288...301)(
-          ArrayPatternNode(291...301)(
+     CaseNode(281...306)(
+       SymbolNode(286...288)((286...287), (287...288), nil, "a"),
+       [InNode(289...302)(
+          ArrayPatternNode(292...302)(
             nil,
-            [SymbolNode(291...293)((291...292), (292...293), nil, "b")],
-            SplatNode(295...297)(
-              (295...296),
-              LocalVariableWriteNode(296...297)(:_, 0, nil, (296...297), nil)
+            [SymbolNode(292...294)((292...293), (293...294), nil, "b")],
+            SplatNode(296...298)(
+              (296...297),
+              LocalVariableWriteNode(297...298)(:_, 0, nil, (297...298), nil)
             ),
-            [SymbolNode(299...301)((299...300), (300...301), nil, "c")],
+            [SymbolNode(300...302)((300...301), (301...302), nil, "c")],
             nil,
             nil
           ),
           nil,
-          (288...290),
+          (289...291),
           nil
         )],
        nil,
-       (280...284),
-       (302...305)
+       (281...285),
+       (303...306)
      ),
-     CaseNode(307...330)(
-       SymbolNode(312...314)((312...313), (313...314), nil, "a"),
-       [InNode(315...326)(
-          ArrayPatternNode(318...326)(
+     CaseNode(308...331)(
+       SymbolNode(313...315)((313...314), (314...315), nil, "a"),
+       [InNode(316...327)(
+          ArrayPatternNode(319...327)(
             nil,
-            [SymbolNode(318...320)((318...319), (319...320), nil, "b"),
-             ArrayPatternNode(322...326)(
+            [SymbolNode(319...321)((319...320), (320...321), nil, "b"),
+             ArrayPatternNode(323...327)(
                nil,
-               [SymbolNode(323...325)((323...324), (324...325), nil, "c")],
+               [SymbolNode(324...326)((324...325), (325...326), nil, "c")],
                nil,
                [],
-               (322...323),
-               (325...326)
+               (323...324),
+               (326...327)
              )],
             nil,
             [],
@@ -229,93 +229,93 @@ ProgramNode(0...746)(
             nil
           ),
           nil,
-          (315...317),
+          (316...318),
           nil
         )],
        nil,
-       (307...311),
-       (327...330)
+       (308...312),
+       (328...331)
      ),
-     CaseNode(332...355)(
-       SymbolNode(337...339)((337...338), (338...339), nil, "a"),
-       [InNode(340...351)(
-          ArrayPatternNode(343...351)(
-            ConstantReadNode(343...349)(),
+     CaseNode(333...356)(
+       SymbolNode(338...340)((338...339), (339...340), nil, "a"),
+       [InNode(341...352)(
+          ArrayPatternNode(344...352)(
+            ConstantReadNode(344...350)(),
             [],
             nil,
             [],
-            (349...350),
-            (350...351)
+            (350...351),
+            (351...352)
           ),
           nil,
-          (340...342),
+          (341...343),
           nil
         )],
        nil,
-       (332...336),
-       (352...355)
+       (333...337),
+       (353...356)
      ),
-     CaseNode(357...393)(
-       SymbolNode(362...364)((362...363), (363...364), nil, "a"),
-       [InNode(365...389)(
-          FindPatternNode(368...389)(
-            ConstantReadNode(368...374)(),
-            SplatNode(375...379)(
-              (375...376),
-              LocalVariableWriteNode(376...379)(:lhs, 0, nil, (376...379), nil)
+     CaseNode(358...394)(
+       SymbolNode(363...365)((363...364), (364...365), nil, "a"),
+       [InNode(366...390)(
+          FindPatternNode(369...390)(
+            ConstantReadNode(369...375)(),
+            SplatNode(376...380)(
+              (376...377),
+              LocalVariableWriteNode(377...380)(:lhs, 0, nil, (377...380), nil)
             ),
-            [LocalVariableWriteNode(381...382)(:x, 0, nil, (381...382), nil)],
-            SplatNode(384...388)(
-              (384...385),
-              LocalVariableWriteNode(385...388)(:rhs, 0, nil, (385...388), nil)
+            [LocalVariableWriteNode(382...383)(:x, 0, nil, (382...383), nil)],
+            SplatNode(385...389)(
+              (385...386),
+              LocalVariableWriteNode(386...389)(:rhs, 0, nil, (386...389), nil)
             ),
-            (374...375),
-            (388...389)
+            (375...376),
+            (389...390)
           ),
           nil,
-          (365...367),
+          (366...368),
           nil
         )],
        nil,
-       (357...361),
-       (390...393)
+       (358...362),
+       (391...394)
      ),
-     CaseNode(395...431)(
-       SymbolNode(400...402)((400...401), (401...402), nil, "a"),
-       [InNode(403...427)(
-          FindPatternNode(406...427)(
-            ConstantReadNode(406...412)(),
-            SplatNode(413...417)(
-              (413...414),
-              LocalVariableWriteNode(414...417)(:lhs, 0, nil, (414...417), nil)
+     CaseNode(396...432)(
+       SymbolNode(401...403)((401...402), (402...403), nil, "a"),
+       [InNode(404...428)(
+          FindPatternNode(407...428)(
+            ConstantReadNode(407...413)(),
+            SplatNode(414...418)(
+              (414...415),
+              LocalVariableWriteNode(415...418)(:lhs, 0, nil, (415...418), nil)
             ),
-            [LocalVariableWriteNode(419...420)(:x, 0, nil, (419...420), nil)],
-            SplatNode(422...426)(
-              (422...423),
-              LocalVariableWriteNode(423...426)(:rhs, 0, nil, (423...426), nil)
+            [LocalVariableWriteNode(420...421)(:x, 0, nil, (420...421), nil)],
+            SplatNode(423...427)(
+              (423...424),
+              LocalVariableWriteNode(424...427)(:rhs, 0, nil, (424...427), nil)
             ),
-            (412...413),
-            (426...427)
+            (413...414),
+            (427...428)
           ),
           nil,
-          (403...405),
+          (404...406),
           nil
         )],
        nil,
-       (395...399),
-       (428...431)
+       (396...400),
+       (429...432)
      ),
-     CaseNode(433...467)(
-       SymbolNode(438...440)((438...439), (439...440), nil, "a"),
-       [InNode(441...463)(
-          ArrayPatternNode(444...463)(
+     CaseNode(434...468)(
+       SymbolNode(439...441)((439...440), (440...441), nil, "a"),
+       [InNode(442...464)(
+          ArrayPatternNode(445...464)(
             nil,
-            [LambdaNode(445...459)(
+            [LambdaNode(446...460)(
                [:b],
-               (445...447),
-               BlockParametersNode(447...450)(
-                 ParametersNode(448...449)(
-                   [RequiredParameterNode(448...449)(:b)],
+               (446...448),
+               BlockParametersNode(448...451)(
+                 ParametersNode(449...450)(
+                   [RequiredParameterNode(449...450)(:b)],
                    [],
                    [],
                    nil,
@@ -324,156 +324,156 @@ ProgramNode(0...746)(
                    nil
                  ),
                  [],
-                 (447...448),
-                 (449...450)
+                 (448...449),
+                 (450...451)
                ),
-               StatementsNode(453...457)([TrueNode(453...457)()])
+               StatementsNode(454...458)([TrueNode(454...458)()])
              ),
-             LocalVariableWriteNode(461...462)(:c, 0, nil, (461...462), nil)],
+             LocalVariableWriteNode(462...463)(:c, 0, nil, (462...463), nil)],
             nil,
             [],
-            (444...445),
-            (462...463)
+            (445...446),
+            (463...464)
           ),
           nil,
-          (441...443),
+          (442...444),
           nil
         )],
        nil,
-       (433...437),
-       (464...467)
+       (434...438),
+       (465...468)
      ),
-     CaseNode(469...509)(
-       SymbolNode(474...476)((474...475), (475...476), nil, "a"),
-       [InNode(477...505)(
-          ArrayPatternNode(480...505)(
+     CaseNode(470...510)(
+       SymbolNode(475...477)((475...476), (476...477), nil, "a"),
+       [InNode(478...506)(
+          ArrayPatternNode(481...506)(
             nil,
-            [SymbolNode(481...483)((481...482), (482...483), nil, "a"),
-             LocalVariableWriteNode(485...486)(:b, 0, nil, (485...486), nil),
-             LocalVariableWriteNode(488...489)(:c, 0, nil, (488...489), nil),
-             ArrayPatternNode(491...504)(
+            [SymbolNode(482...484)((482...483), (483...484), nil, "a"),
+             LocalVariableWriteNode(486...487)(:b, 0, nil, (486...487), nil),
+             LocalVariableWriteNode(489...490)(:c, 0, nil, (489...490), nil),
+             ArrayPatternNode(492...505)(
                nil,
-               [SymbolNode(492...494)((492...493), (493...494), nil, "d")],
-               SplatNode(496...498)(
-                 (496...497),
-                 LocalVariableWriteNode(497...498)(
+               [SymbolNode(493...495)((493...494), (494...495), nil, "d")],
+               SplatNode(497...499)(
+                 (497...498),
+                 LocalVariableWriteNode(498...499)(
                    :e,
                    0,
                    nil,
-                   (497...498),
+                   (498...499),
                    nil
                  )
                ),
-               [NilNode(500...503)()],
-               (491...492),
-               (503...504)
+               [NilNode(501...504)()],
+               (492...493),
+               (504...505)
              )],
             nil,
             [],
-            (480...481),
-            (504...505)
+            (481...482),
+            (505...506)
           ),
           nil,
-          (477...479),
+          (478...480),
           nil
         )],
        nil,
-       (469...473),
-       (506...509)
+       (470...474),
+       (507...510)
      ),
-     CaseNode(511...535)(
-       SymbolNode(516...518)((516...517), (517...518), nil, "a"),
-       [InNode(519...531)(
-          ArrayPatternNode(522...531)(
+     CaseNode(512...536)(
+       SymbolNode(517...519)((517...518), (518...519), nil, "a"),
+       [InNode(520...532)(
+          ArrayPatternNode(523...532)(
             nil,
-            [ConstantReadNode(523...524)()],
-            SplatNode(526...527)((526...527), nil),
-            [ConstantReadNode(529...530)()],
-            (522...523),
-            (530...531)
+            [ConstantReadNode(524...525)()],
+            SplatNode(527...528)((527...528), nil),
+            [ConstantReadNode(530...531)()],
+            (523...524),
+            (531...532)
           ),
           nil,
-          (519...521),
+          (520...522),
           nil
         )],
        nil,
-       (511...515),
-       (532...535)
+       (512...516),
+       (533...536)
      ),
-     CaseNode(537...571)(
-       SymbolNode(542...544)((542...543), (543...544), nil, "a"),
-       [InNode(545...567)(
-          ArrayPatternNode(548...567)(
+     CaseNode(538...572)(
+       SymbolNode(543...545)((543...544), (544...545), nil, "a"),
+       [InNode(546...568)(
+          ArrayPatternNode(549...568)(
             nil,
-            [ArrayPatternNode(549...556)(
+            [ArrayPatternNode(550...557)(
                nil,
-               [SymbolNode(550...552)((550...551), (551...552), nil, "b"),
-                LocalVariableWriteNode(554...555)(
+               [SymbolNode(551...553)((551...552), (552...553), nil, "b"),
+                LocalVariableWriteNode(555...556)(
                   :c,
                   0,
                   nil,
-                  (554...555),
+                  (555...556),
                   nil
                 )],
                nil,
                [],
-               (549...550),
-               (555...556)
+               (550...551),
+               (556...557)
              ),
-             ArrayPatternNode(558...566)(
+             ArrayPatternNode(559...567)(
                nil,
-               [SymbolNode(559...561)((559...560), (560...561), nil, "d"),
-                PinnedVariableNode(563...565)(
-                  LocalVariableReadNode(564...565)(:e, 0),
-                  (563...564)
+               [SymbolNode(560...562)((560...561), (561...562), nil, "d"),
+                PinnedVariableNode(564...566)(
+                  LocalVariableReadNode(565...566)(:e, 0),
+                  (564...565)
                 )],
                nil,
                [],
-               (558...559),
-               (565...566)
+               (559...560),
+               (566...567)
              )],
             nil,
             [],
-            (548...549),
-            (566...567)
+            (549...550),
+            (567...568)
           ),
           nil,
-          (545...547),
+          (546...548),
           nil
         )],
        nil,
-       (537...541),
-       (568...571)
+       (538...542),
+       (569...572)
      ),
-     CaseNode(573...590)(
-       SymbolNode(578...580)((578...579), (579...580), nil, "a"),
-       [InNode(581...586)(
-          ArrayPatternNode(584...586)(
+     CaseNode(574...591)(
+       SymbolNode(579...581)((579...580), (580...581), nil, "a"),
+       [InNode(582...587)(
+          ArrayPatternNode(585...587)(
             nil,
             [],
             nil,
             [],
-            (584...585),
-            (585...586)
+            (585...586),
+            (586...587)
           ),
           nil,
-          (581...583),
+          (582...584),
           nil
         )],
        nil,
-       (573...577),
-       (587...590)
+       (574...578),
+       (588...591)
      ),
-     CaseNode(592...613)(
-       SymbolNode(597...599)((597...598), (598...599), nil, "a"),
-       [InNode(600...609)(
-          ArrayPatternNode(603...609)(
+     CaseNode(593...614)(
+       SymbolNode(598...600)((598...599), (599...600), nil, "a"),
+       [InNode(601...610)(
+          ArrayPatternNode(604...610)(
             nil,
-            [PinnedExpressionNode(604...608)(
-               CallNode(606...607)(
+            [PinnedExpressionNode(605...609)(
+               CallNode(607...608)(
                  nil,
                  nil,
-                 (606...607),
+                 (607...608),
                  nil,
                  nil,
                  nil,
@@ -481,127 +481,127 @@ ProgramNode(0...746)(
                  2,
                  "a"
                ),
-               (604...605),
                (605...606),
-               (607...608)
+               (606...607),
+               (608...609)
              )],
             nil,
             [],
-            (603...604),
-            (608...609)
+            (604...605),
+            (609...610)
           ),
           nil,
-          (600...602),
+          (601...603),
           nil
         )],
        nil,
-       (592...596),
-       (610...613)
+       (593...597),
+       (611...614)
      ),
-     CaseNode(615...646)(
-       SymbolNode(620...622)((620...621), (621...622), nil, "a"),
-       [InNode(623...642)(
-          ArrayPatternNode(626...642)(
+     CaseNode(616...647)(
+       SymbolNode(621...623)((621...622), (622...623), nil, "a"),
+       [InNode(624...643)(
+          ArrayPatternNode(627...643)(
             nil,
-            [PinnedVariableNode(627...630)(
-               InstanceVariableReadNode(628...630)(),
-               (627...628)
+            [PinnedVariableNode(628...631)(
+               InstanceVariableReadNode(629...631)(),
+               (628...629)
              ),
-             PinnedVariableNode(632...635)(
-               GlobalVariableReadNode(633...635)(),
-               (632...633)
+             PinnedVariableNode(633...636)(
+               GlobalVariableReadNode(634...636)(),
+               (633...634)
              ),
-             PinnedVariableNode(637...641)(
-               ClassVariableReadNode(638...641)(),
-               (637...638)
+             PinnedVariableNode(638...642)(
+               ClassVariableReadNode(639...642)(),
+               (638...639)
              )],
             nil,
             [],
-            (626...627),
-            (641...642)
+            (627...628),
+            (642...643)
           ),
           nil,
-          (623...625),
+          (624...626),
           nil
         )],
        nil,
-       (615...619),
-       (643...646)
+       (616...620),
+       (644...647)
      ),
-     CaseNode(648...672)(
-       SymbolNode(653...655)((653...654), (654...655), nil, "a"),
-       [InNode(656...668)(
-          XStringNode(659...668)(
-            (659...660),
-            (660...667),
-            (667...668),
+     CaseNode(649...673)(
+       SymbolNode(654...656)((654...655), (655...656), nil, "a"),
+       [InNode(657...669)(
+          XStringNode(660...669)(
+            (660...661),
+            (661...668),
+            (668...669),
             "echo hi"
           ),
           nil,
-          (656...658),
+          (657...659),
           nil
         )],
        nil,
-       (648...652),
-       (669...672)
+       (649...653),
+       (670...673)
      ),
-     CaseNode(674...702)(
-       SymbolNode(679...681)((679...680), (680...681), nil, "a"),
-       [InNode(682...698)(
-          ArrayPatternNode(685...698)(
+     CaseNode(675...703)(
+       SymbolNode(680...682)((680...681), (681...682), nil, "a"),
+       [InNode(683...699)(
+          ArrayPatternNode(686...699)(
             nil,
-            [NilNode(685...688)(), NilNode(690...693)(), NilNode(695...698)()],
+            [NilNode(686...689)(), NilNode(691...694)(), NilNode(696...699)()],
             nil,
             [],
             nil,
             nil
           ),
           nil,
-          (682...684),
+          (683...685),
           nil
         )],
        nil,
-       (674...678),
-       (699...702)
+       (675...679),
+       (700...703)
      ),
-     CaseNode(704...727)(
-       SymbolNode(709...711)((709...710), (710...711), nil, "a"),
-       [InNode(712...723)(
-          HashPatternNode(715...723)(
+     CaseNode(705...728)(
+       SymbolNode(710...712)((710...711), (711...712), nil, "a"),
+       [InNode(713...724)(
+          HashPatternNode(716...724)(
             nil,
-            [AssocNode(717...721)(
-               SymbolNode(717...721)(
-                 (717...718),
+            [AssocNode(718...722)(
+               SymbolNode(718...722)(
                  (718...719),
-                 (719...721),
+                 (719...720),
+                 (720...722),
                  "b"
                ),
                nil,
                nil
              )],
             nil,
-            (715...716),
-            (722...723)
+            (716...717),
+            (723...724)
           ),
           nil,
-          (712...714),
+          (713...715),
           nil
         )],
        nil,
-       (704...708),
-       (724...727)
+       (705...709),
+       (725...728)
      ),
-     CaseNode(729...746)(
-       SymbolNode(734...736)((734...735), (735...736), nil, "a"),
-       [InNode(737...742)(
-          HashPatternNode(740...742)(nil, [], nil, (740...741), (741...742)),
+     CaseNode(730...747)(
+       SymbolNode(735...737)((735...736), (736...737), nil, "a"),
+       [InNode(738...743)(
+          HashPatternNode(741...743)(nil, [], nil, (741...742), (742...743)),
           nil,
-          (737...739),
+          (738...740),
           nil
         )],
        nil,
-       (729...733),
-       (743...746)
+       (730...734),
+       (744...747)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/defn_forward_args__no_parens.txt b/test/snapshots/seattlerb/defn_forward_args__no_parens.txt
new file mode 100644
index 00000000..14d94bc2
--- /dev/null
+++ b/test/snapshots/seattlerb/defn_forward_args__no_parens.txt
@@ -0,0 +1,38 @@
+ProgramNode(0...22)(
+  [],
+  StatementsNode(0...22)(
+    [DefNode(0...22)(
+       (4...5),
+       nil,
+       ParametersNode(6...9)(
+         [],
+         [],
+         [],
+         nil,
+         [],
+         ForwardingParameterNode(6...9)(),
+         nil
+       ),
+       StatementsNode(12...18)(
+         [CallNode(12...18)(
+            nil,
+            nil,
+            (12...13),
+            (13...14),
+            ArgumentsNode(14...17)([ForwardingArgumentsNode(14...17)()]),
+            (17...18),
+            nil,
+            0,
+            "m"
+          )]
+       ),
+       [:"..."],
+       (0...3),
+       nil,
+       nil,
+       nil,
+       nil,
+       (19...22)
+     )]
+  )
+)
diff --git a/test/snapshots/seattlerb/heredoc_wtf_I_hate_you.txt b/test/snapshots/seattlerb/difficult0_.txt
similarity index 100%
rename from test/snapshots/seattlerb/heredoc_wtf_I_hate_you.txt
rename to test/snapshots/seattlerb/difficult0_.txt
diff --git a/test/snapshots/seattlerb/i_fucking_hate_line_numbers.txt b/test/snapshots/seattlerb/difficult1_line_numbers.txt
similarity index 100%
rename from test/snapshots/seattlerb/i_fucking_hate_line_numbers.txt
rename to test/snapshots/seattlerb/difficult1_line_numbers.txt
diff --git a/test/snapshots/seattlerb/i_fucking_hate_line_numbers2.txt b/test/snapshots/seattlerb/difficult1_line_numbers2.txt
similarity index 100%
rename from test/snapshots/seattlerb/i_fucking_hate_line_numbers2.txt
rename to test/snapshots/seattlerb/difficult1_line_numbers2.txt
diff --git a/test/snapshots/seattlerb/i_have_no_freakin_clue.txt b/test/snapshots/seattlerb/difficult2_.txt
similarity index 100%
rename from test/snapshots/seattlerb/i_have_no_freakin_clue.txt
rename to test/snapshots/seattlerb/difficult2_.txt
diff --git a/test/snapshots/seattlerb/kill_me.txt b/test/snapshots/seattlerb/difficult3_.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me.txt
rename to test/snapshots/seattlerb/difficult3_.txt
diff --git a/test/snapshots/seattlerb/kill_me2.txt b/test/snapshots/seattlerb/difficult3_2.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me2.txt
rename to test/snapshots/seattlerb/difficult3_2.txt
diff --git a/test/snapshots/seattlerb/kill_me3.txt b/test/snapshots/seattlerb/difficult3_3.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me3.txt
rename to test/snapshots/seattlerb/difficult3_3.txt
diff --git a/test/snapshots/seattlerb/kill_me4.txt b/test/snapshots/seattlerb/difficult3_4.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me4.txt
rename to test/snapshots/seattlerb/difficult3_4.txt
diff --git a/test/snapshots/seattlerb/kill_me5.txt b/test/snapshots/seattlerb/difficult3_5.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me5.txt
rename to test/snapshots/seattlerb/difficult3_5.txt
diff --git a/test/snapshots/seattlerb/kill_me_10.txt b/test/snapshots/seattlerb/difficult3__10.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_10.txt
rename to test/snapshots/seattlerb/difficult3__10.txt
diff --git a/test/snapshots/seattlerb/kill_me_11.txt b/test/snapshots/seattlerb/difficult3__11.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_11.txt
rename to test/snapshots/seattlerb/difficult3__11.txt
diff --git a/test/snapshots/seattlerb/kill_me_12.txt b/test/snapshots/seattlerb/difficult3__12.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_12.txt
rename to test/snapshots/seattlerb/difficult3__12.txt
diff --git a/test/snapshots/seattlerb/kill_me_6.txt b/test/snapshots/seattlerb/difficult3__6.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_6.txt
rename to test/snapshots/seattlerb/difficult3__6.txt
diff --git a/test/snapshots/seattlerb/kill_me_7.txt b/test/snapshots/seattlerb/difficult3__7.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_7.txt
rename to test/snapshots/seattlerb/difficult3__7.txt
diff --git a/test/snapshots/seattlerb/kill_me_8.txt b/test/snapshots/seattlerb/difficult3__8.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_8.txt
rename to test/snapshots/seattlerb/difficult3__8.txt
diff --git a/test/snapshots/seattlerb/kill_me_9.txt b/test/snapshots/seattlerb/difficult3__9.txt
similarity index 100%
rename from test/snapshots/seattlerb/kill_me_9.txt
rename to test/snapshots/seattlerb/difficult3__9.txt
diff --git a/test/snapshots/seattlerb/motherfuckin_leading_dots.txt b/test/snapshots/seattlerb/difficult4__leading_dots.txt
similarity index 100%
rename from test/snapshots/seattlerb/motherfuckin_leading_dots.txt
rename to test/snapshots/seattlerb/difficult4__leading_dots.txt
diff --git a/test/snapshots/seattlerb/motherfuckin_leading_dots2.txt b/test/snapshots/seattlerb/difficult4__leading_dots2.txt
similarity index 100%
rename from test/snapshots/seattlerb/motherfuckin_leading_dots2.txt
rename to test/snapshots/seattlerb/difficult4__leading_dots2.txt
diff --git a/test/snapshots/seattlerb/wtf.txt b/test/snapshots/seattlerb/difficult6_.txt
similarity index 100%
rename from test/snapshots/seattlerb/wtf.txt
rename to test/snapshots/seattlerb/difficult6_.txt
diff --git a/test/snapshots/seattlerb/wtf_7.txt b/test/snapshots/seattlerb/difficult6__7.txt
similarity index 100%
rename from test/snapshots/seattlerb/wtf_7.txt
rename to test/snapshots/seattlerb/difficult6__7.txt
diff --git a/test/snapshots/seattlerb/wtf_8.txt b/test/snapshots/seattlerb/difficult6__8.txt
similarity index 100%
rename from test/snapshots/seattlerb/wtf_8.txt
rename to test/snapshots/seattlerb/difficult6__8.txt
diff --git a/test/snapshots/seattlerb/zomg_sometimes_i_hate_this_project.txt b/test/snapshots/seattlerb/difficult7_.txt
similarity index 100%
rename from test/snapshots/seattlerb/zomg_sometimes_i_hate_this_project.txt
rename to test/snapshots/seattlerb/difficult7_.txt
diff --git a/test/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt b/test/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
index 3bb7807c..680ffbe0 100644
--- a/test/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
+++ b/test/snapshots/seattlerb/heredoc_squiggly_visually_blank_lines.txt
@@ -6,8 +6,8 @@ ProgramNode(0...10)(
        0,
        InterpolatedStringNode(4...10)(
          (4...10),
-         [StringNode(11...20)(nil, (11...20), nil, "x\n" + "\n" + "z\n")],
-         (20...24)
+         [StringNode(11...21)(nil, (11...21), nil, "x\n" + "\n" + "z\n")],
+         (21...25)
        ),
        (0...1),
        (2...3)
diff --git a/test/snapshots/seattlerb/if_elsif.txt b/test/snapshots/seattlerb/if_elsif.txt
index 6f1edfb6..51c89a9a 100644
--- a/test/snapshots/seattlerb/if_elsif.txt
+++ b/test/snapshots/seattlerb/if_elsif.txt
@@ -5,7 +5,7 @@ ProgramNode(0...18)(
        (0...2),
        IntegerNode(3...4)(),
        nil,
-       IfNode(6...13)((6...11), IntegerNode(12...13)(), nil, nil, nil),
+       IfNode(6...18)((6...11), IntegerNode(12...13)(), nil, nil, (15...18)),
        (15...18)
      )]
   )
diff --git a/test/snapshots/seattlerb/module_comments.txt b/test/snapshots/seattlerb/module_comments.txt
index 5d11f23a..808f4799 100644
--- a/test/snapshots/seattlerb/module_comments.txt
+++ b/test/snapshots/seattlerb/module_comments.txt
@@ -1,26 +1,26 @@
-ProgramNode(22...75)(
+ProgramNode(24...77)(
   [],
-  StatementsNode(22...75)(
-    [ModuleNode(22...75)(
+  StatementsNode(24...77)(
+    [ModuleNode(24...77)(
        [],
-       (22...28),
-       ConstantReadNode(29...30)(),
-       StatementsNode(44...71)(
-         [DefNode(44...71)(
-            (48...52),
+       (24...30),
+       ConstantReadNode(31...32)(),
+       StatementsNode(46...73)(
+         [DefNode(46...73)(
+            (50...54),
             nil,
             nil,
             nil,
             [],
-            (44...47),
+            (46...49),
             nil,
             nil,
             nil,
             nil,
-            (68...71)
+            (70...73)
           )]
        ),
-       (72...75)
+       (74...77)
      )]
   )
 )
diff --git a/test/snapshots/seattlerb/parse_line_trailing_newlines.txt b/test/snapshots/seattlerb/parse_line_trailing_newlines.txt
index e48c6006..7617e154 100644
--- a/test/snapshots/seattlerb/parse_line_trailing_newlines.txt
+++ b/test/snapshots/seattlerb/parse_line_trailing_newlines.txt
@@ -1,7 +1,7 @@
-ProgramNode(0...3)(
+ProgramNode(0...4)(
   [],
-  StatementsNode(0...3)(
+  StatementsNode(0...4)(
     [CallNode(0...1)(nil, nil, (0...1), nil, nil, nil, nil, 2, "a"),
-     CallNode(2...3)(nil, nil, (2...3), nil, nil, nil, nil, 2, "b")]
+     CallNode(3...4)(nil, nil, (3...4), nil, nil, nil, nil, 2, "b")]
   )
 )
diff --git a/test/snapshots/seattlerb/pct_Q_backslash_nl.txt b/test/snapshots/seattlerb/pct_Q_backslash_nl.txt
index 69721af3..0725aaee 100644
--- a/test/snapshots/seattlerb/pct_Q_backslash_nl.txt
+++ b/test/snapshots/seattlerb/pct_Q_backslash_nl.txt
@@ -1,4 +1,6 @@
 ProgramNode(0...7)(
   [],
-  StatementsNode(0...7)([StringNode(0...7)((0...3), (3...6), (6...7), " ")])
+  StatementsNode(0...7)(
+    [StringNode(0...7)((0...3), (3...6), (6...7), " \\\n")]
+  )
 )
diff --git a/test/snapshots/unparser/corpus/literal/send.txt b/test/snapshots/unparser/corpus/literal/send.txt
index e6ba9654..d41d29f7 100644
--- a/test/snapshots/unparser/corpus/literal/send.txt
+++ b/test/snapshots/unparser/corpus/literal/send.txt
@@ -1,6 +1,6 @@
-ProgramNode(0...991)(
+ProgramNode(0...999)(
   [],
-  StatementsNode(0...991)(
+  StatementsNode(0...999)(
     [ModuleNode(0...35)(
        [:foo, :a, :_],
        (0...6),
@@ -2026,6 +2026,29 @@ ProgramNode(0...991)(
        nil,
        0,
        "~"
+     ),
+     CallNode(992...999)(
+       CallNode(992...993)(nil, nil, (992...993), nil, nil, nil, nil, 2, "a"),
+       (993...995),
+       (995...996),
+       (996...997),
+       ArgumentsNode(997...998)(
+         [CallNode(997...998)(
+            nil,
+            nil,
+            (997...998),
+            nil,
+            nil,
+            nil,
+            nil,
+            2,
+            "b"
+          )]
+       ),
+       (998...999),
+       nil,
+       1,
+       "+"
      )]
   )
 )
diff --git a/test/snapshots/whitequark/dedenting_heredoc.txt b/test/snapshots/whitequark/dedenting_heredoc.txt
index 10692b50..d74deb09 100644
--- a/test/snapshots/whitequark/dedenting_heredoc.txt
+++ b/test/snapshots/whitequark/dedenting_heredoc.txt
@@ -1,6 +1,6 @@
-ProgramNode(0...309)(
+ProgramNode(0...313)(
   [],
-  StatementsNode(0...309)(
+  StatementsNode(0...313)(
     [CallNode(0...8)(
        nil,
        nil,
@@ -206,13 +206,13 @@ ProgramNode(0...309)(
        ArgumentsNode(225...229)(
          [InterpolatedStringNode(225...229)(
             (225...229),
-            [StringNode(230...239)(
+            [StringNode(230...243)(
                nil,
-               (230...239),
+               (230...243),
                nil,
-               "x\n" + "\n" + "y\n"
+               "x\n" + "  \n" + "y\n"
              )],
-            (239...241)
+            (243...245)
           )]
        ),
        nil,
@@ -220,16 +220,16 @@ ProgramNode(0...309)(
        0,
        "p"
      ),
-     CallNode(242...248)(
+     CallNode(246...252)(
        nil,
        nil,
-       (242...243),
+       (246...247),
        nil,
-       ArgumentsNode(244...248)(
-         [InterpolatedStringNode(244...248)(
-            (244...248),
-            [StringNode(249...259)(nil, (249...259), nil, "x\n" + "  y\n")],
-            (259...261)
+       ArgumentsNode(248...252)(
+         [InterpolatedStringNode(248...252)(
+            (248...252),
+            [StringNode(253...263)(nil, (253...263), nil, "x\n" + "  y\n")],
+            (263...265)
           )]
        ),
        nil,
@@ -237,16 +237,16 @@ ProgramNode(0...309)(
        0,
        "p"
      ),
-     CallNode(262...268)(
+     CallNode(266...272)(
        nil,
        nil,
-       (262...263),
+       (266...267),
        nil,
-       ArgumentsNode(264...268)(
-         [InterpolatedStringNode(264...268)(
-            (264...268),
-            [StringNode(269...273)(nil, (269...273), nil, "x\n")],
-            (273...275)
+       ArgumentsNode(268...272)(
+         [InterpolatedStringNode(268...272)(
+            (268...272),
+            [StringNode(273...277)(nil, (273...277), nil, "x\n")],
+            (277...279)
           )]
        ),
        nil,
@@ -254,16 +254,16 @@ ProgramNode(0...309)(
        0,
        "p"
      ),
-     CallNode(276...282)(
+     CallNode(280...286)(
        nil,
        nil,
-       (276...277),
+       (280...281),
        nil,
-       ArgumentsNode(278...282)(
-         [InterpolatedStringNode(278...282)(
-            (278...282),
-            [StringNode(283...288)(nil, (283...288), nil, "ð\n")],
-            (288...290)
+       ArgumentsNode(282...286)(
+         [InterpolatedStringNode(282...286)(
+            (282...286),
+            [StringNode(287...292)(nil, (287...292), nil, "ð\n")],
+            (292...294)
           )]
        ),
        nil,
@@ -271,35 +271,35 @@ ProgramNode(0...309)(
        0,
        "p"
      ),
-     CallNode(291...297)(
+     CallNode(295...301)(
        nil,
        nil,
-       (291...292),
+       (295...296),
        nil,
-       ArgumentsNode(293...297)(
-         [InterpolatedStringNode(293...297)((293...297), [], (298...300))]
+       ArgumentsNode(297...301)(
+         [InterpolatedStringNode(297...301)((297...301), [], (302...304))]
        ),
        nil,
        nil,
        0,
        "p"
      ),
-     CallNode(301...309)(
+     CallNode(305...313)(
        nil,
        nil,
-       (301...302),
+       (305...306),
        nil,
-       ArgumentsNode(303...309)(
-         [InterpolatedXStringNode(303...309)(
-            (303...309),
-            [StringNode(310...318)(nil, (310...318), nil, "  x\n"),
-             EmbeddedStatementsNode(318...324)(
-               (318...320),
-               StatementsNode(320...323)(
-                 [CallNode(320...323)(
+       ArgumentsNode(307...313)(
+         [InterpolatedXStringNode(307...313)(
+            (307...313),
+            [StringNode(314...322)(nil, (314...322), nil, "  x\n"),
+             EmbeddedStatementsNode(322...328)(
+               (322...324),
+               StatementsNode(324...327)(
+                 [CallNode(324...327)(
                     nil,
                     nil,
-                    (320...323),
+                    (324...327),
                     nil,
                     nil,
                     nil,
@@ -308,10 +308,10 @@ ProgramNode(0...309)(
                     "foo"
                   )]
                ),
-               (323...324)
+               (327...328)
              ),
-             StringNode(324...325)(nil, (324...325), nil, "\n")],
-            (325...327)
+             StringNode(328...329)(nil, (328...329), nil, "\n")],
+            (329...331)
           )]
        ),
        nil,
diff --git a/test/snapshots/whitequark/interp_digit_var.txt b/test/snapshots/whitequark/interp_digit_var.txt
index 45d18f9e..4af55a97 100644
--- a/test/snapshots/whitequark/interp_digit_var.txt
+++ b/test/snapshots/whitequark/interp_digit_var.txt
@@ -1,123 +1,123 @@
-ProgramNode(1...433)(
+ProgramNode(1...465)(
   [],
-  StatementsNode(1...433)(
+  StatementsNode(1...465)(
     [StringNode(1...6)((1...2), (2...5), (5...6), "\#@1"),
-     StringNode(9...15)((9...10), (10...14), (14...15), "\#@@1"),
-     ArrayNode(18...25)(
-       [SymbolNode(21...24)(nil, (21...24), nil, "\#@1")],
-       (18...21),
-       (24...25)
-     ),
-     ArrayNode(28...36)(
-       [SymbolNode(31...35)(nil, (31...35), nil, "\#@@1")],
-       (28...31),
-       (35...36)
-     ),
-     StringNode(39...46)((39...42), (42...45), (45...46), "\#@1"),
-     StringNode(49...57)((49...52), (52...56), (56...57), "\#@@1"),
-     ArrayNode(60...67)(
-       [StringNode(63...66)(nil, (63...66), nil, "\#@1")],
-       (60...63),
-       (66...67)
-     ),
-     ArrayNode(70...78)(
-       [StringNode(73...77)(nil, (73...77), nil, "\#@@1")],
-       (70...73),
-       (77...78)
-     ),
-     ArrayNode(81...90)(
-       [SymbolNode(85...88)(nil, (85...88), nil, "\#@1")],
-       (81...84),
-       (89...90)
-     ),
-     ArrayNode(93...103)(
-       [SymbolNode(97...101)(nil, (97...101), nil, "\#@@1")],
-       (93...96),
-       (102...103)
-     ),
-     StringNode(106...113)((106...109), (109...112), (112...113), "\#@1"),
-     StringNode(116...124)((116...119), (119...123), (123...124), "\#@@1"),
-     RegularExpressionNode(127...134)(
-       (127...130),
-       (130...133),
-       (133...134),
+     StringNode(10...16)((10...11), (11...15), (15...16), "\#@@1"),
+     ArrayNode(20...27)(
+       [SymbolNode(23...26)(nil, (23...26), nil, "\#@1")],
+       (20...23),
+       (26...27)
+     ),
+     ArrayNode(31...39)(
+       [SymbolNode(34...38)(nil, (34...38), nil, "\#@@1")],
+       (31...34),
+       (38...39)
+     ),
+     StringNode(43...50)((43...46), (46...49), (49...50), "\#@1"),
+     StringNode(54...62)((54...57), (57...61), (61...62), "\#@@1"),
+     ArrayNode(66...73)(
+       [StringNode(69...72)(nil, (69...72), nil, "\#@1")],
+       (66...69),
+       (72...73)
+     ),
+     ArrayNode(77...85)(
+       [StringNode(80...84)(nil, (80...84), nil, "\#@@1")],
+       (77...80),
+       (84...85)
+     ),
+     ArrayNode(89...98)(
+       [SymbolNode(93...96)(nil, (93...96), nil, "\#@1")],
+       (89...92),
+       (97...98)
+     ),
+     ArrayNode(102...112)(
+       [SymbolNode(106...110)(nil, (106...110), nil, "\#@@1")],
+       (102...105),
+       (111...112)
+     ),
+     StringNode(116...123)((116...119), (119...122), (122...123), "\#@1"),
+     StringNode(127...135)((127...130), (130...134), (134...135), "\#@@1"),
+     RegularExpressionNode(139...146)(
+       (139...142),
+       (142...145),
+       (145...146),
        "\#@1",
        0
      ),
-     RegularExpressionNode(137...145)(
-       (137...140),
-       (140...144),
-       (144...145),
+     RegularExpressionNode(150...158)(
+       (150...153),
+       (153...157),
+       (157...158),
        "\#@@1",
        0
      ),
-     SymbolNode(148...155)((148...151), (151...154), (154...155), "\#@1"),
-     SymbolNode(158...166)((158...161), (161...165), (165...166), "\#@@1"),
-     ArrayNode(169...178)(
-       [StringNode(173...176)(nil, (173...176), nil, "\#@1")],
-       (169...172),
-       (177...178)
-     ),
-     ArrayNode(181...191)(
-       [StringNode(185...189)(nil, (185...189), nil, "\#@@1")],
-       (181...184),
-       (190...191)
-     ),
-     XStringNode(194...201)((194...197), (197...200), (200...201), "\#@1"),
-     XStringNode(204...212)((204...207), (207...211), (211...212), "\#@@1"),
-     StringNode(215...221)((215...217), (217...220), (220...221), "\#@1"),
-     StringNode(224...231)((224...226), (226...230), (230...231), "\#@@1"),
-     StringNode(234...239)((234...235), (235...238), (238...239), "\#@1"),
-     StringNode(242...248)((242...243), (243...247), (247...248), "\#@@1"),
-     RegularExpressionNode(251...256)(
-       (251...252),
-       (252...255),
-       (255...256),
+     SymbolNode(162...169)((162...165), (165...168), (168...169), "\#@1"),
+     SymbolNode(173...181)((173...176), (176...180), (180...181), "\#@@1"),
+     ArrayNode(185...194)(
+       [StringNode(189...192)(nil, (189...192), nil, "\#@1")],
+       (185...188),
+       (193...194)
+     ),
+     ArrayNode(198...208)(
+       [StringNode(202...206)(nil, (202...206), nil, "\#@@1")],
+       (198...201),
+       (207...208)
+     ),
+     XStringNode(212...219)((212...215), (215...218), (218...219), "\#@1"),
+     XStringNode(223...231)((223...226), (226...230), (230...231), "\#@@1"),
+     StringNode(235...241)((235...237), (237...240), (240...241), "\#@1"),
+     StringNode(245...252)((245...247), (247...251), (251...252), "\#@@1"),
+     StringNode(256...261)((256...257), (257...260), (260...261), "\#@1"),
+     StringNode(265...271)((265...266), (266...270), (270...271), "\#@@1"),
+     RegularExpressionNode(275...280)(
+       (275...276),
+       (276...279),
+       (279...280),
        "\#@1",
        0
      ),
-     RegularExpressionNode(259...265)(
-       (259...260),
-       (260...264),
-       (264...265),
+     RegularExpressionNode(284...290)(
+       (284...285),
+       (285...289),
+       (289...290),
        "\#@@1",
        0
      ),
-     SymbolNode(268...274)(nil, (270...273), nil, "\#@1"),
-     SymbolNode(277...284)(nil, (279...283), nil, "\#@@1"),
-     SymbolNode(287...293)((287...289), (289...292), (292...293), "\#@1"),
-     SymbolNode(296...303)((296...298), (298...302), (302...303), "\#@@1"),
-     XStringNode(306...311)((306...307), (307...310), (310...311), "\#@1"),
-     XStringNode(314...320)((314...315), (315...319), (319...320), "\#@@1"),
-     InterpolatedStringNode(322...331)(
-       (322...331),
-       [StringNode(332...336)(nil, (332...336), nil, "\#@1\n")],
-       (336...341)
-     ),
-     InterpolatedStringNode(342...351)(
-       (342...351),
-       [StringNode(352...357)(nil, (352...357), nil, "\#@@1\n")],
-       (357...362)
-     ),
-     InterpolatedStringNode(363...372)(
-       (363...372),
-       [StringNode(373...377)(nil, (373...377), nil, "\#@1\n")],
-       (377...382)
-     ),
-     InterpolatedStringNode(383...392)(
-       (383...392),
-       [StringNode(393...398)(nil, (393...398), nil, "\#@@1\n")],
-       (398...403)
-     ),
-     InterpolatedXStringNode(404...413)(
-       (404...413),
-       [StringNode(414...418)(nil, (414...418), nil, "\#@1\n")],
-       (418...423)
-     ),
-     InterpolatedXStringNode(424...433)(
-       (424...433),
-       [StringNode(434...439)(nil, (434...439), nil, "\#@@1\n")],
-       (439...444)
+     SymbolNode(294...300)(nil, (296...299), nil, "\#@1"),
+     SymbolNode(304...311)(nil, (306...310), nil, "\#@@1"),
+     SymbolNode(315...321)((315...317), (317...320), (320...321), "\#@1"),
+     SymbolNode(325...332)((325...327), (327...331), (331...332), "\#@@1"),
+     XStringNode(336...341)((336...337), (337...340), (340...341), "\#@1"),
+     XStringNode(345...351)((345...346), (346...350), (350...351), "\#@@1"),
+     InterpolatedStringNode(354...363)(
+       (354...363),
+       [StringNode(364...368)(nil, (364...368), nil, "\#@1\n")],
+       (368...373)
+     ),
+     InterpolatedStringNode(374...383)(
+       (374...383),
+       [StringNode(384...389)(nil, (384...389), nil, "\#@@1\n")],
+       (389...394)
+     ),
+     InterpolatedStringNode(395...404)(
+       (395...404),
+       [StringNode(405...409)(nil, (405...409), nil, "\#@1\n")],
+       (409...414)
+     ),
+     InterpolatedStringNode(415...424)(
+       (415...424),
+       [StringNode(425...430)(nil, (425...430), nil, "\#@@1\n")],
+       (430...435)
+     ),
+     InterpolatedXStringNode(436...445)(
+       (436...445),
+       [StringNode(446...450)(nil, (446...450), nil, "\#@1\n")],
+       (450...455)
+     ),
+     InterpolatedXStringNode(456...465)(
+       (456...465),
+       [StringNode(466...471)(nil, (466...471), nil, "\#@@1\n")],
+       (471...476)
      )]
   )
 )
diff --git a/test/snapshots/whitequark/procarg0.txt b/test/snapshots/whitequark/procarg0.txt
index 8153bfdd..fb43e872 100644
--- a/test/snapshots/whitequark/procarg0.txt
+++ b/test/snapshots/whitequark/procarg0.txt
@@ -1,6 +1,6 @@
-ProgramNode(0...31)(
+ProgramNode(0...32)(
   [],
-  StatementsNode(0...31)(
+  StatementsNode(0...32)(
     [CallNode(0...18)(
        nil,
        nil,
@@ -36,18 +36,18 @@ ProgramNode(0...31)(
        0,
        "m"
      ),
-     CallNode(20...31)(
+     CallNode(21...32)(
        nil,
        nil,
-       (20...21),
+       (21...22),
        nil,
        nil,
        nil,
-       BlockNode(22...31)(
+       BlockNode(23...32)(
          [:foo],
-         BlockParametersNode(24...29)(
-           ParametersNode(25...28)(
-             [RequiredParameterNode(25...28)(:foo)],
+         BlockParametersNode(25...30)(
+           ParametersNode(26...29)(
+             [RequiredParameterNode(26...29)(:foo)],
              [],
              [],
              nil,
@@ -56,12 +56,12 @@ ProgramNode(0...31)(
              nil
            ),
            [],
-           (24...25),
-           (28...29)
+           (25...26),
+           (29...30)
          ),
          nil,
-         (22...23),
-         (30...31)
+         (23...24),
+         (31...32)
        ),
        0,
        "m"