Add `node_id_for_backtrace_location` function

We want to use error highlight with eval'd code, specifically ERB
templates. We're able to recover the generated code for eval'd templates
and can get a parse tree for the ERB generated code, but we don't have a
way to get the node id from the backtrace location. So we can't pass the
right node into error highlight.

This patch gives us an API to get the node id from the backtrace
location so we can find the node in the AST.

Error Highlight PR: https://github.com/ruby/error_highlight/pull/26

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
eileencodes 2022-10-19 13:23:53 -04:00 коммит произвёл Yusuke Endoh
Родитель 7ed10abdd9
Коммит 3391c51eff
3 изменённых файлов: 40 добавлений и 0 удалений

12
ast.c
Просмотреть файл

@ -195,6 +195,18 @@ script_lines(VALUE path)
return lines;
}
static VALUE
node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
{
int node_id;
node_id = rb_get_node_id_from_frame_info(location);
if (node_id == -1) {
return Qnil;
}
return INT2NUM(node_id);
}
static VALUE
ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines, VALUE error_tolerant)
{

15
ast.rb
Просмотреть файл

@ -67,6 +67,21 @@ module RubyVM::AbstractSyntaxTree
Primitive.ast_s_of body, keep_script_lines, error_tolerant
end
# call-seq:
# RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(backtrace_location) -> integer
#
# Returns the node id for the given backtrace location.
#
# begin
# raise
# rescue => e
# loc = e.backtrace_locations.first
# RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
# end # => 0
def self.node_id_for_backtrace_location backtrace_location
Primitive.node_id_for_backtrace_location backtrace_location
end
# RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
# RubyVM::AbstractSyntaxTree.
#

Просмотреть файл

@ -186,6 +186,19 @@ class TestAst < Test::Unit::TestCase
end
end
def test_node_id_for_location
exception = begin
raise
rescue => e
e
end
loc = exception.backtrace_locations.first
node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
assert_equal node.node_id, node_id
end
def test_of_proc_and_method
proc = Proc.new { 1 + 2 }
method = self.method(__method__)