Repository: ruby/prism
Pull request #3200 (2024-11-04 16:33:31)

Fix up regression in ruby parser translation
Fixes https://github.com/ruby/prism/issues/3186


Patch:
diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb
index 189038d0..465b6934 100644
--- a/lib/prism/translation/ruby_parser.rb
+++ b/lib/prism/translation/ruby_parser.rb
@@ -881,6 +881,7 @@ module Prism
         # Visit the interpolated content of the string-like node.
         private def visit_interpolated_parts(parts)
           visited = []
+
           parts.each do |part|
             result = visit(part)
 
@@ -892,6 +893,7 @@ module Prism
               else
                 visited << result
               end
+              visited << :space
             elsif result[0] == :dstr
               if !visited.empty? && part.parts[0].is_a?(StringNode)
                 # If we are in the middle of an implicitly concatenated string,
@@ -907,8 +909,9 @@ module Prism
           end
 
           state = :beginning #: :beginning | :string_content | :interpolated_content
+          results = []
 
-          visited.each_with_object([]) do |result, results|
+          visited.each_with_index do |result, index|
             case state
             when :beginning
               if result.is_a?(String)
@@ -923,7 +926,9 @@ module Prism
                 state = :interpolated_content
               end
             when :string_content
-              if result.is_a?(String)
+              if result == :space
+                # continue
+              elsif result.is_a?(String)
                 results[0] << result
               elsif result.is_a?(Array) && result[0] == :str
                 results[0] << result[1]
@@ -932,7 +937,9 @@ module Prism
                 state = :interpolated_content
               end
             when :interpolated_content
-              if result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line)
+              if result == :space
+                # continue
+              elsif visited[index - 1] != :space && result.is_a?(Array) && result[0] == :str && results[-1][0] == :str && (results[-1].line_max == result.line)
                 results[-1][1] << result[1]
                 results[-1].line_max = result.line_max
               else
@@ -940,6 +947,8 @@ module Prism
               end
             end
           end
+
+          results
         end
 
         # -> { it }


Test patch:
diff --git a/test/prism/fixtures/strings.txt b/test/prism/fixtures/strings.txt
index 2ce8b738..6963d75b 100644
--- a/test/prism/fixtures/strings.txt
+++ b/test/prism/fixtures/strings.txt
@@ -103,3 +103,5 @@
 %^#$^#
 
 %@#@#
+
+"#{"#{B} C"} D"
diff --git a/test/prism/snapshots/strings.txt b/test/prism/snapshots/strings.txt
index eeef15a6..05b3a27b 100644
--- a/test/prism/snapshots/strings.txt
+++ b/test/prism/snapshots/strings.txt
@@ -1,10 +1,10 @@
-@ ProgramNode (location: (1,0)-(105,4))
+@ ProgramNode (location: (1,0)-(107,15))
 ├── flags: ∅
 ├── locals: []
 └── statements:
-    @ StatementsNode (location: (1,0)-(105,4))
+    @ StatementsNode (location: (1,0)-(107,15))
     ├── flags: ∅
-    └── body: (length: 50)
+    └── body: (length: 51)
         ├── @ StringNode (location: (1,0)-(1,6))
         │   ├── flags: newline
         │   ├── opening_loc: (1,0)-(1,2) = "%%"
@@ -540,9 +540,50 @@
         │   ├── content_loc: (103,2)-(103,4) = "\#$"
         │   ├── closing_loc: (103,4)-(103,5) = "^"
         │   └── unescaped: "\#$"
-        └── @ StringNode (location: (105,0)-(105,4))
+        ├── @ StringNode (location: (105,0)-(105,4))
+        │   ├── flags: newline
+        │   ├── opening_loc: (105,0)-(105,2) = "%@"
+        │   ├── content_loc: (105,2)-(105,3) = "#"
+        │   ├── closing_loc: (105,3)-(105,4) = "@"
+        │   └── unescaped: "#"
+        └── @ InterpolatedStringNode (location: (107,0)-(107,15))
             ├── flags: newline
-            ├── opening_loc: (105,0)-(105,2) = "%@"
-            ├── content_loc: (105,2)-(105,3) = "#"
-            ├── closing_loc: (105,3)-(105,4) = "@"
-            └── unescaped: "#"
+            ├── opening_loc: (107,0)-(107,1) = "\""
+            ├── parts: (length: 2)
+            │   ├── @ EmbeddedStatementsNode (location: (107,1)-(107,12))
+            │   │   ├── flags: ∅
+            │   │   ├── opening_loc: (107,1)-(107,3) = "\#{"
+            │   │   ├── statements:
+            │   │   │   @ StatementsNode (location: (107,3)-(107,11))
+            │   │   │   ├── flags: ∅
+            │   │   │   └── body: (length: 1)
+            │   │   │       └── @ InterpolatedStringNode (location: (107,3)-(107,11))
+            │   │   │           ├── flags: ∅
+            │   │   │           ├── opening_loc: (107,3)-(107,4) = "\""
+            │   │   │           ├── parts: (length: 2)
+            │   │   │           │   ├── @ EmbeddedStatementsNode (location: (107,4)-(107,8))
+            │   │   │           │   │   ├── flags: ∅
+            │   │   │           │   │   ├── opening_loc: (107,4)-(107,6) = "\#{"
+            │   │   │           │   │   ├── statements:
+            │   │   │           │   │   │   @ StatementsNode (location: (107,6)-(107,7))
+            │   │   │           │   │   │   ├── flags: ∅
+            │   │   │           │   │   │   └── body: (length: 1)
+            │   │   │           │   │   │       └── @ ConstantReadNode (location: (107,6)-(107,7))
+            │   │   │           │   │   │           ├── flags: ∅
+            │   │   │           │   │   │           └── name: :B
+            │   │   │           │   │   └── closing_loc: (107,7)-(107,8) = "}"
+            │   │   │           │   └── @ StringNode (location: (107,8)-(107,10))
+            │   │   │           │       ├── flags: static_literal, frozen
+            │   │   │           │       ├── opening_loc: ∅
+            │   │   │           │       ├── content_loc: (107,8)-(107,10) = " C"
+            │   │   │           │       ├── closing_loc: ∅
+            │   │   │           │       └── unescaped: " C"
+            │   │   │           └── closing_loc: (107,10)-(107,11) = "\""
+            │   │   └── closing_loc: (107,11)-(107,12) = "}"
+            │   └── @ StringNode (location: (107,12)-(107,14))
+            │       ├── flags: static_literal, frozen
+            │       ├── opening_loc: ∅
+            │       ├── content_loc: (107,12)-(107,14) = " D"
+            │       ├── closing_loc: ∅
+            │       └── unescaped: " D"
+            └── closing_loc: (107,14)-(107,15) = "\""