Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/prism/lex_compat.rb
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,8 @@ def post_process_tokens(tokens, source, data_loc, bom, eof_token)

cache = Translation::Ripper::LineAndColumnCache.new(source)

brace_depth = 0

tokens.each do |token|
# Skip missing heredoc ends.
next if token[1] == :on_heredoc_end && token[2] == ""
Expand All @@ -854,6 +856,24 @@ def post_process_tokens(tokens, source, data_loc, bom, eof_token)
line, column = token[0]
start_offset = source.byte_offset(line, column)

# Replicate a Ripper quirk where exactly the first unmatched closing
# brace is emitted as :on_embexpr_end, and any subsequent ones are :on_rbrace.
#
# Track brace nesting depth to handle unmatched closing braces.
# :on_lbrace (hashes/blocks), :on_embexpr_beg (interpolation), and :on_tlambeg (lambdas)
# all start a curly brace context balanced by a closing }.
case token[1]
when :on_lbrace, :on_embexpr_beg, :on_tlambeg
brace_depth += 1
when :on_rbrace
if brace_depth == 0
token = [token[0], :on_embexpr_end, token[2], prev_token_state]
end
brace_depth -= 1
when :on_embexpr_end
brace_depth -= 1
end

# Ripper reports columns on line 1 without counting the BOM, so we
# adjust to get the real offset
start_offset += 3 if line == 1 && bom
Expand Down
8 changes: 8 additions & 0 deletions test/prism/ruby/ripper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ def test_lex_invalid_syntax
assert_equal(Ripper.lex('if;)'), Translation::Ripper.lex('if;)'))
end

def test_unmatched_braces
assert_ripper_lex("} }")
assert_ripper_lex("{ b: 1 } }")
assert_ripper_lex("[].each { } }")
assert_ripper_lex('"#{1}" }')
assert_ripper_lex("-> { } }")
end

def test_tokenize
source = "foo;1;BAZ"
assert_equal(Ripper.tokenize(source), Translation::Ripper.tokenize(source))
Expand Down
Loading