Skip to content

Commit e1b18cb

Browse files
committed
Implement various string start/end events for ripper
`tstring_beg` in particular is needed by `yard`. Before: > 1980 examples, 606 failures, 15 pending After: > 1980 examples, 582 failures, 15 pending Thought it would be more, but oh well. It needs `on_sp` which I guess is why there are not many new passes
1 parent 40ca8a4 commit e1b18cb

File tree

2 files changed

+136
-110
lines changed

2 files changed

+136
-110
lines changed

lib/prism/translation/ripper.rb

Lines changed: 134 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ module Translation
3535
# - on_rparen
3636
# - on_semicolon
3737
# - on_sp
38-
# - on_symbeg
39-
# - on_tstring_beg
40-
# - on_tstring_end
4138
#
4239
class Ripper < Compiler
4340
# Parses the given Ruby program read from +src+.
@@ -2235,61 +2232,67 @@ def visit_interpolated_regular_expression_node(node)
22352232
# "foo #{bar}"
22362233
# ^^^^^^^^^^^^
22372234
def visit_interpolated_string_node(node)
2238-
if node.opening&.start_with?("<<~")
2239-
heredoc = visit_heredoc_string_node(node)
2235+
with_string_bounds(node) do
2236+
if node.opening&.start_with?("<<~")
2237+
heredoc = visit_heredoc_string_node(node)
22402238

2241-
bounds(node.location)
2242-
on_string_literal(heredoc)
2243-
elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2244-
first, *rest = node.parts
2245-
rest.inject(visit(first)) do |content, part|
2246-
concat = visit(part)
2247-
2248-
bounds(part.location)
2249-
on_string_concat(content, concat)
2250-
end
2251-
else
2252-
bounds(node.parts.first.location)
2253-
parts =
2254-
node.parts.inject(on_string_content) do |content, part|
2255-
on_string_add(content, visit_string_content(part))
2239+
bounds(node.location)
2240+
on_string_literal(heredoc)
2241+
elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2242+
first, *rest = node.parts
2243+
rest.inject(visit(first)) do |content, part|
2244+
concat = visit(part)
2245+
2246+
bounds(part.location)
2247+
on_string_concat(content, concat)
22562248
end
2249+
else
2250+
bounds(node.parts.first.location)
2251+
parts =
2252+
node.parts.inject(on_string_content) do |content, part|
2253+
on_string_add(content, visit_string_content(part))
2254+
end
22572255

2258-
bounds(node.location)
2259-
on_string_literal(parts)
2256+
bounds(node.location)
2257+
on_string_literal(parts)
2258+
end
22602259
end
22612260
end
22622261

22632262
# :"foo #{bar}"
22642263
# ^^^^^^^^^^^^^
22652264
def visit_interpolated_symbol_node(node)
2266-
bounds(node.parts.first.location)
2267-
parts =
2268-
node.parts.inject(on_string_content) do |content, part|
2269-
on_string_add(content, visit_string_content(part))
2270-
end
2265+
with_string_bounds(node) do
2266+
bounds(node.parts.first.location)
2267+
parts =
2268+
node.parts.inject(on_string_content) do |content, part|
2269+
on_string_add(content, visit_string_content(part))
2270+
end
22712271

2272-
bounds(node.location)
2273-
on_dyna_symbol(parts)
2272+
bounds(node.location)
2273+
on_dyna_symbol(parts)
2274+
end
22742275
end
22752276

22762277
# `foo #{bar}`
22772278
# ^^^^^^^^^^^^
22782279
def visit_interpolated_x_string_node(node)
2279-
if node.opening.start_with?("<<~")
2280-
heredoc = visit_heredoc_x_string_node(node)
2280+
with_string_bounds(node) do
2281+
if node.opening.start_with?("<<~")
2282+
heredoc = visit_heredoc_x_string_node(node)
22812283

2282-
bounds(node.location)
2283-
on_xstring_literal(heredoc)
2284-
else
2285-
bounds(node.parts.first.location)
2286-
parts =
2287-
node.parts.inject(on_xstring_new) do |content, part|
2288-
on_xstring_add(content, visit_string_content(part))
2289-
end
2284+
bounds(node.location)
2285+
on_xstring_literal(heredoc)
2286+
else
2287+
bounds(node.parts.first.location)
2288+
parts =
2289+
node.parts.inject(on_xstring_new) do |content, part|
2290+
on_xstring_add(content, visit_string_content(part))
2291+
end
22902292

