Skip to content

Commit 5857b8f

Browse files
committed
Fix ERB template highlighting crash when compiled longer than source
This ensures that the debug template will be shown even if the error highlighting is on the wrong line. When the compiled template is much longer than the source, errors can occur on lines that don't exist on the source. Avoid crashing so the exception middleware can display.
1 parent 9add4d9 commit 5857b8f

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

actionview/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Fix a crash in ERB template error highlighting when the error occurs on a
2+
line in the compiled template that is past the end of the source template.
3+
4+
*Martin Emde*
5+
16
* Improve reliability of ERB template error highlighting.
27
Fix infinite loops and crashes in highlighting and
38
improve tolerance for alternate ERB handlers.

actionview/lib/action_view/template/handlers/erb.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ def handles_encoding?
4242
# source location inside the template.
4343
def translate_location(spot, backtrace_location, source)
4444
# Tokenize the source line
45-
tokens = ::ERB::Util.tokenize(source.lines[backtrace_location.lineno - 1])
45+
source_lines = source.lines
46+
return nil if source_lines.size < backtrace_location.lineno
47+
tokens = ::ERB::Util.tokenize(source_lines[backtrace_location.lineno - 1])
4648
new_first_column = find_offset(spot[:snippet], tokens, spot[:first_column])
4749
lineno_delta = spot[:first_lineno] - backtrace_location.lineno
4850
spot[:first_lineno] -= lineno_delta
@@ -51,7 +53,7 @@ def translate_location(spot, backtrace_location, source)
5153
column_delta = spot[:first_column] - new_first_column
5254
spot[:first_column] -= column_delta
5355
spot[:last_column] -= column_delta
54-
spot[:script_lines] = source.lines
56+
spot[:script_lines] = source_lines
5557

5658
spot
5759
rescue NotImplementedError, LocationParsingError

actionview/test/template/template_test.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,11 @@ def test_template_translate_location
339339
assert_equal expected, new_template(source).translate_location(backtrace_location, spot)
340340
end
341341

342+
# merely tests for the case where the backtrace and spot disagree about lineno
342343
def test_template_translate_location_lineno_offset
343344
highlight = "nomethoderror"
344345
source = "<%= nomethoderror %>"
345-
compiled = "\n'.freeze; @output_buffer.append= nomethoderror ; @output_buffer.safe_append='\n"
346+
compiled = "'.freeze; @output_buffer.append= nomethoderror ; @output_buffer.safe_append='"
346347

347348
backtrace_location = Data.define(:lineno).new(lineno: 1)
348349
spot = spot_highlight(compiled, highlight, first_lineno: 2, last_lineno: 2)
@@ -351,6 +352,21 @@ def test_template_translate_location_lineno_offset
351352
assert_equal expected, new_template(source).translate_location(backtrace_location, spot)
352353
end
353354

355+
# We are testing the failure case here. `find_offset` doesn't correctly handle the case
356+
# where the line number is not the same in the backtrace and template.
357+
def test_template_translate_location_with_multiline_code_source
358+
highlight = "nomethoderror"
359+
source = "<%=\ngood(\n nomethoderror\n) %>"
360+
extracted_line = " nomethoderror\n"
361+
compiled = "ValidatedOutputBuffer.wrap(@output_buffer, ({}), ' \ngood(\n nomethoderror\n" \
362+
") '.freeze, true).safe_none_append=( \ngood(\n nomethoderror\n) );\n@output_buffer"
363+
364+
backtrace_location = Data.define(:lineno).new(lineno: 6)
365+
spot = spot_highlight(compiled, highlight, first_column: 1, first_lineno: 6, last_lineno: 6, snippet: extracted_line)
366+
367+
assert_equal spot, new_template(source).translate_location(backtrace_location, spot)
368+
end
369+
354370
def test_template_translate_location_with_multibye_string_before_highlight
355371
highlight = "nomethoderror"
356372
source = String.new("\u{a5}<%= nomethoderror %>", encoding: Encoding::UTF_8) # yen symbol

0 commit comments

Comments
 (0)