Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 189
Prism.find#4021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Prism.find #4021
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| # frozen_string_literal: true | ||
| # :markup: markdown | ||
| #-- | ||
| # rbs_inline: enabled | ||
| module Prism | ||
| # Finds the Prism AST node corresponding to a given Method, UnboundMethod, | ||
| # Proc, or Thread::Backtrace::Location. On CRuby, uses node_id from the | ||
| # instruction sequence for an exact match. On other implementations, falls | ||
| # back to best-effort matching by source location line number. | ||
| # | ||
| # This module is autoloaded so that programs that don't use Prism.find don't | ||
| # pay for its definition. | ||
| module NodeFind # :nodoc: | ||
| # Find the node for the given callable or backtrace location. | ||
| #-- | ||
| #: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, bool rubyvm) -> Node? | ||
| #++ | ||
| def self.find(callable, rubyvm) | ||
| case callable | ||
| when Proc | ||
| if rubyvm | ||
| RubyVMCallableFind.new.find(callable) | ||
| elsif callable.lambda? | ||
| LineLambdaFind.new.find(callable) | ||
| else | ||
| LineProcFind.new.find(callable) | ||
| end | ||
| when Method, UnboundMethod | ||
| if rubyvm | ||
| RubyVMCallableFind.new.find(callable) | ||
| else | ||
| LineMethodFind.new.find(callable) | ||
| end | ||
| when Thread::Backtrace::Location | ||
| if rubyvm | ||
| RubyVMBacktraceLocationFind.new.find(callable) | ||
| else | ||
| LineBacktraceLocationFind.new.find(callable) | ||
| end | ||
| else | ||
| raise ArgumentError, "Expected a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, got #{callable.class}" | ||
| end | ||
| end | ||
| # Base class that handles parsing a file. | ||
| class Find | ||
| private | ||
| # Parse the given file path, returning a ParseResult or nil. | ||
| #-- | ||
| #: (String? file) -> ParseResult? | ||
| def parse_file(file) | ||
| return unless file && File.readable?(file) | ||
| result = Prism.parse_file(file) | ||
| result if result.success? | ||
| end | ||
| end | ||
| # Finds the AST node for a Method, UnboundMethod, or Proc using the node_id | ||
| # from the instruction sequence. | ||
| class RubyVMCallableFind < Find | ||
| # Find the node for the given callable using the ISeq node_id. | ||
| #-- | ||
| #: (Method | UnboundMethod | Proc callable) -> Node? | ||
| def find(callable) | ||
| return unless (source_location = callable.source_location) | ||
| return unless (result = parse_file(source_location[0])) | ||
| return unless (iseq = RubyVM::InstructionSequence.of(callable)) | ||
| header = iseq.to_a[4] | ||
| return unless header[:parser] == :prism | ||
| result.value.find { |node| node.node_id == header[:node_id] } | ||
| end | ||
| end | ||
| # Finds the AST node for a Thread::Backtrace::Location using the node_id | ||
| # from the backtrace location. | ||
| class RubyVMBacktraceLocationFind < Find | ||
| # Find the node for the given backtrace location using node_id. | ||
| #-- | ||
| #: (Thread::Backtrace::Location location) -> Node? | ||
| def find(location) | ||
| file = location.absolute_path || location.path | ||
| return unless (result = parse_file(file)) | ||
| return unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) | ||
| node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location) | ||
| result.value.find { |node| node.node_id == node_id } | ||
| end | ||
| end | ||
| # Finds the AST node for a Method or UnboundMethod using best-effort line | ||
| # matching. Used on non-CRuby implementations. | ||
| class LineMethodFind < Find | ||
| # Find the node for the given method by matching on name and line. | ||
| #-- | ||
| #: (Method | UnboundMethod callable) -> Node? | ||
| def find(callable) | ||
| return unless (source_location = callable.source_location) | ||
| return unless (result = parse_file(source_location[0])) | ||
| name = callable.name | ||
| start_line = source_location[1] | ||
| result.value.find do |node| | ||
| case node | ||
| when DefNode | ||
| node.name == name && node.location.start_line == start_line | ||
| when CallNode | ||
| node.block.is_a?(BlockNode) && node.location.start_line == start_line | ||
| else | ||
| false | ||
| end | ||
| end | ||
| end | ||
| end | ||
| # Finds the AST node for a lambda using best-effort line matching. Used | ||
| # on non-CRuby implementations. | ||
| class LineLambdaFind < Find | ||
| # Find the node for the given lambda by matching on line. | ||
| #-- | ||
| #: (Proc callable) -> Node? | ||
| def find(callable) | ||
| return unless (source_location = callable.source_location) | ||
| return unless (result = parse_file(source_location[0])) | ||
| start_line = source_location[1] | ||
| result.value.find do |node| | ||
| case node | ||
| when LambdaNode | ||
| node.location.start_line == start_line | ||
| when CallNode | ||
| node.block.is_a?(BlockNode) && node.location.start_line == start_line | ||
| else | ||
| false | ||
| end | ||
| end | ||
| end | ||
| end | ||
| # Finds the AST node for a non-lambda Proc using best-effort line | ||
| # matching. Used on non-CRuby implementations. | ||
| class LineProcFind < Find | ||
| # Find the node for the given proc by matching on line. | ||
| #-- | ||
| #: (Proc callable) -> Node? | ||
| def find(callable) | ||
| return unless (source_location = callable.source_location) | ||
| return unless (result = parse_file(source_location[0])) | ||
| start_line = source_location[1] | ||
| result.value.find do |node| | ||
| case node | ||
| when ForNode | ||
| node.location.start_line == start_line | ||
| when CallNode | ||
| node.block.is_a?(BlockNode) && node.location.start_line == start_line | ||
| else | ||
| false | ||
| end | ||
| end | ||
| end | ||
| end | ||
| # Finds the AST node for a Thread::Backtrace::Location using best-effort | ||
| # line matching. Used on non-CRuby implementations. | ||
| class LineBacktraceLocationFind < Find | ||
| # Find the node for the given backtrace location by matching on line. | ||
| #-- | ||
| #: (Thread::Backtrace::Location location) -> Node? | ||
| def find(location) | ||
| file = location.absolute_path || location.path | ||
| return unless (result = parse_file(file)) | ||
| start_line = location.lineno | ||
| result.value.find { |node| node.location.start_line == start_line } | ||
| end | ||
| end | ||
| end | ||
| end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # typed: true | ||
| class RubyVM::InstructionSequence | ||
| sig { params(callable: T.any(Method, UnboundMethod, Proc)).returns(T.nilable(RubyVM::InstructionSequence)) } | ||
| def self.of(callable); end | ||
| sig { returns(T::Array[T.untyped]) } | ||
| def to_a; end | ||
| end | ||
| module RubyVM::AbstractSyntaxTree | ||
| sig { params(location: Thread::Backtrace::Location).returns(Integer) } | ||
| def self.node_id_for_backtrace_location(location); end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| class RubyVM | ||
| class InstructionSequence | ||
| def self.of: (Method | UnboundMethod | Proc) -> RubyVM::InstructionSequence? | ||
| def to_a: () -> Array[untyped] | ||
| end | ||
| end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intentional to expose a
rubyvm:kwarg as public API here?If it's just for testing, tests could use
NodeFind.finddirectly.