Skip to content

Commit 9fa39a7

Browse files
committed
Use error tokens if there are no correct tokens in the same place
For example, the broken code "%www" will result in only one error token.
1 parent 54f90cb commit 9fa39a7

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

lib/irb/ruby-lex.rb

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ def set_input(io, p = nil, &block)
6666
unprocessed_tokens = []
6767
line_num_offset = 0
6868
tokens.each do |t|
69-
next if t[1] == :on_parse_error || t[1] == :compile_error
7069
partial_tokens << t
7170
unprocessed_tokens << t
7271
if t[2].include?("\n")
@@ -107,13 +106,35 @@ def set_prompt(p = nil, &block)
107106
end
108107
end
109108

109+
ERROR_TOKENS = [
110+
:on_parse_error,
111+
:compile_error,
112+
:on_assign_error,
113+
:on_alias_error,
114+
:on_class_name_error,
115+
:on_param_error
116+
]
117+
110118
def ripper_lex_without_warning(code)
111119
verbose, $VERBOSE = $VERBOSE, nil
112120
tokens = nil
113121
self.class.compile_with_errors_suppressed(code) do |inner_code, line_no|
114122
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
115123
if lexer.respond_to?(:scan) # Ruby 2.7+
116-
tokens = lexer.scan
124+
tokens = []
125+
pos_to_index = {}
126+
lexer.scan.each do |t|
127+
if pos_to_index.has_key?(t[0])
128+
index = pos_to_index[t[0]]
129+
found_tk = tokens[index]
130+
if ERROR_TOKENS.include?(found_tk[1]) && !ERROR_TOKENS.include?(t[1])
131+
tokens[index] = t
132+
end
133+
else
134+
pos_to_index[t[0]] = tokens.size
135+
tokens << t
136+
end
137+
end
117138
else
118139
tokens = lexer.parse
119140
end
@@ -128,7 +149,6 @@ def find_prev_spaces(line_index)
128149
prev_spaces = md.nil? ? 0 : md[1].count(' ')
129150
line_count = 0
130151
@tokens.each_with_index do |t, i|
131-
next if t[1] == :on_parse_error || t[1] == :compile_error
132152
if t[2].include?("\n")
133153
line_count += t[2].count("\n")
134154
if line_count >= line_index
@@ -357,7 +377,6 @@ def process_nesting_level(tokens = @tokens)
357377
indent = 0
358378
in_oneliner_def = nil
359379
tokens.each_with_index { |t, index|
360-
next if t[1] == :on_parse_error || t[1] == :compile_error
361380
# detecting one-liner method definition
362381
if in_oneliner_def.nil?
363382
if t[3].allbits?(Ripper::EXPR_ENDFN)
@@ -443,7 +462,6 @@ def check_newline_depth_difference
443462
open_brace_on_line = 0
444463
in_oneliner_def = nil
445464
@tokens.each_with_index do |t, index|
446-
next if t[1] == :on_parse_error || t[1] == :compile_error
447465
# detecting one-liner method definition
448466
if in_oneliner_def.nil?
449467
if t[3].allbits?(Ripper::EXPR_ENDFN)
@@ -513,7 +531,6 @@ def check_corresponding_token_depth
513531
open_brace_on_line = 0
514532
in_oneliner_def = nil
515533
@tokens.each_with_index do |t, index|
516-
next if t[1] == :on_parse_error || t[1] == :compile_error
517534
# detecting one-liner method definition
518535
if in_oneliner_def.nil?
519536
if t[3].allbits?(Ripper::EXPR_ENDFN)

test/irb/test_ruby_lex.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,5 +442,37 @@ def test_dyanmic_prompt_with_blank_line
442442
expected_prompt_list = input_with_prompt.map(&:prompt)
443443
assert_dynamic_prompt(lines, expected_prompt_list)
444444
end
445+
446+
def test_broken_percent_literal
447+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
448+
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
449+
end
450+
451+
ruby_lex = RubyLex.new
452+
tokens = ruby_lex.ripper_lex_without_warning('%wwww')
453+
pos_to_index = {}
454+
tokens.each_with_index { |t, i|
455+
assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
456+
pos_to_index[t[0]] = i
457+
}
458+
end
459+
460+
def test_broken_percent_literal_in_method
461+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0')
462+
skip 'This test needs Ripper::Lexer#scan to take broken tokens'
463+
end
464+
465+
ruby_lex = RubyLex.new
466+
tokens = ruby_lex.ripper_lex_without_warning(<<~EOC.chomp)
467+
def foo
468+
%wwww
469+
end
470+
EOC
471+
pos_to_index = {}
472+
tokens.each_with_index { |t, i|
473+
assert_nil(pos_to_index[t[0]], "There is already another token in the position of #{t.inspect}.")
474+
pos_to_index[t[0]] = i
475+
}
476+
end
445477
end
446478
end

0 commit comments

Comments
 (0)