2291-
bounds(node.location)
2292-
on_xstring_literal(parts)
2293+
bounds(node.location)
2294+
on_xstring_literal(parts)
2295+
end
22932296
end
22942297
end
22952298

@@ -3022,24 +3025,60 @@ def visit_statements_node(node)
30223025
# "foo"
30233026
# ^^^^^
30243027
def visit_string_node(node)
3025-
if (content = node.content).empty?
3026-
bounds(node.location)
3027-
on_string_literal(on_string_content)
3028-
elsif (opening = node.opening) == "?"
3029-
bounds(node.location)
3030-
on_CHAR("?#{node.content}")
3031-
elsif opening.start_with?("<<~")
3032-
heredoc = visit_heredoc_string_node(node.to_interpolated)
3028+
with_string_bounds(node) do
3029+
if (content = node.content).empty?
3030+
bounds(node.location)
3031+
on_string_literal(on_string_content)
3032+
elsif (opening = node.opening) == "?"
3033+
bounds(node.location)
3034+
on_CHAR("?#{node.content}")
3035+
elsif opening.start_with?("<<~")
3036+
heredoc = visit_heredoc_string_node(node.to_interpolated)
30333037

3034-
bounds(node.location)
3035-
on_string_literal(heredoc)
3036-
else
3037-
bounds(node.content_loc)
3038-
tstring_content = on_tstring_content(content)
3038+
bounds(node.location)
3039+
on_string_literal(heredoc)
3040+
else
3041+
bounds(node.content_loc)
3042+
tstring_content = on_tstring_content(content)
30393043

3040-
bounds(node.location)
3041-
on_string_literal(on_string_add(on_string_content, tstring_content))
3044+
bounds(node.location)
3045+
on_string_literal(on_string_add(on_string_content, tstring_content))
3046+
end
3047+
end
3048+
end
3049+
3050+
# Responsible for emitting the various string-like begin/end events
3051+
private def with_string_bounds(node)
3052+
# `foo "bar": baz` doesn't emit the closing location
3053+
assoc = !(opening = node.opening)&.include?(":") && node.closing&.end_with?(":")
3054+
3055+
is_heredoc = opening&.start_with?("<<")
3056+
if is_heredoc
3057+
bounds(node.opening_loc)
3058+
on_heredoc_beg(node.opening)
3059+
elsif opening&.start_with?(":", "%s")
3060+
bounds(node.opening_loc)
3061+
on_symbeg(node.opening)
3062+
elsif opening&.start_with?("`", "%x")
3063+
bounds(node.opening_loc)
3064+
on_backtick(node.opening)
3065+
elsif opening && !opening.start_with?("?")
3066+
bounds(node.opening_loc)
3067+
on_tstring_beg(opening)
30423068
end
3069+
3070+
result = yield
3071+
return result if assoc
3072+
3073+
if is_heredoc
3074+
bounds(node.closing_loc)
3075+
on_heredoc_end(node.closing)
3076+
elsif node.closing_loc
3077+
bounds(node.closing_loc)
3078+
on_tstring_end(node.closing)
3079+
end
3080+
3081+
result
30433082
end
30443083

30453084
# Ripper gives back the escaped string content but strips out the common
@@ -3119,36 +3158,18 @@ def visit_string_node(node)
31193158

31203159
# Visit a heredoc node that is representing a string.
31213160
private def visit_heredoc_string_node(node)
3122-
bounds(node.opening_loc)
3123-
on_heredoc_beg(node.opening)
3124-
31253161
bounds(node.location)
3126-
result =
3127-
visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3128-
on_string_add(parts, part)
3129-
end
3130-
3131-
bounds(node.closing_loc)
3132-
on_heredoc_end(node.closing)
3133-
3134-
result
3162+
visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3163+
on_string_add(parts, part)
3164+
end
31353165
end
31363166

31373167
# Visit a heredoc node that is representing an xstring.
31383168
private def visit_heredoc_x_string_node(node)
3139-
bounds(node.opening_loc)
3140-
on_heredoc_beg(node.opening)
3141-
31423169
bounds(node.location)
3143-
result =
3144-
visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3145-
on_xstring_add(parts, part)
3146-
end
3147-
3148-
bounds(node.closing_loc)
3149-
on_heredoc_end(node.closing)
3150-
3151-
result
3170+
visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3171+
on_xstring_add(parts, part)
3172+
end
31523173
end
31533174

31543175
# super(foo)
@@ -3175,23 +3196,25 @@ def visit_super_node(node)
31753196
# :foo
31763197
# ^^^^
31773198
def visit_symbol_node(node)
3178-
if node.value_loc.nil?
3179-
bounds(node.location)
3180-
on_dyna_symbol(on_string_content)
3181-
elsif (opening = node.opening)&.match?(/^%s|['"]:?$/)
3182-
bounds(node.value_loc)
3183-
content = on_string_add(on_string_content, on_tstring_content(node.value))
3184-
bounds(node.location)
3185-
on_dyna_symbol(content)
3186-
elsif (closing = node.closing) == ":"
3187-
bounds(node.location)
3188-
on_label("#{node.value}:")
3189-
elsif opening.nil? && node.closing_loc.nil?
3190-
bounds(node.value_loc)
3191-
on_symbol_literal(visit_token(node.value))
3192-
else
3193-
bounds(node.value_loc)
3194-
on_symbol_literal(on_symbol(visit_token(node.value)))
3199+
with_string_bounds(node) do
3200+
if node.value_loc.nil?
3201+
bounds(node.location)
3202+
on_dyna_symbol(on_string_content)
3203+
elsif (opening = node.opening)&.match?(/^%s|['"]:?$/)
3204+
bounds(node.value_loc)
3205+
content = on_string_add(on_string_content, on_tstring_content(node.value))
3206+
bounds(node.location)
3207+
on_dyna_symbol(content)
3208+
elsif (closing = node.closing) == ":"
3209+
bounds(node.location)
3210+
on_label("#{node.value}:")
3211+
elsif opening.nil? && node.closing_loc.nil?
3212+
bounds(node.value_loc)
3213+
on_symbol_literal(visit_token(node.value))
3214+
else
3215+
bounds(node.value_loc)
3216+
on_symbol_literal(on_symbol(visit_token(node.value)))
3217+
end
31953218
end
31963219
end
31973220

@@ -3314,20 +3337,22 @@ def visit_while_node(node)
33143337
# `foo`
33153338
# ^^^^^
33163339
def visit_x_string_node(node)
3317-
if node.unescaped.empty?
3318-
bounds(node.location)
3319-
on_xstring_literal(on_xstring_new)
3320-
elsif node.opening.start_with?("<<~")
3321-
heredoc = visit_heredoc_x_string_node(node.to_interpolated)
3340+
with_string_bounds(node) do
3341+
if node.unescaped.empty?
3342+
bounds(node.location)
3343+
on_xstring_literal(on_xstring_new)
3344+
elsif node.opening.start_with?("<<~")
3345+
heredoc = visit_heredoc_x_string_node(node.to_interpolated)
33223346

3323-
bounds(node.location)
3324-
on_xstring_literal(heredoc)
3325-
else
3326-
bounds(node.content_loc)
3327-
content = on_tstring_content(node.content)
3347+
bounds(node.location)
3348+
on_xstring_literal(heredoc)
3349+
else
3350+
bounds(node.content_loc)
3351+
content = on_tstring_content(node.content)
33283352

3329-
bounds(node.location)
3330-
on_xstring_literal(on_xstring_add(on_xstring_new, content))
3353+
bounds(node.location)
3354+
on_xstring_literal(on_xstring_add(on_xstring_new, content))
3355+
end
33313356
end
33323357
end
33333358

test/prism/ruby/ripper_test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class RipperTest < TestCase
106106
"variables.txt",
107107
"whitequark/dedenting_heredoc.txt",
108108
"whitequark/masgn_nested.txt",
109+
"whitequark/newline_in_hash_argument.txt",
109110
"whitequark/numparam_ruby_bug_19025.txt",
110111
"whitequark/op_asgn_cmd.txt",
111112
"whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt",
@@ -135,7 +136,7 @@ def test_lex_ignored_missing_heredoc_end
135136
end
136137
end
137138

138-
UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ignored_nl kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp]
139+
UNSUPPORTED_EVENTS = %i[comma ignored_nl kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp words_sep ignored_sp]
139140
SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS
140141

141142
module Events

0 commit comments

Comments
 (0)