Skip to content

Commit

Permalink
Make indexing enhancement error-tolerant
Browse files Browse the repository at this point in the history
This change rescues errors raised when indexing with enhancements
and logs them to stderr when the file's indexing is finished.
  • Loading branch information
st0012 committed Aug 5, 2024
1 parent 8110deb commit c6ac782
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
10 changes: 9 additions & 1 deletion lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ class DeclarationListener
OBJECT_NESTING = T.let(["Object"].freeze, T::Array[String])
BASIC_OBJECT_NESTING = T.let(["BasicObject"].freeze, T::Array[String])

sig { returns(T::Array[String]) }
attr_reader :indexing_errors

sig do
params(
index: Index,
Expand Down Expand Up @@ -36,6 +39,7 @@ def initialize(index, dispatcher, parse_result, file_path, enhancements: [])

# A stack of namespace entries that represent where we currently are. Used to properly assign methods to an owner
@owner_stack = T.let([], T::Array[Entry::Namespace])
@indexing_errors = T.let([], T::Array[String])

dispatcher.register(
self,
Expand Down Expand Up @@ -287,7 +291,11 @@ def on_call_node_enter(node)
@visibility_stack.push(Entry::Visibility::PRIVATE)
end

@enhancements.each { |aug| aug.on_call_node(@index, @owner_stack.last, node, @file_path) }
@enhancements.each do |enhancement|
enhancement.on_call_node(@index, @owner_stack.last, node, @file_path)
rescue StandardError
@indexing_errors << "Error occurred when indexing #{@file_path} with '#{enhancement.class.name}' enhancement"
end
end

sig { params(node: Prism::CallNode).void }
Expand Down
2 changes: 2 additions & 0 deletions lib/ruby_indexer/lib/ruby_indexer/enhancement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module Enhancement

interface!

requires_ancestor { Object }

# The `on_extend` indexing enhancement is invoked whenever an extend is encountered in the code. It can be used to
# register for an included callback, similar to what `ActiveSupport::Concern` does in order to auto-extend the
# `ClassMethods` modules
Expand Down
16 changes: 15 additions & 1 deletion lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,25 @@ def index_single(indexable_path, source = nil)
dispatcher = Prism::Dispatcher.new

result = Prism.parse(content)
DeclarationListener.new(self, dispatcher, result, indexable_path.full_path, enhancements: @enhancements)
listener = DeclarationListener.new(
self,
dispatcher,
result,
indexable_path.full_path,
enhancements: @enhancements,
)
dispatcher.dispatch(result.value)

indexing_errors = listener.indexing_errors.uniq

require_path = indexable_path.require_path
@require_paths_tree.insert(require_path, indexable_path) if require_path

if indexing_errors.any?
indexing_errors.each do |error|
$stderr.puts error
end
end
rescue Errno::EISDIR, Errno::ENOENT
# If `path` is a directory, just ignore it and continue indexing. If the file doesn't exist, then we also ignore
# it
Expand Down
32 changes: 32 additions & 0 deletions lib/ruby_indexer/test/enhancements_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,5 +159,37 @@ class User < ActiveRecord::Base

assert_entry("posts", Entry::Method, "/fake/path/foo.rb:23-11:23-17")
end

def test_error_handling_in_enhancement
enhancement_class = Class.new do
include Enhancement

def on_call_node(index, owner, node, file_path)
raise "Error"
end

class << self
def name
"TestEnhancement"
end
end
end

@index.register_enhancement(enhancement_class.new)

_stdout, stderr = capture_io do
index(<<~RUBY)
module ActiveSupport
module Concern
def self.extended(base)
base.class_eval("def new_method(a); end")
end
end
end
RUBY
end

assert_match(%r{Error occurred when indexing /fake/path/foo\.rb with 'TestEnhancement' enhancement}, stderr)
end
end
end

0 comments on commit c6ac782

Please sign in to comment.