Skip to content

Crash in a custom ruby extractor brings whole of RuboCop down #13149

@Earlopain

Description

@Earlopain

Expected behavior

RuboCop provides a hook allowing extensions to extract Ruby code from files like erb, html, markdown, etc. When an error occurs during that process, RuboCop has no mechanism to gracefully show that error to the user and instead fully crashes.

This is particularly annoying because it doesn't show which file caused the crash.

Steps to reproduce the problem

Test diff

Bit larger than necessary, I restructured existing tests a bit. "and the extractor crashes" is the important part.

diff --git a/spec/rubocop/runner_spec.rb b/spec/rubocop/runner_spec.rb
index 75d2652a5..a8d80965d 100644
--- a/spec/rubocop/runner_spec.rb
+++ b/spec/rubocop/runner_spec.rb
@@ -121,42 +121,15 @@ RSpec.describe RuboCop::Runner, :isolated_environment do
       end
     end
 
-    context 'with available custom ruby extractor' do
+    context 'with a custom ruby extractor' do
       before do
         described_class.ruby_extractors.unshift(custom_ruby_extractor)
-
-        # Make Style/EndOfLine give same output regardless of platform.
-        create_file('.rubocop.yml', <<~YAML)
-          Layout/EndOfLine:
-            EnforcedStyle: lf
-        YAML
       end
 
       after do
         described_class.ruby_extractors.shift
       end
 
-      # rubocop:disable Layout/LineLength
-      let(:custom_ruby_extractor) do
-        lambda do |_processed_source|
-          [
-            {
-              offset: 1,
-              processed_source: RuboCop::ProcessedSource.new(<<~RUBY, 3.3, 'dummy.rb', parser_engine: parser_engine)
-                # frozen_string_literal: true
-
-                def valid_code; end
-              RUBY
-            },
-            {
-              offset: 2,
-              processed_source: RuboCop::ProcessedSource.new(source, 3.3, 'dummy.rb', parser_engine: parser_engine)
-            }
-          ]
-        end
-      end
-      # rubocop:enable Layout/LineLength
-
       let(:source) do
         <<~RUBY
           # frozen_string_literal: true
@@ -165,57 +138,84 @@ RSpec.describe RuboCop::Runner, :isolated_environment do
         RUBY
       end
 
-      it 'sends the offense to a formatter' do
-        runner.run([])
-        expect(formatter_output).to eq <<~RESULT
-          Inspecting 1 file
-          C
-
-          Offenses:
+      context 'and the extractor returns a result' do
+        # rubocop:disable Layout/LineLength
+        let(:custom_ruby_extractor) do
+          lambda do |_processed_source|
+            [
+              {
+                offset: 1,
+                processed_source: RuboCop::ProcessedSource.new(<<~RUBY, 3.3, 'dummy.rb', parser_engine: parser_engine)
+                  # frozen_string_literal: true
+
+                  def valid_code; end
+                RUBY
+              },
+              {
+                offset: 2,
+                processed_source: RuboCop::ProcessedSource.new(source, 3.3, 'dummy.rb', parser_engine: parser_engine)
+              }
+            ]
+          end
+        end
+        # rubocop:enable Layout/LineLength
 
-          example.rb:3:7: C: Naming/MethodName: Use snake_case for method names.
-          def INVALID_CODE; end
-                ^^^^^^^^^^^^
+        it 'sends the offense to a formatter' do
+          runner.run([])
+          expect(formatter_output).to eq <<~RESULT
+            Inspecting 1 file
+            C
 
-          1 file inspected, 1 offense detected
-        RESULT
-      end
-    end
+            Offenses:
 
-    context 'with unavailable custom ruby extractor' do
-      before do
-        described_class.ruby_extractors.unshift(custom_ruby_extractor)
-      end
+            example.rb:3:7: C: Naming/MethodName: Use snake_case for method names.
+            def INVALID_CODE; end
+                  ^^^^^^^^^^^^
 
-      after do
-        described_class.ruby_extractors.shift
+            1 file inspected, 1 offense detected
+          RESULT
+        end
       end
 
-      let(:custom_ruby_extractor) do
-        lambda do |_processed_source|
+      context 'and the extractor returns no result' do
+        let(:custom_ruby_extractor) do
+          lambda do |_processed_source|
+          end
         end
-      end
 
-      let(:source) { <<~RUBY }
-        # frozen_string_literal: true
+        it 'sends the offense to a formatter' do
+          runner.run([])
+          expect(formatter_output).to eq <<~RESULT
+            Inspecting 1 file
+            C
 
-        def INVALID_CODE; end
-      RUBY
+            Offenses:
 
-      it 'sends the offense to a formatter' do
-        runner.run([])
-        expect(formatter_output).to eq <<~RESULT
-          Inspecting 1 file
-          C
+            example.rb:3:5: C: Naming/MethodName: Use snake_case for method names.
+            def INVALID_CODE; end
+                ^^^^^^^^^^^^
 
-          Offenses:
+            1 file inspected, 1 offense detected
+          RESULT
+        end
+      end
 
-          example.rb:3:5: C: Naming/MethodName: Use snake_case for method names.
-          def INVALID_CODE; end
-              ^^^^^^^^^^^^
+      context 'and the extractor crashes' do
+        let(:custom_ruby_extractor) do
+          lambda do |_processed_source|
+            raise 'Oh no!'
+          end
+        end
 
-          1 file inspected, 1 offense detected
-        RESULT
+        it 'sends the error to a formatter' do
+          runner.run([])
+          # No idea how this should be shown to the user yet
+          expect(formatter_output).to eq <<~RESULT
+            Inspecting 1 file
+
+            An error occurred while ... was inspecting 'example.rb'
+          RESULT
+        end
       end
     end

RuboCop version

$ [bundle exec] rubocop -V
1.65.1 (using Parser 3.3.4.2, rubocop-ast 1.32.1, running on ruby 3.3.4) [x86_64-linux]
  - rubocop-performance 1.21.1
  - rubocop-rake 0.6.0
  - rubocop-rspec 3.0.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy