Addressed an issue where syntax errors generated inside an eval

method cause an Internal Server Error due to a TypeError.

In order to display the extraced source of a syntax error, we try
to locate the node id for the backtrace location. The call to
RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location is expecting
a Thread::Backtrace::Location object, but we are passing a
SourceMapLocation instead.

This commit does two things:
1) it addresses the issue by making sure that we are always passing
a Thread::Backtrace::Location instead
2) it allows the development view to show the extracted source fragment
This commit is contained in:
Mario Caropreso 2023-08-16 20:05:45 +00:00
parent 1c8be9c67a
commit df6d2fbf2e
3 changed files with 41 additions and 3 deletions

@ -242,7 +242,7 @@ def initialize(location, template)
end
def spot(exc)
if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location)
if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
location = @template.spot(__getobj__)
else
location = super
@ -267,7 +267,12 @@ def build_backtrace
(@exception.backtrace_locations || []).map do |loc|
if built_methods.key?(loc.label.to_s)
SourceMapLocation.new(loc, built_methods[loc.label.to_s])
thread_backtrace_location = if loc.respond_to?(:__getobj__)
loc.__getobj__
else
loc
end
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
else
loc
end

@ -69,6 +69,20 @@ def backtrace
end
end
class_eval "def throw_syntax_error; eval %(
'abc' + pluralize 'def'
); end", "lib/file.rb", 42
test "#source_extracts works with eval syntax error" do
exception = begin throw_syntax_error; rescue SyntaxError => ex; ex; end
wrapper = ExceptionWrapper.new(nil, TopErrorProxy.new(exception, 1))
assert_called_with(wrapper, :source_fragment, ["lib/file.rb", 42], returns: "foo") do
assert_equal [ code: "foo", line_number: 42 ], wrapper.source_extracts
end
end
if defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
test "#source_extracts works with error_highlight" do
lineno = __LINE__

@ -43,7 +43,26 @@ def backtrace_locations
private
def parse_message_for_trace
__getobj__.to_s.split("\n")
if source_location_eval?
# If the exception is coming from a call to eval, we need to keep
# the path of the file in which eval was called to ensure we can
# return the right source fragment to show the location of the
# error
location = __getobj__.backtrace_locations[0]
["#{location.path}:#{location.lineno}: #{__getobj__}"]
else
__getobj__.to_s.split("\n")
end
end
if SyntaxError.method_defined?(:path) # Ruby 3.3+
def source_location_eval?
__getobj__.path.start_with?("(eval")
end
else # 3.2 and older versions of Ruby
def source_location_eval?
__getobj__.to_s.start_with?("(eval")
end
end
end
end